119 lines
3.6 KiB
Go
119 lines
3.6 KiB
Go
package httpx
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
bootstrapservice "moku-backend/internal/bootstrap"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
type createProjectFolderRequest struct {
|
|
Name string `json:"name"`
|
|
ParentFolderID string `json:"parentFolderId"`
|
|
}
|
|
|
|
func (routes apiRoutes) handleProjectFolders(w http.ResponseWriter, r *http.Request) {
|
|
projectID := strings.TrimSpace(chi.URLParam(r, "projectId"))
|
|
if projectID == "" {
|
|
WriteError(w, http.StatusBadRequest, RequestIDFromContext(r.Context()), "invalid_request", "Project ID is required.")
|
|
return
|
|
}
|
|
|
|
folders, err := routes.bootstrapService().GetProjectHierarchyFolders(r.Context(), projectID)
|
|
if err != nil {
|
|
routes.writeProjectFolderError(w, r, err)
|
|
return
|
|
}
|
|
|
|
WriteJSON(w, http.StatusOK, map[string]any{
|
|
"data": map[string]any{
|
|
"projectId": projectID,
|
|
"folders": folders,
|
|
},
|
|
"meta": map[string]any{
|
|
"resource": "project-folders",
|
|
},
|
|
})
|
|
}
|
|
|
|
func (routes apiRoutes) handleCreateProjectFolder(w http.ResponseWriter, r *http.Request) {
|
|
projectID := strings.TrimSpace(chi.URLParam(r, "projectId"))
|
|
if projectID == "" {
|
|
WriteError(w, http.StatusBadRequest, RequestIDFromContext(r.Context()), "invalid_request", "Project ID is required.")
|
|
return
|
|
}
|
|
|
|
payload, ok := decodeProjectFolderRequest(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
payload.Name = strings.TrimSpace(payload.Name)
|
|
payload.ParentFolderID = strings.TrimSpace(payload.ParentFolderID)
|
|
if payload.Name == "" {
|
|
WriteError(w, http.StatusBadRequest, RequestIDFromContext(r.Context()), "invalid_request", "Folder name is required.")
|
|
return
|
|
}
|
|
|
|
result, err := routes.bootstrapService().CreateProjectFolder(r.Context(), bootstrapservice.CreateProjectFolderInput{
|
|
ProjectID: projectID,
|
|
ParentFolderID: payload.ParentFolderID,
|
|
Name: payload.Name,
|
|
})
|
|
if err != nil {
|
|
routes.writeProjectFolderError(w, r, err)
|
|
return
|
|
}
|
|
|
|
WriteJSON(w, http.StatusCreated, map[string]any{
|
|
"data": result,
|
|
"meta": map[string]any{
|
|
"resource": "project-folder-create",
|
|
"persisted": true,
|
|
},
|
|
})
|
|
}
|
|
|
|
func (routes apiRoutes) writeProjectFolderError(w http.ResponseWriter, r *http.Request, err error) {
|
|
switch {
|
|
case errors.Is(err, bootstrapservice.ErrProjectNotFound), errors.Is(err, bootstrapservice.ErrProjectFolderNotFound):
|
|
WriteError(w, http.StatusNotFound, RequestIDFromContext(r.Context()), "not_found", err.Error())
|
|
default:
|
|
routes.cfg.Logger.Error("persist project folder", "error", err, "path", r.URL.Path)
|
|
message := "Failed to persist project folder."
|
|
if routes.cfg.Config.IsDevelopment() {
|
|
message = message + " " + err.Error()
|
|
}
|
|
WriteError(w, http.StatusInternalServerError, RequestIDFromContext(r.Context()), "project_folder_persist_failed", message)
|
|
}
|
|
}
|
|
|
|
func decodeProjectFolderRequest(w http.ResponseWriter, r *http.Request) (createProjectFolderRequest, bool) {
|
|
var payload createProjectFolderRequest
|
|
|
|
decoder := json.NewDecoder(r.Body)
|
|
decoder.DisallowUnknownFields()
|
|
|
|
if err := decoder.Decode(&payload); err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
WriteError(w, http.StatusBadRequest, RequestIDFromContext(r.Context()), "invalid_json", "The request body is required and must be valid JSON.")
|
|
return payload, false
|
|
}
|
|
|
|
WriteError(w, http.StatusBadRequest, RequestIDFromContext(r.Context()), "invalid_json", "The request body must be valid JSON.")
|
|
return payload, false
|
|
}
|
|
|
|
if err := decoder.Decode(&struct{}{}); !errors.Is(err, io.EOF) {
|
|
WriteError(w, http.StatusBadRequest, RequestIDFromContext(r.Context()), "invalid_json", "The request body must contain a single JSON object.")
|
|
return payload, false
|
|
}
|
|
|
|
return payload, true
|
|
}
|