Skip to content

Commit

Permalink
Merge pull request #10 from SShlykov/bookback
Browse files Browse the repository at this point in the history
feat: pages funct
  • Loading branch information
SShlykov authored Mar 2, 2024
2 parents 9a66a29 + 2005608 commit 0dfc8c2
Show file tree
Hide file tree
Showing 15 changed files with 218 additions and 97 deletions.
17 changes: 9 additions & 8 deletions bookback/internal/models/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import (
)

type Page struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt NullTime `json:"deleted_at"`
Text string `json:"text"`
ChapterID string `json:"chapter_id"`
IsPublic bool `json:"is_public"`
MapParamsID string `json:"map_params"` // параметры карты (координаты и тп.)
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt NullTime `json:"deleted_at"`
Title string `json:"title"`
Text string `json:"text"`
ChapterID string `json:"chapter_id"`
IsPublic bool `json:"is_public"`
MapParamsID NullString `json:"map_params"` // параметры карты (координаты и тп.)
//Paragraphs []Paragraph
//Variables []Variable `json:"variables"` // переменные мира key: key, value: value
}
4 changes: 4 additions & 0 deletions bookback/internal/servers/http/controllers/page/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package page

import (
"context"
"fmt"
"github.com/SShlykov/zeitment/bookback/internal/config"
"github.com/SShlykov/zeitment/bookback/internal/models"
service "github.com/SShlykov/zeitment/bookback/internal/services/page"
Expand Down Expand Up @@ -38,6 +39,7 @@ func (p *Controller) RegisterRoutes(e *echo.Echo, ctx context.Context) {
func (p *Controller) ListPages(c echo.Context, ctx context.Context) error {
pages, err := p.Service.ListPages(ctx)
if err != nil {
fmt.Println(err)
return echo.NewHTTPError(http.StatusBadGateway, config.ErrorForbidden)
}
return c.JSON(http.StatusOK, pages)
Expand Down Expand Up @@ -102,6 +104,7 @@ func (p *Controller) UpdatePage(c echo.Context, ctx context.Context) error {
}
updatedPage, err := p.Service.UpdatePage(ctx, id, &page)
if err != nil {
fmt.Println(err)
return echo.NewHTTPError(http.StatusInternalServerError, config.ErrorNotUpdated)
}
return c.JSON(http.StatusOK, updatedPage)
Expand All @@ -120,6 +123,7 @@ func (p *Controller) DeletePage(c echo.Context, ctx context.Context) error {
id := c.Param("id")
deletedPage, err := p.Service.DeletePage(ctx, id)
if err != nil {
fmt.Println(err)
return echo.NewHTTPError(http.StatusInternalServerError, config.ErrorNotDeleted)
}
return c.JSON(http.StatusOK, deletedPage)
Expand Down
5 changes: 2 additions & 3 deletions bookback/internal/services/book/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ func (r *repository) Create(ctx context.Context, book *models.Book) (string, err
}

func (r *repository) FindByID(ctx context.Context, id string) (*models.Book, error) {
query := `SELECT ` + allItems() + ` FROM ` + tableName + Where + columnID + ` = $1 AND ` +
columnDeletedAt + ` IS NULL LIMIT 1`
query := services.SelectWhere(allItems, tableName, columnID) + " AND " + columnDeletedAt + ` IS NULL` + " LIMIT 1"

q := db.Query{Name: "BookRepository.FindById", Raw: query}

Expand All @@ -110,7 +109,7 @@ func (r *repository) Update(ctx context.Context, id string, updBook *models.Book
}

func (r *repository) Delete(ctx context.Context, id string) (*models.Book, error) {
query := `DELETE FROM` + " " + tableName + Where + columnID + ` = $1 RETURNING ` + allItems()
query := services.DeleteQuery(tableName, columnID) + ` RETURNING ` + allItems()

q := db.Query{Name: "BookRepository.Delete", Raw: query}

Expand Down
5 changes: 4 additions & 1 deletion bookback/internal/services/book/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import (
"github.com/jackc/pgx/v5"
)

// readList reads a list of books from the database.
// It returns a slice of books and an error if any.
func readList(rows pgx.Rows) ([]models.Book, error) {
defer rows.Close()
books := make([]models.Book, 0)
for rows.Next() {
item, err := readItem(rows)
Expand All @@ -18,7 +21,7 @@ func readList(rows pgx.Rows) ([]models.Book, error) {
return nil, err
}

return books, nil
return books, rows.Err()
}

func readItem(row pgx.Row) (*models.Book, error) {
Expand Down
3 changes: 2 additions & 1 deletion bookback/internal/services/bookevents/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
)

func readList(rows pgx.Rows) ([]models.BookEvent, error) {
defer rows.Close()
events := make([]models.BookEvent, 0)
for rows.Next() {
item, err := readItem(rows)
Expand All @@ -18,7 +19,7 @@ func readList(rows pgx.Rows) ([]models.BookEvent, error) {
return nil, err
}

return events, nil
return events, rows.Err()
}

func readItem(row pgx.Row) (*models.BookEvent, error) {
Expand Down
2 changes: 1 addition & 1 deletion bookback/internal/services/chapter/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (r *repository) Update(ctx context.Context, id string, updChapter *models.C
}

func (r *repository) Delete(ctx context.Context, id string) (*models.Chapter, error) {
query := `DELETE ` + FromTable + WHERE + columnID + ` = $1` + Returning + allItems()
query := services.DeleteQuery(tableName, columnID) + ` RETURNING ` + allItems()
q := db.Query{Name: "BookRepository.Delete", Raw: query}

row := r.db.DB().QueryRowContext(ctx, q, id)
Expand Down
3 changes: 2 additions & 1 deletion bookback/internal/services/chapter/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
)

func readList(rows pgx.Rows) ([]models.Chapter, error) {
defer rows.Close()
chapters := make([]models.Chapter, 0)
for rows.Next() {
item, err := readItem(rows)
Expand All @@ -18,7 +19,7 @@ func readList(rows pgx.Rows) ([]models.Chapter, error) {
return nil, err
}

return chapters, nil
return chapters, rows.Err()
}

func readItem(row pgx.Row) (*models.Chapter, error) {
Expand Down
12 changes: 12 additions & 0 deletions bookback/internal/services/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ package services

import "strconv"

// DeleteQuery returns a SQL query to delete a row from a table by its ID.
// e.g. DeleteQuery(table_name, id_name) -> DELETE FROM table_name WHERE id_name = $1
func DeleteQuery(tableName, idName string) string {
sql := `DELETE FROM` + " " + tableName + ` WHERE ` + idName + ` = $1`

return sql
}

// SelectWhere returns a SQL query to select all items from a table with a WHERE clause.
// e.g. SelectWhere(allItems, table_name, column1, column2) -> SELECT allItems... FROM table_name WHERE column1 = $1 AND column2 = $2
func SelectWhere(allItems func() string, tableName string, args ...string) string {
sql := `SELECT ` + allItems() + ` FROM ` + tableName
if len(args) > 0 {
Expand All @@ -10,6 +20,8 @@ func SelectWhere(allItems func() string, tableName string, args ...string) strin
return sql
}

// ParamsToQuery returns a string of SQL query parameters.
// e.g. ParamsToQuery(", ", "column1", "column2") -> column1 = $1, column2 = $2
func ParamsToQuery(joiner string, args ...string) (sql string) {
for i, arg := range args {
sql += arg + ` = $` + strconv.Itoa(i+1)
Expand Down
23 changes: 23 additions & 0 deletions bookback/internal/services/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,29 @@ func mockAllItems() string {
return "*"
}

func TestDeleteQuery(t *testing.T) {
tests := []struct {
name string
tableName string
idName string
want string
}{
{
name: "Single WHERE condition",
tableName: "books",
idName: "id",
want: "DELETE FROM books WHERE id = $1",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := DeleteQuery(tt.tableName, tt.idName)
assert.Equal(t, tt.want, got)
})
}
}

func TestSelectWhere(t *testing.T) {
tests := []struct {
name string
Expand Down
3 changes: 2 additions & 1 deletion bookback/internal/services/mapvariables/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
)

func readList(rows pgx.Rows) ([]models.MapVariable, error) {
defer rows.Close()
variables := make([]models.MapVariable, 0)
for rows.Next() {
item, err := readItem(rows)
Expand All @@ -18,7 +19,7 @@ func readList(rows pgx.Rows) ([]models.MapVariable, error) {
return nil, err
}

return variables, nil
return variables, rows.Err()
}

func readItem(row pgx.Row) (*models.MapVariable, error) {
Expand Down
114 changes: 43 additions & 71 deletions bookback/internal/services/page/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@ package page
import (
"context"
"github.com/SShlykov/zeitment/bookback/internal/models"
"github.com/SShlykov/zeitment/bookback/internal/services"
"github.com/SShlykov/zeitment/bookback/pkg/db"
"github.com/SShlykov/zeitment/bookback/pkg/db/sq"
"github.com/google/uuid"
"strings"
)

const (
// model fields and table name for books table
tableName = "pages"
columnID = "id"
columnCreatedAt = "created_at"
columnUpdatedAt = "updated_at"
columnDeletedAt = "deleted_at"
columnText = "text"
columnChapterID = "chapter_id"
columnIsPublic = "is_public"
Returning = "RETURNING "
tableName = "pages"
columnID = "id"
columnCreatedAt = "created_at"
columnUpdatedAt = "updated_at"
columnDeletedAt = "deleted_at"
columnTitle = "title"
columnText = "text"
columnChapterID = "chapter_id"
columnIsPublic = "is_public"
columnMapParamsID = "map_params_id"
Returning = " RETURNING "
)

// Repository определяет интерфейс для взаимодействия с хранилищем книг.
Expand All @@ -41,23 +42,30 @@ func NewRepository(database db.Client) Repository {
return &repository{database}
}

func allItems() string {
cols := []string{columnID, columnCreatedAt, columnUpdatedAt, columnDeletedAt, columnTitle, columnText, columnChapterID,
columnIsPublic, columnMapParamsID}

return strings.Join(cols, ", ")
}

func insertItems() string {
cols := []string{columnTitle, columnText, columnChapterID, columnIsPublic, columnMapParamsID}

return strings.Join(cols, ", ")
}

// Create inserts a new page into the database and returns its ID.
func (r *repository) Create(ctx context.Context, page *models.Page) (string, error) {
page.ID = uuid.New().String()
query, args, err := sq.Insert(tableName).
Columns(columnID, columnText, columnChapterID, columnIsPublic).
Values(page.ID, page.Text, page.ChapterID, page.IsPublic).
Suffix("RETURNING id").
ToSql()
if err != nil {
return "", err
}
query := `INSERT INTO` + " " + tableName + ` (` + insertItems() + `) VALUES ($1, $2, $3, $4, $5) ` +
Returning + columnID
args := []interface{}{page.Title, page.Text, page.ChapterID, page.IsPublic, page.MapParamsID}

q := db.Query{Name: "PageRepository.Create", Raw: query}

row := r.db.DB().QueryRowContext(ctx, q, args...)
var id string
if err = row.Scan(&id); err != nil {
if err := row.Scan(&id); err != nil {
return "", err
}

Expand All @@ -66,38 +74,21 @@ func (r *repository) Create(ctx context.Context, page *models.Page) (string, err

// FindByID retrieves a page by its ID.
func (r *repository) FindByID(ctx context.Context, id string) (*models.Page, error) {
query, args, err := sq.Select(columnID, columnCreatedAt, columnUpdatedAt, columnDeletedAt, columnText,
columnChapterID, columnIsPublic).
From("pages").
Where(sq.Eq{"id": id, "deleted_at": nil}).
Limit(1).
ToSql()
if err != nil {
return nil, err
}

query := services.SelectWhere(allItems, tableName, columnID)
q := db.Query{Name: "PageRepository.FindByID", Raw: query}

var page models.Page
if err = r.db.DB().QueryRowContext(ctx, q, args...).Scan(&page.ID, &page.CreatedAt, &page.UpdatedAt,
&page.DeletedAt, &page.Text, &page.ChapterID, &page.IsPublic); err != nil {
return nil, err
}
row := r.db.DB().QueryRowContext(ctx, q, id)

return &page, nil
return readItem(row)
}

// Update modifies an existing page's data.
func (r *repository) Update(ctx context.Context, id string, updPage *models.Page) (*models.Page, error) {
query, args, err := sq.Update(tableName).
Set(columnText, updPage.Text).
Set(columnIsPublic, updPage.IsPublic).
Where(sq.Eq{"id": id}).
Suffix("RETURNING id, created_at, updated_at, deleted_at, text, chapter_id, is_public").
ToSql()
if err != nil {
return nil, err
}
query := "Update " + tableName + " SET " +
services.ParamsToQuery(", ", columnTitle, columnText, columnChapterID, columnIsPublic, columnMapParamsID) +
" WHERE " + columnID + " = $6" + Returning + allItems()

args := []interface{}{updPage.Title, updPage.Text, updPage.ChapterID, updPage.IsPublic, updPage.MapParamsID, id}

q := db.Query{Name: "PageRepository.Update", Raw: query}

Expand All @@ -108,56 +99,37 @@ func (r *repository) Update(ctx context.Context, id string, updPage *models.Page

// Delete removes a page from the database.
func (r *repository) Delete(ctx context.Context, id string) (*models.Page, error) {
query, args, err := sq.SQ.Delete(tableName).
Where(sq.Eq{"id": id}).
Suffix("RETURNING id").
ToSql()
if err != nil {
return nil, err
}
query := services.DeleteQuery(tableName, columnID) + Returning + allItems()

q := db.Query{Name: "PageRepository.Delete", Raw: query}
row := r.db.DB().QueryRowContext(ctx, q, id)

var deletedID string
if err = r.db.DB().QueryRowContext(ctx, q, args...).Scan(&deletedID); err != nil {
return nil, err
}

return &models.Page{ID: deletedID}, nil
return readItem(row)
}

// List retrieves all pages for a given chapter ID.
func (r *repository) List(ctx context.Context) ([]models.Page, error) {
query :=
`SELECT id, created_at, updated_at, deleted_at, text, chapter_id, is_public
FROM pages
WHERE deleted_at IS NULL`
query := `Select ` + allItems() + ` FROM ` + tableName + ` WHERE ` + columnDeletedAt + ` IS NULL`

q := db.Query{Name: "PageRepository.List", Raw: query}

rows, err := r.db.DB().QueryContext(ctx, q)
if err != nil {
return nil, err
}
defer rows.Close()

return readList(rows)
}

// GetPagesByChapterID retrieves all pages for a given chapter ID.
func (r *repository) GetPagesByChapterID(ctx context.Context, chapterID string) ([]models.Page, error) {
query :=
`SELECT id, created_at, updated_at, deleted_at, text, chapter_id, is_public
FROM pages
WHERE chapter_id = $1 AND deleted_at IS NULL`
query := services.SelectWhere(allItems, tableName, columnChapterID) + ` AND ` + columnDeletedAt + ` IS NULL`

q := db.Query{Name: "PageRepository.GetPagesByChapterID", Raw: query}

rows, err := r.db.DB().QueryContext(ctx, q, chapterID)
if err != nil {
return nil, err
}
defer rows.Close()

return readList(rows)
}
Loading

0 comments on commit 0dfc8c2

Please sign in to comment.