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, "duedate": r.jsonToString(todo.Duedate), "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", "duedate", "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.Duedate, &item.UserId, ) if err != nil { return Todo{}, -1, err } } else { err := rows.Scan( &item.Id, &item.CreatedAt, &item.UpdatedAt, &item.Name, &item.Completed, &item.Duedate, &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, Duedate: item.Duedate, 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, "duedate": r.jsonToString(todo.Duedate), "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" TodoFieldDuedate TodoField = "duedate" ) 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) }