// monorepo/cloud/backend/internal/maplefile/interface/http/collection/create.go package collection import ( "bytes" "context" "encoding/json" "io" "net/http" "go.uber.org/zap" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/interface/http/middleware" svc_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/service/collection" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror" ) type CreateCollectionHTTPHandler struct { config *config.Configuration logger *zap.Logger service svc_collection.CreateCollectionService middleware middleware.Middleware } func NewCreateCollectionHTTPHandler( config *config.Configuration, logger *zap.Logger, service svc_collection.CreateCollectionService, middleware middleware.Middleware, ) *CreateCollectionHTTPHandler { logger = logger.Named("CreateCollectionHTTPHandler") return &CreateCollectionHTTPHandler{ config: config, logger: logger, service: service, middleware: middleware, } } func (*CreateCollectionHTTPHandler) Pattern() string { return "POST /api/v1/collections" } func (h *CreateCollectionHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Apply middleware before handling the request h.middleware.Attach(h.Execute)(w, req) } func (h *CreateCollectionHTTPHandler) unmarshalRequest( ctx context.Context, r *http.Request, ) (*svc_collection.CreateCollectionRequestDTO, error) { // Initialize our structure which will store the parsed request data var requestData svc_collection.CreateCollectionRequestDTO defer r.Body.Close() var rawJSON bytes.Buffer teeReader := io.TeeReader(r.Body, &rawJSON) // TeeReader allows you to read the JSON and capture it // Read the JSON string and convert it into our golang struct err := json.NewDecoder(teeReader).Decode(&requestData) if err != nil { h.logger.Error("Failed to decode create collection request", zap.Error(err), zap.String("json", rawJSON.String()), ) return nil, httperror.NewBadRequestError("Invalid request payload. Please check your collection data.") } return &requestData, nil } func (h *CreateCollectionHTTPHandler) Execute(w http.ResponseWriter, r *http.Request) { ctx := r.Context() req, err := h.unmarshalRequest(ctx, r) if err != nil { h.logger.Error("Failed to unmarshal create collection request", zap.Error(err)) httperror.RespondWithError(w, r, err) return } resp, err := h.service.Execute(ctx, req) if err != nil { h.logger.Error("Failed to create collection", zap.Error(err)) // Service returns RFC 9457 errors, use RespondWithError to handle them httperror.RespondWithError(w, r, err) return } if resp == nil { h.logger.Error("No collection returned from service") problem := httperror.NewInternalServerError("Failed to create collection. Please try again."). WithInstance(r.URL.Path). WithTraceID(httperror.ExtractRequestID(r)) httperror.RespondWithProblem(w, problem) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) if err := json.NewEncoder(w).Encode(resp); err != nil { h.logger.Error("Failed to encode collection response", zap.Error(err)) // At this point headers are already sent, log the error but can't send RFC 9457 response return } }