171 lines
4.5 KiB
Go
171 lines
4.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/google/uuid"
|
|
|
|
"git.threesix.ai/jordan/persona-community-2/pkg/app"
|
|
"git.threesix.ai/jordan/persona-community-2/pkg/httperror"
|
|
"git.threesix.ai/jordan/persona-community-2/pkg/httpresponse"
|
|
"git.threesix.ai/jordan/persona-community-2/pkg/logging"
|
|
"git.threesix.ai/jordan/persona-community-2/services/persona-api/internal/domain"
|
|
"git.threesix.ai/jordan/persona-community-2/services/persona-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
|
|
}
|
|
}
|