feat: added exercises handler/service/repo
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/robindittmar/dttmr-api/internal/api/request"
|
||||
"github.com/robindittmar/dttmr-api/internal/api/response"
|
||||
"github.com/robindittmar/dttmr-api/internal/domain"
|
||||
)
|
||||
|
||||
type ExerciseHandler struct {
|
||||
ExerciseService *domain.ExerciseService
|
||||
}
|
||||
|
||||
func NewExerciseHandler(exerciseService *domain.ExerciseService) *ExerciseHandler {
|
||||
return &ExerciseHandler{ExerciseService: exerciseService}
|
||||
}
|
||||
|
||||
// GetExercises handles fetching the list of exercises
|
||||
//
|
||||
// @Summary Get exercises route
|
||||
// @Description Gets a list of all exercises
|
||||
// @Tags Exercise
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "page"
|
||||
// @Param count query int false "count"
|
||||
// @Success 200 {object} response.Paginated[domain.Exercise]
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||
// @Error 400 {object} response.ErrorResponse "invalid value for page"
|
||||
// @Error 400 {object} response.ErrorResponse "invalid value for count"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to get exercises"
|
||||
// @Router /exercises [get]
|
||||
func (h *ExerciseHandler) GetExercises(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
page, count, err := request.ParsePaginatedQueryParams(r)
|
||||
if err != nil {
|
||||
response.Error(ctx, w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
exercises, err := h.ExerciseService.GetExercises(ctx, page, count)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to get exercises", slog.Any("error", err))
|
||||
response.Error(ctx, w, http.StatusInternalServerError, "failed to get exercises")
|
||||
return
|
||||
}
|
||||
|
||||
total, err := h.ExerciseService.CountExercises(ctx)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to count exercises", slog.Any("error", err))
|
||||
response.Error(ctx, w, http.StatusInternalServerError, "failed to get exercises")
|
||||
return
|
||||
}
|
||||
|
||||
response.JSON(ctx, w, http.StatusOK, response.Paginated[domain.Exercise]{
|
||||
Count: len(exercises),
|
||||
Total: total,
|
||||
Data: exercises,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFailedToDecodeRequestQuery = errors.New("failed to decode request query")
|
||||
ErrInvalidPageValue = errors.New("invalid value for page")
|
||||
ErrInvalidCountValue = errors.New("invalid value for count")
|
||||
)
|
||||
|
||||
func ParsePaginatedQueryParams(r *http.Request) (int, int, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
pageStr := r.URL.Query().Get("page")
|
||||
if pageStr == "" {
|
||||
pageStr = "1"
|
||||
}
|
||||
countStr := r.URL.Query().Get("count")
|
||||
if countStr == "" {
|
||||
countStr = "10"
|
||||
}
|
||||
|
||||
page, err := strconv.Atoi(pageStr)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx,
|
||||
"failed to read page from query",
|
||||
slog.String("page", pageStr))
|
||||
return 0, 0, ErrFailedToDecodeRequestQuery
|
||||
}
|
||||
|
||||
count, err := strconv.Atoi(countStr)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx,
|
||||
"failed to read count from query",
|
||||
slog.String("count", countStr))
|
||||
return 0, 0, ErrFailedToDecodeRequestQuery
|
||||
}
|
||||
|
||||
if page < 1 {
|
||||
slog.ErrorContext(ctx,
|
||||
"page parameter is invalid",
|
||||
slog.Int("page", page))
|
||||
return 0, 0, ErrInvalidPageValue
|
||||
}
|
||||
if count <= 0 {
|
||||
slog.ErrorContext(ctx,
|
||||
"count parameter is invalid",
|
||||
slog.Int("count", count))
|
||||
return 0, 0, ErrInvalidCountValue
|
||||
}
|
||||
|
||||
return page, count, nil
|
||||
}
|
||||
@@ -26,11 +26,13 @@ func NewMux(cfg Config) http.Handler {
|
||||
userService := domain.NewUserService(store.User)
|
||||
registrationService := domain.NewRegistrationService(store, userService, inviteService)
|
||||
listService := domain.NewListService(store, store.List)
|
||||
exerciseService := domain.NewExerciseService(store.Exercise)
|
||||
|
||||
authHandler := handler.NewAuthHandler(authService)
|
||||
inviteHandler := handler.NewInviteHandler(inviteService)
|
||||
userHandler := handler.NewUserHandler(userService, authService, registrationService)
|
||||
listHandler := handler.NewListHandler(listService, userService)
|
||||
exerciseHandler := handler.NewExerciseHandler(exerciseService)
|
||||
|
||||
protected := middleware.WithJWT(authService)
|
||||
|
||||
@@ -70,6 +72,9 @@ func NewMux(cfg Config) http.Handler {
|
||||
apiMux.Handle("POST /lists/items/{id}/complete", protected(listHandler.SetListItemCompleted))
|
||||
apiMux.Handle("GET /lists/{id}", protected(listHandler.GetListItems))
|
||||
|
||||
// Exercises
|
||||
apiMux.Handle("GET /exercises", protected(exerciseHandler.GetExercises))
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/api/v1/", http.StripPrefix("/api/v1", apiMux))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user