package api import ( "database/sql" "net/http" "github.com/go-chi/chi/v5" "github.com/google/uuid" "work-queue-api/internal/model" ) type createProjectRequest struct { Name string `json:"name"` ExternalRef *string `json:"external_ref"` } type patchProjectRequest struct { Name *string `json:"name"` ExternalRef *string `json:"external_ref"` } func (s *Server) handleCreateProject(w http.ResponseWriter, r *http.Request) { var req createProjectRequest if err := decodeJSON(r, &req); err != nil { writeError(w, http.StatusBadRequest, "invalid json body") return } if req.Name == "" { writeError(w, http.StatusBadRequest, "name is required") return } now := nowUTC() project := model.Project{ ID: uuid.NewString(), Name: req.Name, ExternalRef: req.ExternalRef, CreatedAt: now, UpdatedAt: now, } _, err := s.db.Exec(`INSERT INTO projects (id, name, external_ref, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`, project.ID, project.Name, project.ExternalRef, project.CreatedAt.Format(timeLayout), project.UpdatedAt.Format(timeLayout), ) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusCreated, project) } func (s *Server) handleListProjects(w http.ResponseWriter, r *http.Request) { rows, err := s.db.Query(`SELECT id, name, external_ref, created_at, updated_at FROM projects ORDER BY created_at ASC`) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } defer rows.Close() var projects []model.Project for rows.Next() { project, err := scanProject(rows) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } projects = append(projects, project) } writeJSON(w, http.StatusOK, projects) } func (s *Server) handleGetProject(w http.ResponseWriter, r *http.Request) { project, err := s.fetchProject(chi.URLParam(r, "id")) if err == sql.ErrNoRows { writeError(w, http.StatusNotFound, "project not found") return } if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, project) } func (s *Server) handlePatchProject(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") project, err := s.fetchProject(id) if err == sql.ErrNoRows { writeError(w, http.StatusNotFound, "project not found") return } if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } var req patchProjectRequest if err := decodeJSON(r, &req); err != nil { writeError(w, http.StatusBadRequest, "invalid json body") return } if req.Name != nil { if *req.Name == "" { writeError(w, http.StatusBadRequest, "name cannot be empty") return } project.Name = *req.Name } if req.ExternalRef != nil { project.ExternalRef = req.ExternalRef } project.UpdatedAt = nowUTC() _, err = s.db.Exec(`UPDATE projects SET name = ?, external_ref = ?, updated_at = ? WHERE id = ?`, project.Name, project.ExternalRef, project.UpdatedAt.Format(timeLayout), project.ID, ) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, project) } func (s *Server) fetchProject(id string) (model.Project, error) { row := s.db.QueryRow(`SELECT id, name, external_ref, created_at, updated_at FROM projects WHERE id = ?`, id) return scanProject(row) }