package handlers import ( "errors" "net/http" "github.com/go-chi/chi/v5" "github.com/google/uuid" "git.threesix.ai/jordan/slate-test-1770507751/pkg/app" "git.threesix.ai/jordan/slate-test-1770507751/pkg/httperror" "git.threesix.ai/jordan/slate-test-1770507751/pkg/httpresponse" "git.threesix.ai/jordan/slate-test-1770507751/pkg/logging" "git.threesix.ai/jordan/slate-test-1770507751/services/preferences-api/internal/domain" "git.threesix.ai/jordan/slate-test-1770507751/services/preferences-api/internal/service" ) // Example handles HTTP requests for example resources. type Example struct { svc *service.ExampleService logger *logging.Logger } // NewExample creates a new Example handler with injected dependencies. func NewExample(svc *service.ExampleService, logger *logging.Logger) *Example { return &Example{ svc: svc, logger: logger.WithComponent("ExampleHandler"), } } // CreateRequest is the request body for creating an example. type CreateRequest struct { Name string `json:"name" validate:"required,min=1,max=100"` Description string `json:"description" validate:"max=500"` } // UpdateRequest is the request body for updating an example. type UpdateRequest struct { Name string `json:"name" validate:"required,min=1,max=100"` Description string `json:"description" validate:"max=500"` } // ExampleResponse is the response for an example resource. type ExampleResponse struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } // toResponse converts a domain example to an API response. func toResponse(e *domain.Example) ExampleResponse { return ExampleResponse{ ID: e.ID.String(), Name: e.Name, Description: e.Description, CreatedAt: e.CreatedAt.Format("2006-01-02T15:04:05Z"), UpdatedAt: e.UpdatedAt.Format("2006-01-02T15:04:05Z"), } } // List returns all examples. func (h *Example) List(w http.ResponseWriter, r *http.Request) error { examples, err := h.svc.List(r.Context()) if err != nil { return err } result := make([]ExampleResponse, len(examples)) for i, e := range examples { result[i] = toResponse(&e) } httpresponse.OK(w, r, result) return nil } // Get returns an example by ID. func (h *Example) Get(w http.ResponseWriter, r *http.Request) error { id := chi.URLParam(r, "id") // Validate UUID format if _, err := uuid.Parse(id); err != nil { return httperror.BadRequest("invalid id format") } example, err := h.svc.Get(r.Context(), domain.ExampleID(id)) if err != nil { return mapDomainError(err) } httpresponse.OK(w, r, toResponse(example)) return nil } // Create creates a new example. func (h *Example) Create(w http.ResponseWriter, r *http.Request) error { var req CreateRequest if err := app.BindAndValidate(r, &req); err != nil { return err } example, err := h.svc.Create(r.Context(), service.CreateInput{ Name: req.Name, Description: req.Description, }) if err != nil { return mapDomainError(err) } httpresponse.Created(w, r, toResponse(example)) return nil } // Update updates an existing example. func (h *Example) Update(w http.ResponseWriter, r *http.Request) error { id := chi.URLParam(r, "id") if _, err := uuid.Parse(id); err != nil { return httperror.BadRequest("invalid id format") } var req UpdateRequest if err := app.BindAndValidate(r, &req); err != nil { return err } example, err := h.svc.Update(r.Context(), domain.ExampleID(id), service.UpdateInput{ Name: req.Name, Description: req.Description, }) if err != nil { return mapDomainError(err) } httpresponse.OK(w, r, toResponse(example)) return nil } // Delete removes an example by ID. func (h *Example) Delete(w http.ResponseWriter, r *http.Request) error { id := chi.URLParam(r, "id") if _, err := uuid.Parse(id); err != nil { return httperror.BadRequest("invalid id format") } if err := h.svc.Delete(r.Context(), domain.ExampleID(id)); err != nil { return mapDomainError(err) } httpresponse.NoContent(w) return nil } // mapDomainError converts domain errors to HTTP errors. func mapDomainError(err error) error { switch { case errors.Is(err, domain.ErrExampleNotFound): return httperror.NotFound("example not found") case errors.Is(err, domain.ErrDuplicateExample): return httperror.Conflict("example with this name already exists") case errors.Is(err, domain.ErrInvalidExampleName): return httperror.BadRequest("invalid example name") default: return err } }