Merge pull request 'Fixes after first integration test' (#19) from dev into main
This commit was merged in pull request #19.
This commit is contained in:
@@ -5,6 +5,8 @@ services:
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- "15432:5432"
|
||||
volumes:
|
||||
- /srv/dttmr-api/postgresql:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
|
||||
@@ -28,7 +28,7 @@ func NewAuthHandler(authService *domain.AuthService) *AuthHandler {
|
||||
// @Success 200 {object} domain.TokenPair
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to login"
|
||||
// @Router /api/v1/login [post]
|
||||
// @Router /login [post]
|
||||
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -60,7 +60,7 @@ func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
|
||||
// @Success 200 {object} domain.TokenPair
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to refresh token"
|
||||
// @Router /api/v1/login/refresh [post]
|
||||
// @Router /login/refresh [post]
|
||||
func (h *AuthHandler) Refresh(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -92,7 +92,7 @@ func (h *AuthHandler) Refresh(w http.ResponseWriter, r *http.Request) {
|
||||
// @Success 200 {object} nil
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to logout"
|
||||
// @Router /api/v1/logout [post]
|
||||
// @Router /logout [post]
|
||||
func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -123,7 +123,7 @@ func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
// @Success 200 {object} nil
|
||||
// @Error 401 {object} response.ErrorResponse "failed to get auth context"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to logout"
|
||||
// @Router /api/v1/logout/all [post]
|
||||
// @Router /logout/all [post]
|
||||
func (h *AuthHandler) LogoutAllDevices(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ type apiResponse struct {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} apiResponse
|
||||
// @Router /api/v1/ [get]
|
||||
// @Router / [get]
|
||||
func DefaultHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ func NewListHandler(listService *domain.ListService) *ListHandler {
|
||||
// @Success 201 {object} domain.List
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to create list"
|
||||
// @Router /api/v1/lists [post]
|
||||
// @Router /lists [post]
|
||||
func (h *ListHandler) CreateList(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -68,7 +68,7 @@ func (h *ListHandler) CreateList(w http.ResponseWriter, r *http.Request) {
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request url"
|
||||
// @Error 401 {object} response.ErrorResponse "not authorized"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to read lists"
|
||||
// @Router /api/v1/lists [get]
|
||||
// @Router /lists [get]
|
||||
func (h *ListHandler) GetLists(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -100,7 +100,7 @@ func (h *ListHandler) GetLists(w http.ResponseWriter, r *http.Request) {
|
||||
// @Success 204 {object} nil
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to add user to list"
|
||||
// @Router /api/v1/lists/user [post]
|
||||
// @Router /lists/user [post]
|
||||
func (h *ListHandler) AddUserToList(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -126,7 +126,7 @@ func (h *ListHandler) AddUserToList(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
slog.InfoContext(ctx, "added user to list successfully", slog.Any("list_id", payload.ListID), slog.Any("user_id", payload.UserID))
|
||||
response.JSON(ctx, w, http.StatusNoContent, nil)
|
||||
response.Status(ctx, w, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// RemoveUserFromList handles the removal of a user association to a list
|
||||
@@ -140,7 +140,7 @@ func (h *ListHandler) AddUserToList(w http.ResponseWriter, r *http.Request) {
|
||||
// @Success 204 {object} nil
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to remove user from list"
|
||||
// @Router /api/v1/lists/user [delete]
|
||||
// @Router /lists/user [delete]
|
||||
func (h *ListHandler) RemoveUserFromList(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -166,7 +166,7 @@ func (h *ListHandler) RemoveUserFromList(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
slog.InfoContext(ctx, "removed user from list successfully", slog.Any("list_id", payload.ListID), slog.Any("user_id", payload.UserID))
|
||||
response.JSON(ctx, w, http.StatusNoContent, nil)
|
||||
response.Status(ctx, w, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// CreateListItem handles creation of a new list item on a given list
|
||||
@@ -180,7 +180,7 @@ func (h *ListHandler) RemoveUserFromList(w http.ResponseWriter, r *http.Request)
|
||||
// @Success 204 {object} nil
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to create list item"
|
||||
// @Router /api/v1/lists/item [post]
|
||||
// @Router /lists/item [post]
|
||||
func (h *ListHandler) CreateListItem(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -220,7 +220,7 @@ func (h *ListHandler) CreateListItem(w http.ResponseWriter, r *http.Request) {
|
||||
// @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 /api/v1/lists/item [put]
|
||||
// @Router /lists/item [put]
|
||||
func (h *ListHandler) UpdateListItem(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -238,14 +238,14 @@ func (h *ListHandler) UpdateListItem(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = h.ListService.UpdateListItem(ctx, payload.ListItemID, authContext.UserID, payload.Title, payload.IsCompleted)
|
||||
err = h.ListService.UpdateListItem(ctx, authContext.UserID, payload.ListItemID, payload.Title, payload.IsCompleted)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to update list item", slog.Any("error", err))
|
||||
response.Error(ctx, w, http.StatusInternalServerError, "failed to update list item")
|
||||
return
|
||||
}
|
||||
|
||||
response.JSON(ctx, w, http.StatusNoContent, nil)
|
||||
response.Status(ctx, w, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// SetListItemCompleted handles updating "is_completed" of a list item
|
||||
@@ -261,7 +261,7 @@ func (h *ListHandler) UpdateListItem(w http.ResponseWriter, r *http.Request) {
|
||||
// @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 /api/v1/lists/items/{id} [post]
|
||||
// @Router /lists/items/{id} [post]
|
||||
func (h *ListHandler) SetListItemCompleted(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -286,14 +286,14 @@ func (h *ListHandler) SetListItemCompleted(w http.ResponseWriter, r *http.Reques
|
||||
return
|
||||
}
|
||||
|
||||
err = h.ListService.SetListItemCompleted(ctx, listItemID, authContext.UserID, payload.IsCompleted)
|
||||
err = h.ListService.SetListItemCompleted(ctx, authContext.UserID, listItemID, payload.IsCompleted)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to set list item completed", slog.Any("error", err))
|
||||
response.Error(ctx, w, http.StatusInternalServerError, "failed to set list item completed")
|
||||
return
|
||||
}
|
||||
|
||||
response.JSON(ctx, w, http.StatusNoContent, nil)
|
||||
response.Status(ctx, w, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// GetListItems handles return all list items of a list
|
||||
@@ -307,7 +307,7 @@ func (h *ListHandler) SetListItemCompleted(w http.ResponseWriter, r *http.Reques
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request url"
|
||||
// @Error 401 {object} response.ErrorResponse "not authorized"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to read list items"
|
||||
// @Router /api/v1/lists/{id} [get]
|
||||
// @Router /lists/{id} [get]
|
||||
func (h *ListHandler) GetListItems(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -327,7 +327,7 @@ func (h *ListHandler) GetListItems(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
items, err := h.ListService.GetListItems(ctx, authContext.UserID, listID)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to set list item completed", slog.Any("error", err))
|
||||
slog.ErrorContext(ctx, "failed to read list items", slog.Any("error", err))
|
||||
response.Error(ctx, w, http.StatusInternalServerError, "failed to read list items")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func NewUserHandler(userService *domain.UserService) *UserHandler {
|
||||
// @Success 201 {object} domain.User
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to create user"
|
||||
// @Router /api/v1/users [post]
|
||||
// @Router /users [post]
|
||||
func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
|
||||
@@ -7,6 +7,11 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// TODO: Move to own file
|
||||
func Status(ctx context.Context, w http.ResponseWriter, status int) {
|
||||
w.WriteHeader(status)
|
||||
}
|
||||
|
||||
func JSON(ctx context.Context, w http.ResponseWriter, status int, data any) {
|
||||
payload, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
|
||||
@@ -12,6 +12,7 @@ CREATE TABLE IF NOT EXISTS list_users (
|
||||
PRIMARY KEY (list_id, user_id)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_list_users_list_id_user_id ON list_users(list_id, user_id);
|
||||
CREATE INDEX idx_list_users_user_id ON list_users(user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS list_items (
|
||||
|
||||
@@ -127,7 +127,7 @@ func (s *ListService) UpdateListItem(ctx context.Context, authUserID string, lis
|
||||
return ErrListItemTitleEmpty
|
||||
}
|
||||
|
||||
if err := s.userAllowedToAccessList(ctx, authUserID, listItemID); err != nil {
|
||||
if err := s.userAllowedToAccessListItem(ctx, authUserID, listItemID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
@@ -60,11 +61,14 @@ func (r *ListRepo) CreateList(ctx context.Context, name string, userIDs []string
|
||||
|
||||
func (r *ListRepo) GetLists(ctx context.Context, userID string) ([]domain.List, error) {
|
||||
rows, err := r.db.QueryContext(ctx,
|
||||
"SELECT id, name, created_at, modified_at FROM lists JOIN list_users ON list.id=list_users.list_id WHERE list_users.user_id = $1",
|
||||
"SELECT id, name, lists.created_at, modified_at FROM lists INNER JOIN list_users ON lists.id=list_users.list_id WHERE list_users.user_id = $1",
|
||||
userID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get list items: %w", err)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get lists: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
@@ -177,6 +181,9 @@ func (r *ListRepo) GetListItems(ctx context.Context, listID string) ([]domain.Li
|
||||
listID,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get list items: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
Reference in New Issue
Block a user