334 lines
7.1 KiB
Go
334 lines
7.1 KiB
Go
package todo
|
|
|
|
// GENERATED FILE
|
|
// DO NOT EDIT
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/doug-martin/goqu/v9"
|
|
_ "github.com/doug-martin/goqu/v9/dialect/postgres"
|
|
"github.com/doug-martin/goqu/v9/exp"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"strings"
|
|
"time"
|
|
"todo/crud"
|
|
)
|
|
|
|
type TodoRepository struct {
|
|
connPool *pgxpool.Pool
|
|
dialect goqu.DialectWrapper
|
|
}
|
|
|
|
func NewTodoRepository(connPool *pgxpool.Pool) *TodoRepository {
|
|
return &TodoRepository{
|
|
connPool: connPool,
|
|
dialect: goqu.Dialect("postgres"),
|
|
}
|
|
}
|
|
|
|
func (r *TodoRepository) Create(todo Todo) (int, error) {
|
|
sql, args, err := r.dialect.Insert("todo").
|
|
Prepared(true).
|
|
Rows(goqu.Record{
|
|
|
|
"updated_at": time.Now(),
|
|
"name": todo.Name,
|
|
"completed": todo.Completed,
|
|
"due": r.jsonToString(todo.Due),
|
|
"user_id": todo.UserId,
|
|
}).
|
|
Returning("id").
|
|
ToSQL()
|
|
if err != nil {
|
|
crud.LogError("error creating create Todo sql: %v", err)
|
|
return -1, err
|
|
}
|
|
|
|
rows, err := r.connPool.Query(context.Background(), sql, args...)
|
|
if err != nil {
|
|
crud.LogError("error creating Todo: %v", err)
|
|
return -1, err
|
|
}
|
|
defer rows.Close()
|
|
var id int
|
|
if rows.Next() {
|
|
err = rows.Scan(&id)
|
|
if err != nil {
|
|
crud.LogError("error scanning User: %v", err)
|
|
return -1, err
|
|
}
|
|
} else {
|
|
crud.Error("Todo already exists")
|
|
return -1, TodoAlreadyExistsError{Todo: todo}
|
|
}
|
|
|
|
return id, nil
|
|
|
|
}
|
|
|
|
type TodoAlreadyExistsError struct {
|
|
Todo Todo
|
|
}
|
|
|
|
func (e TodoAlreadyExistsError) Error() string {
|
|
return fmt.Sprint("Todo ", e.Todo, " already exists")
|
|
}
|
|
|
|
func (r *TodoRepository) getSelectColumns() []any {
|
|
return []any{"id", "created_at", "updated_at",
|
|
"name", "completed", "due", "user_id",
|
|
}
|
|
}
|
|
|
|
func (r *TodoRepository) Read(userId int, id int) (Todo, error) {
|
|
crud.Debug("Getting Todo by id ", id)
|
|
sql, args, _ := r.dialect.From("todo").
|
|
Prepared(true).
|
|
Select(r.getSelectColumns()...).
|
|
Where(goqu.Ex{
|
|
"id": id,
|
|
"user_id": userId,
|
|
}).
|
|
ToSQL()
|
|
|
|
rows, err := r.connPool.Query(context.Background(), sql, args...)
|
|
if err != nil {
|
|
crud.Error("Failed to get Todo: ", err)
|
|
}
|
|
defer rows.Close()
|
|
if rows.Next() {
|
|
item, _, err := r.rowToItem(rows, false)
|
|
return item, err
|
|
}
|
|
return Todo{}, errors.New("no rows found")
|
|
}
|
|
|
|
type TodoItemScan struct {
|
|
Todo
|
|
RowId int
|
|
Count int
|
|
}
|
|
|
|
func (r *TodoRepository) rowToItem(rows pgx.Rows, rowId bool) (Todo, int, error) {
|
|
var item TodoItemScan
|
|
if rowId {
|
|
err := rows.Scan(
|
|
&item.RowId,
|
|
&item.Count,
|
|
&item.Id,
|
|
&item.CreatedAt,
|
|
&item.UpdatedAt,
|
|
&item.Name,
|
|
&item.Completed,
|
|
&item.Due,
|
|
&item.UserId,
|
|
)
|
|
if err != nil {
|
|
return Todo{}, -1, err
|
|
}
|
|
} else {
|
|
err := rows.Scan(
|
|
&item.Id,
|
|
&item.CreatedAt,
|
|
&item.UpdatedAt,
|
|
&item.Name,
|
|
&item.Completed,
|
|
&item.Due,
|
|
&item.UserId,
|
|
)
|
|
if err != nil {
|
|
return Todo{}, -1, err
|
|
}
|
|
}
|
|
return Todo{
|
|
Id: item.Id,
|
|
CreatedAt: item.CreatedAt,
|
|
UpdatedAt: item.UpdatedAt,
|
|
Name: item.Name,
|
|
Completed: item.Completed,
|
|
Due: item.Due,
|
|
UserId: item.UserId,
|
|
}, item.Count, nil
|
|
}
|
|
|
|
func (r *TodoRepository) Update(userId int, todo Todo) error {
|
|
sql, args, err := r.dialect.Update("todo").
|
|
Prepared(true).
|
|
Set(goqu.Record{
|
|
|
|
"updated_at": time.Now(),
|
|
"name": todo.Name,
|
|
"completed": todo.Completed,
|
|
"due": r.jsonToString(todo.Due),
|
|
"user_id": todo.UserId,
|
|
}).
|
|
Where(goqu.Ex{
|
|
"id": todo.Id,
|
|
"user_id": userId,
|
|
}).
|
|
ToSQL()
|
|
if err != nil {
|
|
crud.LogError("error creating update Todo sql: %v", err)
|
|
return err
|
|
}
|
|
|
|
_, err = r.connPool.Exec(context.Background(), sql, args...)
|
|
if err != nil {
|
|
crud.LogError("error updating Todo: %v", err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *TodoRepository) Delete(userId int, id int) error {
|
|
sql, args, err := r.dialect.Delete("todo").
|
|
Prepared(true).
|
|
Where(goqu.Ex{
|
|
"id": id,
|
|
"user_id": userId,
|
|
}).
|
|
ToSQL()
|
|
if err != nil {
|
|
crud.LogError("error creating delete Todo sql: %v", err)
|
|
return err
|
|
}
|
|
|
|
_, err = r.connPool.Exec(context.Background(), sql, args...)
|
|
if err != nil {
|
|
crud.LogError("error deleting Todo: %v", err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type TodoField string
|
|
|
|
const (
|
|
TodoFieldName TodoField = "name"
|
|
TodoFieldCompleted TodoField = "completed"
|
|
TodoFieldDue TodoField = "due"
|
|
)
|
|
|
|
type TodoNameFilter struct {
|
|
Active bool
|
|
Value string
|
|
}
|
|
|
|
type TodoCompletedFilter struct {
|
|
Active bool
|
|
Value bool
|
|
}
|
|
|
|
type TodoOrderDirection string
|
|
|
|
const (
|
|
TodoOrderDirectionAsc TodoOrderDirection = "asc"
|
|
TodoOrderDirectionDesc TodoOrderDirection = "desc"
|
|
)
|
|
|
|
type TodoReferences struct {
|
|
UserId int
|
|
}
|
|
|
|
type TodoPaginationParams struct {
|
|
RowId int
|
|
PageSize int
|
|
OrderBy TodoField
|
|
OrderDirection TodoOrderDirection
|
|
|
|
NameFilter TodoNameFilter
|
|
|
|
CompletedFilter TodoCompletedFilter
|
|
|
|
References TodoReferences
|
|
}
|
|
|
|
func (r *TodoRepository) GetPage(params TodoPaginationParams) ([]Todo, int, error) {
|
|
var orderByWindow exp.WindowExpression
|
|
if params.OrderDirection == TodoOrderDirectionAsc {
|
|
orderByWindow = goqu.W().OrderBy(goqu.C(string(params.OrderBy)).Asc())
|
|
} else {
|
|
orderByWindow = goqu.W().OrderBy(goqu.C(string(params.OrderBy)).Desc())
|
|
}
|
|
selectColumns := []any{
|
|
goqu.ROW_NUMBER().Over(orderByWindow).As("row_id"),
|
|
goqu.COUNT("*"),
|
|
}
|
|
selectColumns = append(selectColumns, r.getSelectColumns()...)
|
|
whereExpressions := []goqu.Expression{
|
|
goqu.Ex{
|
|
|
|
"user_id": params.References.UserId,
|
|
},
|
|
}
|
|
whereExpressions = r.addPageFilters(params, whereExpressions)
|
|
var colOrder exp.OrderedExpression
|
|
if params.OrderDirection == TodoOrderDirectionAsc {
|
|
colOrder = goqu.C(string(params.OrderBy)).Asc()
|
|
} else {
|
|
colOrder = goqu.C(string(params.OrderBy)).Desc()
|
|
}
|
|
dialect := goqu.Dialect("postgres")
|
|
innerFrom := dialect.From("todo").
|
|
Prepared(true).
|
|
Select(selectColumns...).
|
|
Where(whereExpressions...).
|
|
Order(colOrder)
|
|
|
|
sql, args, _ := dialect.From(innerFrom).
|
|
Prepared(true).
|
|
Where(goqu.Ex{"row_id": goqu.Op{"gt": params.RowId}}).
|
|
Limit(uint(params.PageSize)).
|
|
ToSQL()
|
|
sql = strings.Replace(sql, "COUNT(*)", "COUNT(*) over()", 1)
|
|
|
|
rows, err := r.connPool.Query(context.Background(), sql, args...)
|
|
if err != nil {
|
|
crud.LogError("failed to run sql query: %v", err)
|
|
return nil, -1, err
|
|
}
|
|
defer rows.Close()
|
|
results := make([]Todo, 0)
|
|
totalCount := 0
|
|
for rows.Next() {
|
|
parsed, count, err := r.rowToItem(rows, true)
|
|
if err != nil {
|
|
return nil, -1, err
|
|
}
|
|
totalCount = count
|
|
results = append(results, parsed)
|
|
}
|
|
return results, totalCount, nil
|
|
}
|
|
|
|
func (r *TodoRepository) addPageFilters(params TodoPaginationParams, whereExpressions []goqu.Expression) []goqu.Expression {
|
|
|
|
if params.NameFilter.Active {
|
|
whereExpressions = append(whereExpressions, goqu.Ex{
|
|
"name": goqu.Op{"like": fmt.Sprint("%", params.NameFilter.Value, "%")},
|
|
})
|
|
}
|
|
|
|
if params.CompletedFilter.Active {
|
|
whereExpressions = append(whereExpressions, goqu.Ex{
|
|
"completed": params.CompletedFilter.Value,
|
|
})
|
|
}
|
|
|
|
return whereExpressions
|
|
}
|
|
|
|
func (r *TodoRepository) jsonToString(jsonData any) string {
|
|
bytes, err := json.Marshal(jsonData)
|
|
if err != nil {
|
|
return "{}"
|
|
}
|
|
return string(bytes)
|
|
}
|