Files
create-glstack/template/services/api/server/todo/todo.go
T
gregor 6afe1f7b67
Publish npm package / publish (push) Failing after 16s
0.0.9
2026-10-08 09:39:38 +02:00

135 lines
3.8 KiB
Go

package todo
import (
"cmp"
"context"
"net/http"
"slices"
"time"
"connectrpc.com/connect"
"connectrpc.com/validate"
"<@var(context.project.goprefix)>/<@var(context.project.name)>/db"
todov1 "<@var(context.project.goprefix)>/<@var(context.project.name)>/gen/todo/v1"
"<@var(context.project.goprefix)>/<@var(context.project.name)>/gen/todo/v1/todov1connect"
. "<@var(context.project.goprefix)>/<@var(context.project.name)>/utils"
"github.com/jackc/pgx/v5/pgtype"
)
type TodoServer struct{}
func (srv *TodoServer) CreateTodo(ctx context.Context, req *connect.Request[todov1.CreateTodoRequest]) (*connect.Response[todov1.CreateTodoResponse], error) {
var id pgtype.UUID
err := id.Scan(*req.Msg.Todo.Id)
if err != nil {
return nil, err
}
todo, err := db.Q.CreateTodo(ctx, db.CreateTodoParams{
ID: id,
Task: req.Msg.Todo.Task,
})
if err != nil {
return nil, err
}
return &connect.Response[todov1.CreateTodoResponse]{
Msg: &todov1.CreateTodoResponse{
Todo: &todov1.Todo{
Id: StrPtr(todo.ID.String()),
Task: todo.Task,
CreatedAt: StrPtr(todo.CreatedAt.Time.Format(time.RFC3339)),
UpdatesAt: StrPtr(todo.UpdatedAt.Time.Format(time.RFC3339)),
Done: BoolPtr(todo.Done.Bool),
},
},
}, nil
}
func (srv *TodoServer) ListTodos(ctx context.Context, req *connect.Request[todov1.ListTodosRequest]) (*connect.Response[todov1.ListTodosResponse], error) {
todos, err := db.Q.ListTodos(ctx)
if err != nil {
return nil, err
}
responseTodos := []*todov1.Todo{}
for _, todo := range todos {
responseTodos = append(responseTodos, &todov1.Todo{
Id: StrPtr(todo.ID.String()),
Task: todo.Task,
CreatedAt: StrPtr(todo.CreatedAt.Time.Format(time.RFC3339)),
UpdatesAt: StrPtr(todo.UpdatedAt.Time.Format(time.RFC3339)),
Done: BoolPtr(todo.Done.Bool),
})
}
// Open todos first, each group oldest first. A single total order (SortFunc isn't stable)
// keeps rows from swapping places between polls.
boolToInt := func(b bool) int {
if b {
return 1
}
return 0
}
slices.SortFunc(responseTodos, func(a, b *todov1.Todo) int {
return cmp.Or(
cmp.Compare(boolToInt(a.GetDone()), boolToInt(b.GetDone())),
cmp.Compare(a.GetCreatedAt(), b.GetCreatedAt()),
cmp.Compare(a.GetId(), b.GetId()),
)
})
return &connect.Response[todov1.ListTodosResponse]{
Msg: &todov1.ListTodosResponse{
Todos: responseTodos,
},
}, nil
}
func (srv *TodoServer) UpdateTodo(ctx context.Context, req *connect.Request[todov1.UpdateTodoRequest]) (*connect.Response[todov1.UpdateTodoResponse], error) {
var id pgtype.UUID
err := id.Scan(*req.Msg.Todo.Id)
if err != nil {
return nil, err
}
todo, err := db.Q.UpdateTodo(ctx, db.UpdateTodoParams{
Task: req.Msg.Todo.Task,
Done: pgtype.Bool{
Bool: *req.Msg.Todo.Done,
Valid: true,
},
ID: id,
})
if err != nil {
return nil, err
}
return &connect.Response[todov1.UpdateTodoResponse]{
Msg: &todov1.UpdateTodoResponse{
Todo: &todov1.Todo{
Id: StrPtr(todo.ID.String()),
Task: todo.Task,
CreatedAt: StrPtr(todo.CreatedAt.Time.Format(time.RFC3339)),
UpdatesAt: StrPtr(todo.UpdatedAt.Time.Format(time.RFC3339)),
Done: BoolPtr(todo.Done.Bool),
},
},
}, nil
}
func (srv *TodoServer) DeleteTodo(ctx context.Context, req *connect.Request[todov1.DeleteTodoRequest]) (*connect.Response[todov1.DeleteTodoResponse], error) {
var id pgtype.UUID
err := id.Scan(*req.Msg.Todo.Id)
if err != nil {
return nil, err
}
err = db.Q.DeleteTodo(ctx, id)
if err != nil {
return nil, err
}
return &connect.Response[todov1.DeleteTodoResponse]{
Msg: &todov1.DeleteTodoResponse{},
}, nil
}
func GetPathHandler() (path string, handler http.Handler) {
path, handler = todov1connect.NewTodoServiceHandler(&TodoServer{}, connect.WithInterceptors(validate.NewInterceptor()))
return
}