package recipes import ( "context" "log/slog" "net/http" "recipes/internal/domain/models" resp "recipes/internal/lib/api/response" "recipes/internal/lib/logger/sl" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/render" "github.com/go-playground/validator/v10" ) type Request struct { Page uint `json:"page" validate:"required,gt=0"` } type Response struct { resp.Response Recipes []models.Recipe `json:"recipes"` } //go:generate go run github.com/vektra/mockery/v2@v2.40.1 --name=RecipesProvider type RecipesProvider interface { GetRecipes(ctx context.Context, offset, limit int) ([]models.Recipe, error) } const GetRecipesLimit int = 16 func New(log *slog.Logger, recipesProvider RecipesProvider) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { const op = "http-server.handlers.recipes.New" log := log.With( slog.String("op", op), slog.String("request_id", middleware.GetReqID(r.Context())), ) var req Request // decode request err := render.DecodeJSON(r.Body, &req) if err != nil { log.Error("failed to decode request body", sl.Err(err)) render.JSON(w, r, resp.Error("failed to decode request")) return } log.Debug("request body decoded", slog.Any("request", req)) // validate request if err := validator.New().Struct(req); err != nil { validateErr := err.(validator.ValidationErrors) log.Error("invalid request", sl.Err(err)) render.JSON(w, r, resp.ValidationError(validateErr)) return } // get from storage recipes, err := recipesProvider.GetRecipes(r.Context(), GetRecipesLimit*(int(req.Page)-1), GetRecipesLimit) if err != nil { log.Error("failed to get recipes from storage", sl.Err(err)) render.JSON(w, r, resp.Error("failed to get recipes")) return } // return render.JSON(w, r, Response{ Response: resp.OK(), Recipes: recipes, }) } }