diff --git a/internal/api/handler/list.go b/internal/api/handler/list.go index ad2863f..8875bae 100644 --- a/internal/api/handler/list.go +++ b/internal/api/handler/list.go @@ -353,6 +353,56 @@ func (h *ListHandler) UpdateListItem(w http.ResponseWriter, r *http.Request) { response.Status(w, http.StatusNoContent) } +// SetListItemTitle handles updating "title" of a list item +// +// @Summary Updates "title" of list item +// @Description Update an existing list item, setting the "title" field +// @Tags List +// @Accept json +// @Produce json +// @Param id path int true "List Item ID" +// @Param payload body request.SetListItemTitlePayload true "Update list item title payload" +// @Success 204 {object} nil +// @Error 400 {object} response.ErrorResponse "failed to decode request url" +// @Error 400 {object} response.ErrorResponse "failed to decode request body" +// @Error 401 {object} response.ErrorResponse "not authorized" +// @Error 500 {object} response.ErrorResponse "failed to set list item title" +// @Router /lists/items/{id}/title [post] +func (h *ListHandler) SetListItemTitle(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + listItemID := r.PathValue("id") + if listItemID == "" { + slog.ErrorContext(ctx, "failed to read list item id from path") + response.Error(ctx, w, http.StatusBadRequest, "failed to decode request url") + return + } + + payload, err := request.DecodeJSON[request.SetListItemTitlePayload](r) + if err != nil { + slog.ErrorContext(ctx, "failed to decode set list item title payload", slog.Any("error", err)) + response.Error(ctx, w, http.StatusBadRequest, "failed to decode request body") + return + } + + authContext, err := domain.GetAuthContext(ctx) + if err != nil { + slog.ErrorContext(ctx, "failed to get auth context", slog.Any("error", err)) + response.Error(ctx, w, http.StatusUnauthorized, "not authorized") + return + } + + err = h.ListService.SetListItemTitle(ctx, authContext.UserID, listItemID, payload.Title) + if err != nil { + slog.ErrorContext(ctx, "failed to set list item title", slog.Any("error", err)) + response.Error(ctx, w, http.StatusInternalServerError, "failed to set list item title") + return + } + + slog.InfoContext(ctx, "update list item title successful", slog.String("list_item_id", listItemID)) + response.Status(w, http.StatusNoContent) +} + // SetListItemCompleted handles updating "is_completed" of a list item // // @Summary Updates "is_completed" of list item @@ -366,8 +416,8 @@ func (h *ListHandler) UpdateListItem(w http.ResponseWriter, r *http.Request) { // @Error 400 {object} response.ErrorResponse "failed to decode request url" // @Error 400 {object} response.ErrorResponse "failed to decode request body" // @Error 401 {object} response.ErrorResponse "not authorized" -// @Error 500 {object} response.ErrorResponse "failed to update list item" -// @Router /lists/items/{id} [post] +// @Error 500 {object} response.ErrorResponse "failed to set list item completed" +// @Router /lists/items/{id}/complete [post] func (h *ListHandler) SetListItemCompleted(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -399,7 +449,7 @@ func (h *ListHandler) SetListItemCompleted(w http.ResponseWriter, r *http.Reques return } - slog.InfoContext(ctx, "updated list item completed successful", slog.String("list_item_id", listItemID)) + slog.InfoContext(ctx, "update list item completed successful", slog.String("list_item_id", listItemID)) response.Status(w, http.StatusNoContent) } diff --git a/internal/api/request/list.go b/internal/api/request/list.go index ca8bf1e..fcb5093 100644 --- a/internal/api/request/list.go +++ b/internal/api/request/list.go @@ -25,6 +25,10 @@ type UpdateListItemPayload struct { IsCompleted bool `json:"is_completed"` } +type SetListItemTitlePayload struct { + Title string `json:"title"` +} + type SetListItemCompletedPayload struct { IsCompleted bool `json:"is_completed"` } diff --git a/internal/api/router/router.go b/internal/api/router/router.go index fb94676..2007727 100644 --- a/internal/api/router/router.go +++ b/internal/api/router/router.go @@ -66,7 +66,8 @@ func NewMux(cfg Config) http.Handler { apiMux.Handle("POST /lists/items", protected(listHandler.CreateListItem)) apiMux.Handle("DELETE /lists/items/{id}", protected(listHandler.DeleteListItem)) apiMux.Handle("PUT /lists/items", protected(listHandler.UpdateListItem)) - apiMux.Handle("POST /lists/items/{id}", protected(listHandler.SetListItemCompleted)) + apiMux.Handle("POST /lists/items/{id}/title", protected(listHandler.SetListItemTitle)) + apiMux.Handle("POST /lists/items/{id}/complete", protected(listHandler.SetListItemCompleted)) apiMux.Handle("GET /lists/{id}", protected(listHandler.GetListItems)) mux := http.NewServeMux() diff --git a/internal/domain/list.go b/internal/domain/list.go index 7c93f32..68bed6d 100644 --- a/internal/domain/list.go +++ b/internal/domain/list.go @@ -44,6 +44,7 @@ type ListRepository interface { CreateListItem(ctx context.Context, listID string, title string) (*ListItem, error) DeleteListItem(ctx context.Context, listItemID string) error UpdateListItem(ctx context.Context, listItemID string, title string, isCompleted bool) error + SetListItemTitle(ctx context.Context, listItemID string, title string) error SetListItemCompleted(ctx context.Context, listItemID string, isCompleted bool) error GetListItems(ctx context.Context, listID string) ([]ListItem, error) } @@ -172,6 +173,21 @@ func (s *ListService) UpdateListItem(ctx context.Context, authUserID string, lis return s.repo.UpdateListItem(ctx, listItemID, title, isCompleted) } +func (s *ListService) SetListItemTitle(ctx context.Context, authUserID string, listItemID string, title string) error { + if listItemID == "" { + return ErrListItemIDMissing + } + if title == "" { + return ErrListItemTitleMissing + } + + if err := s.userAllowedToAccessListItem(ctx, authUserID, listItemID); err != nil { + return err + } + + return s.repo.SetListItemTitle(ctx, listItemID, title) +} + func (s *ListService) SetListItemCompleted(ctx context.Context, authUserID string, listItemID string, isCompleted bool) error { if listItemID == "" { return ErrListItemIDMissing diff --git a/internal/repository/list.go b/internal/repository/list.go index c8ae0fa..4a8b6cf 100644 --- a/internal/repository/list.go +++ b/internal/repository/list.go @@ -150,6 +150,17 @@ func (r *ListRepo) UpdateListItem(ctx context.Context, listItemID string, title return nil } +func (r *ListRepo) SetListItemTitle(ctx context.Context, listItemID string, title string) error { + _, err := r.conn(ctx).ExecContext(ctx, "UPDATE list_items SET title = $1, modified_at = NOW() WHERE id = $2", + title, listItemID, + ) + if err != nil { + return fmt.Errorf("failed to update list item title: %w", err) + } + + return nil +} + func (r *ListRepo) SetListItemCompleted(ctx context.Context, listItemID string, isCompleted bool) error { _, err := r.conn(ctx).ExecContext(ctx, "UPDATE list_items SET is_completed = $1, modified_at = NOW() WHERE id = $2", isCompleted, listItemID,