diff --git a/internal/http-server/handlers/recipes/recipes.go b/internal/http-server/handlers/recipes/recipes.go index 5e3faa7..32ffbb2 100644 --- a/internal/http-server/handlers/recipes/recipes.go +++ b/internal/http-server/handlers/recipes/recipes.go @@ -22,13 +22,13 @@ type Response struct { Recipes []models.Recipe `json:"recipes"` } -type RecipeProvider interface { +type RecipesProvider interface { GetRecipes(ctx context.Context, offset, limit int) ([]models.Recipe, error) } const getRecipesLimit int = 16 -func New(log *slog.Logger, recipeProvider RecipeProvider) http.HandlerFunc { +func New(log *slog.Logger, recipesProvider RecipesProvider) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { const op = "http-server.handlers.recipes.New" @@ -55,7 +55,7 @@ func New(log *slog.Logger, recipeProvider RecipeProvider) http.HandlerFunc { return } // get from storage - recipes, err := recipeProvider.GetRecipes(r.Context(), getRecipesLimit*(int(req.Page)-1), getRecipesLimit) + 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")) diff --git a/internal/http-server/handlers/recipesByCategory/recipesByCategory.go b/internal/http-server/handlers/recipesByCategory/recipesByCategory.go new file mode 100644 index 0000000..1b810ef --- /dev/null +++ b/internal/http-server/handlers/recipesByCategory/recipesByCategory.go @@ -0,0 +1,72 @@ +package recipes_by_category + +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"` + Category string `json:"category" validate:"required,containsany"` +} + +type Response struct { + resp.Response + Recipes []models.Recipe `json:"recipes"` +} + +type RecipesProvider interface { + GetRecipesByCategory(ctx context.Context, offset, limit int, category string) ([]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.recipesByCategory.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 recipes from storage + recipes, err := recipesProvider.GetRecipesByCategory(r.Context(), getRecipesLimit*(int(req.Page)-1), getRecipesLimit, req.Category) + 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 + // return + render.JSON(w, r, Response{ + Response: resp.OK(), + Recipes: recipes, + }) + } +}