package recipe import ( "context" "errors" "log/slog" "net/http" "recipes/internal/domain/models" resp "recipes/internal/lib/api/response" "recipes/internal/lib/logger/sl" "recipes/internal/storage" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/render" "github.com/go-playground/validator/v10" ) type Request struct { RecipeId uint `json:"recipe_id" validate:"required"` } type Response struct { resp.Response Recipe models.Recipe `json:"recipe"` } //go:generate go run github.com/vektra/mockery/v2@v2.40.1 --name=RecipeProvider type RecipeProvider interface { GetRecipe(ctx context.Context, r_id uint) (models.Recipe, error) } func New(log *slog.Logger, recipeProvider RecipeProvider) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { const op = "http-server.handlers.recipe.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 recipe, err := recipeProvider.GetRecipe(r.Context(), req.RecipeId) if err != nil { log.Error("failed to get recipe from storage", sl.Err(err)) if errors.Is(err, storage.ErrRecipeNotFound) { render.JSON(w, r, resp.Error("recipe not found")) return } render.JSON(w, r, resp.Error("failed to get recipe")) return } // render response render.JSON(w, r, Response{ Response: resp.OK(), Recipe: recipe, }) } }