first commit
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"secunda-test/internal/domain"
|
||||
"secunda-test/internal/httpx"
|
||||
"secunda-test/internal/service"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
auth *service.AuthService
|
||||
teams *service.TeamService
|
||||
tasks *service.TaskService
|
||||
mw *httpx.Middleware
|
||||
}
|
||||
|
||||
func New(auth *service.AuthService, teams *service.TeamService, tasks *service.TaskService, mw *httpx.Middleware) *Handler {
|
||||
return &Handler{auth: auth, teams: teams, tasks: tasks, mw: mw}
|
||||
}
|
||||
|
||||
func (h *Handler) Routes() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("POST /api/v1/register", h.register)
|
||||
mux.HandleFunc("POST /api/v1/login", h.login)
|
||||
mux.Handle("POST /api/v1/teams", h.mw.Auth(http.HandlerFunc(h.createTeam)))
|
||||
mux.Handle("GET /api/v1/teams", h.mw.Auth(http.HandlerFunc(h.listTeams)))
|
||||
mux.Handle("POST /api/v1/teams/", h.mw.Auth(http.HandlerFunc(h.teamSubroutes)))
|
||||
mux.Handle("POST /api/v1/tasks", h.mw.Auth(http.HandlerFunc(h.createTask)))
|
||||
mux.Handle("GET /api/v1/tasks", h.mw.Auth(http.HandlerFunc(h.listTasks)))
|
||||
mux.Handle("PUT /api/v1/tasks/", h.mw.Auth(http.HandlerFunc(h.taskSubroutes)))
|
||||
mux.Handle("GET /api/v1/tasks/", h.mw.Auth(http.HandlerFunc(h.taskSubroutes)))
|
||||
mux.Handle("GET /api/v1/reports/team-summary", h.mw.Auth(http.HandlerFunc(h.teamSummary)))
|
||||
mux.Handle("GET /api/v1/reports/top-creators", h.mw.Auth(http.HandlerFunc(h.topCreators)))
|
||||
mux.Handle("GET /api/v1/reports/invalid-assignees", h.mw.Auth(http.HandlerFunc(h.invalidAssignees)))
|
||||
return h.mw.Chain(mux)
|
||||
}
|
||||
|
||||
func (h *Handler) register(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct{ Email, Password, Name string }
|
||||
if !decode(w, r, &req) {
|
||||
return
|
||||
}
|
||||
id, err := h.auth.Register(r.Context(), req.Email, req.Password, req.Name)
|
||||
if err != nil {
|
||||
httpx.WriteError(w, httpx.StatusFromError(err), err.Error())
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusCreated, map[string]int64{"id": id})
|
||||
}
|
||||
|
||||
func (h *Handler) login(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct{ Email, Password string }
|
||||
if !decode(w, r, &req) {
|
||||
return
|
||||
}
|
||||
token, err := h.auth.Login(r.Context(), req.Email, req.Password)
|
||||
if err != nil {
|
||||
httpx.WriteError(w, httpx.StatusFromError(err), err.Error())
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, map[string]string{"token": token})
|
||||
}
|
||||
|
||||
func (h *Handler) createTeam(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct{ Name string }
|
||||
if !decode(w, r, &req) {
|
||||
return
|
||||
}
|
||||
team, err := h.teams.Create(r.Context(), mustUserID(r), req.Name)
|
||||
respond(w, team, http.StatusCreated, err)
|
||||
}
|
||||
|
||||
func (h *Handler) listTeams(w http.ResponseWriter, r *http.Request) {
|
||||
teams, err := h.teams.List(r.Context(), mustUserID(r))
|
||||
respond(w, teams, http.StatusOK, err)
|
||||
}
|
||||
|
||||
func (h *Handler) teamSubroutes(w http.ResponseWriter, r *http.Request) {
|
||||
parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/api/v1/teams/"), "/")
|
||||
if len(parts) == 2 && parts[1] == "invite" {
|
||||
teamID, _ := strconv.ParseInt(parts[0], 10, 64)
|
||||
var req struct{ Email string }
|
||||
if !decode(w, r, &req) {
|
||||
return
|
||||
}
|
||||
err := h.teams.Invite(r.Context(), mustUserID(r), teamID, req.Email)
|
||||
respond(w, map[string]string{"status": "invited"}, http.StatusOK, err)
|
||||
return
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
func (h *Handler) createTask(w http.ResponseWriter, r *http.Request) {
|
||||
var req domain.Task
|
||||
if !decode(w, r, &req) {
|
||||
return
|
||||
}
|
||||
task, err := h.tasks.Create(r.Context(), mustUserID(r), req)
|
||||
respond(w, task, http.StatusCreated, err)
|
||||
}
|
||||
|
||||
func (h *Handler) listTasks(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
teamID, _ := strconv.ParseInt(q.Get("team_id"), 10, 64)
|
||||
page, _ := strconv.Atoi(q.Get("page"))
|
||||
pageSize, _ := strconv.Atoi(q.Get("page_size"))
|
||||
var assignee *int64
|
||||
if q.Get("assignee_id") != "" {
|
||||
v, _ := strconv.ParseInt(q.Get("assignee_id"), 10, 64)
|
||||
assignee = &v
|
||||
}
|
||||
tasks, err := h.tasks.List(r.Context(), mustUserID(r), domain.TaskFilter{
|
||||
TeamID: teamID, Status: domain.TaskStatus(q.Get("status")), AssigneeID: assignee, Page: page, PageSize: pageSize,
|
||||
})
|
||||
respond(w, tasks, http.StatusOK, err)
|
||||
}
|
||||
|
||||
func (h *Handler) taskSubroutes(w http.ResponseWriter, r *http.Request) {
|
||||
path := strings.TrimPrefix(r.URL.Path, "/api/v1/tasks/")
|
||||
parts := strings.Split(path, "/")
|
||||
id, _ := strconv.ParseInt(parts[0], 10, 64)
|
||||
if len(parts) == 2 && parts[1] == "history" && r.Method == http.MethodGet {
|
||||
history, err := h.tasks.History(r.Context(), mustUserID(r), id)
|
||||
respond(w, history, http.StatusOK, err)
|
||||
return
|
||||
}
|
||||
if len(parts) == 1 && r.Method == http.MethodPut {
|
||||
var req domain.Task
|
||||
if !decode(w, r, &req) {
|
||||
return
|
||||
}
|
||||
task, err := h.tasks.Update(r.Context(), mustUserID(r), id, req)
|
||||
respond(w, task, http.StatusOK, err)
|
||||
return
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
func (h *Handler) teamSummary(w http.ResponseWriter, r *http.Request) {
|
||||
out, err := h.tasks.TeamSummary(r.Context())
|
||||
respond(w, out, http.StatusOK, err)
|
||||
}
|
||||
|
||||
func (h *Handler) topCreators(w http.ResponseWriter, r *http.Request) {
|
||||
out, err := h.tasks.TopCreators(r.Context())
|
||||
respond(w, out, http.StatusOK, err)
|
||||
}
|
||||
|
||||
func (h *Handler) invalidAssignees(w http.ResponseWriter, r *http.Request) {
|
||||
out, err := h.tasks.InvalidAssignees(r.Context())
|
||||
respond(w, out, http.StatusOK, err)
|
||||
}
|
||||
|
||||
func decode(w http.ResponseWriter, r *http.Request, v any) bool {
|
||||
if err := json.NewDecoder(r.Body).Decode(v); err != nil {
|
||||
httpx.WriteError(w, http.StatusBadRequest, "invalid json")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func respond(w http.ResponseWriter, v any, status int, err error) {
|
||||
if err != nil {
|
||||
httpx.WriteError(w, httpx.StatusFromError(err), err.Error())
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, status, v)
|
||||
}
|
||||
|
||||
func mustUserID(r *http.Request) int64 {
|
||||
id, _ := httpx.UserID(r.Context())
|
||||
return id
|
||||
}
|
||||
Reference in New Issue
Block a user