102 lines
2.6 KiB
Go
102 lines
2.6 KiB
Go
package handler
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/wotra/wotra/internal/domain"
|
|
"github.com/wotra/wotra/internal/service"
|
|
)
|
|
|
|
// DayHandler serves /api/days routes.
|
|
type DayHandler struct {
|
|
svc *service.DayService
|
|
}
|
|
|
|
func NewDayHandler(svc *service.DayService) *DayHandler {
|
|
return &DayHandler{svc: svc}
|
|
}
|
|
|
|
func (h *DayHandler) Routes(r chi.Router) {
|
|
r.Get("/days", h.List)
|
|
r.Post("/days/{day_key}/close", h.Close)
|
|
r.Post("/days/{day_key}/mark", h.Mark)
|
|
r.Delete("/days/{day_key}/close", h.Reopen)
|
|
}
|
|
|
|
// List GET /api/days?from=YYYY-MM-DD&to=YYYY-MM-DD
|
|
func (h *DayHandler) List(w http.ResponseWriter, r *http.Request) {
|
|
from := r.URL.Query().Get("from")
|
|
to := r.URL.Query().Get("to")
|
|
if from == "" {
|
|
from = "0000-01-01"
|
|
}
|
|
if to == "" {
|
|
to = "9999-12-31"
|
|
}
|
|
days, err := h.svc.ListDays(r.Context(), from, to)
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
if days == nil {
|
|
writeJSON(w, http.StatusOK, []struct{}{})
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, days)
|
|
}
|
|
|
|
// Close POST /api/days/{day_key}/close
|
|
func (h *DayHandler) Close(w http.ResponseWriter, r *http.Request) {
|
|
dayKey := chi.URLParam(r, "day_key")
|
|
cd, err := h.svc.CloseDay(r.Context(), dayKey)
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, service.ErrDayAlreadyClosed):
|
|
writeError(w, http.StatusConflict, err.Error())
|
|
case errors.Is(err, service.ErrRunningEntryOnDay):
|
|
writeError(w, http.StatusConflict, err.Error())
|
|
case errors.Is(err, service.ErrDayHasNoEntries):
|
|
writeError(w, http.StatusUnprocessableEntity, err.Error())
|
|
default:
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
}
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, cd)
|
|
}
|
|
|
|
// Mark POST /api/days/{day_key}/mark
|
|
func (h *DayHandler) Mark(w http.ResponseWriter, r *http.Request) {
|
|
dayKey := chi.URLParam(r, "day_key")
|
|
var body struct {
|
|
Kind domain.DayKind `json:"kind"`
|
|
}
|
|
if err := decodeJSON(r, &body); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON")
|
|
return
|
|
}
|
|
cd, err := h.svc.MarkDay(r.Context(), dayKey, body.Kind)
|
|
if err != nil {
|
|
writeError(w, http.StatusUnprocessableEntity, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, cd)
|
|
}
|
|
|
|
// Reopen DELETE /api/days/{day_key}/close
|
|
func (h *DayHandler) Reopen(w http.ResponseWriter, r *http.Request) {
|
|
dayKey := chi.URLParam(r, "day_key")
|
|
if err := h.svc.ReopenDay(r.Context(), dayKey); err != nil {
|
|
switch {
|
|
case errors.Is(err, service.ErrDayNotClosed):
|
|
writeError(w, http.StatusNotFound, err.Error())
|
|
default:
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
}
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|