Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

295 rindas
8.9KB

  1. package handlers
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "strconv"
  6. "strings"
  7. "github.com/go-chi/chi/v5"
  8. "qctextbuilder/internal/buildsvc"
  9. "qctextbuilder/internal/draftsvc"
  10. "qctextbuilder/internal/onboarding"
  11. "qctextbuilder/internal/templatesvc"
  12. )
  13. type API struct {
  14. templateSvc *templatesvc.Service
  15. onboardSvc *onboarding.Service
  16. draftSvc *draftsvc.Service
  17. buildSvc buildsvc.Service
  18. }
  19. func NewAPI(templateSvc *templatesvc.Service, onboardSvc *onboarding.Service, draftSvc *draftsvc.Service, buildSvc buildsvc.Service) *API {
  20. return &API{
  21. templateSvc: templateSvc,
  22. onboardSvc: onboardSvc,
  23. draftSvc: draftSvc,
  24. buildSvc: buildSvc,
  25. }
  26. }
  27. func (a *API) Health(w http.ResponseWriter, _ *http.Request) {
  28. writeJSON(w, http.StatusOK, map[string]any{"status": "ok"})
  29. }
  30. func (a *API) SyncTemplates(w http.ResponseWriter, r *http.Request) {
  31. templates, err := a.templateSvc.SyncAITemplates(r.Context())
  32. if err != nil {
  33. writeJSON(w, http.StatusBadGateway, map[string]any{"error": err.Error()})
  34. return
  35. }
  36. writeJSON(w, http.StatusOK, map[string]any{"count": len(templates), "templates": templates})
  37. }
  38. func (a *API) ListTemplates(w http.ResponseWriter, r *http.Request) {
  39. templates, err := a.templateSvc.ListTemplates(r.Context())
  40. if err != nil {
  41. writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
  42. return
  43. }
  44. writeJSON(w, http.StatusOK, map[string]any{"count": len(templates), "templates": templates})
  45. }
  46. func (a *API) GetTemplateDetail(w http.ResponseWriter, r *http.Request) {
  47. rawID := chi.URLParam(r, "id")
  48. templateID, err := strconv.ParseInt(rawID, 10, 64)
  49. if err != nil {
  50. writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid template id"})
  51. return
  52. }
  53. detail, err := a.templateSvc.GetTemplateDetail(r.Context(), templateID)
  54. if err != nil {
  55. writeJSON(w, http.StatusNotFound, map[string]any{"error": err.Error()})
  56. return
  57. }
  58. writeJSON(w, http.StatusOK, detail)
  59. }
  60. func (a *API) OnboardTemplate(w http.ResponseWriter, r *http.Request) {
  61. rawID := chi.URLParam(r, "id")
  62. templateID, err := strconv.ParseInt(rawID, 10, 64)
  63. if err != nil {
  64. writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid template id"})
  65. return
  66. }
  67. manifest, fields, err := a.onboardSvc.OnboardTemplate(r.Context(), templateID)
  68. if err != nil {
  69. writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
  70. return
  71. }
  72. writeJSON(w, http.StatusOK, map[string]any{
  73. "manifestId": manifest.ID,
  74. "fieldCount": len(fields),
  75. "status": "reviewed",
  76. })
  77. }
  78. type updateTemplateFieldsRequest struct {
  79. ManifestID string `json:"manifestId"`
  80. Fields []updateTemplateFieldItem `json:"fields"`
  81. }
  82. type updateTemplateFieldItem struct {
  83. Path string `json:"path"`
  84. IsEnabled *bool `json:"isEnabled,omitempty"`
  85. IsRequiredByUs *bool `json:"isRequiredByUs,omitempty"`
  86. DisplayLabel *string `json:"displayLabel,omitempty"`
  87. DisplayOrder *int `json:"displayOrder,omitempty"`
  88. Notes *string `json:"notes,omitempty"`
  89. }
  90. func (a *API) UpdateTemplateFields(w http.ResponseWriter, r *http.Request) {
  91. rawID := chi.URLParam(r, "id")
  92. templateID, err := strconv.ParseInt(rawID, 10, 64)
  93. if err != nil {
  94. writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid template id"})
  95. return
  96. }
  97. var req updateTemplateFieldsRequest
  98. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  99. writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid JSON body"})
  100. return
  101. }
  102. if len(req.Fields) == 0 {
  103. writeJSON(w, http.StatusBadRequest, map[string]any{"error": "fields is required"})
  104. return
  105. }
  106. patches := make([]onboarding.FieldPatch, 0, len(req.Fields))
  107. for _, f := range req.Fields {
  108. patches = append(patches, onboarding.FieldPatch{
  109. Path: f.Path,
  110. IsEnabled: f.IsEnabled,
  111. IsRequiredByUs: f.IsRequiredByUs,
  112. DisplayLabel: f.DisplayLabel,
  113. DisplayOrder: f.DisplayOrder,
  114. Notes: f.Notes,
  115. })
  116. }
  117. manifest, fields, err := a.onboardSvc.UpdateTemplateFields(r.Context(), templateID, req.ManifestID, patches)
  118. if err != nil {
  119. writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
  120. return
  121. }
  122. writeJSON(w, http.StatusOK, map[string]any{
  123. "templateId": templateID,
  124. "manifestId": manifest.ID,
  125. "fieldCount": len(fields),
  126. "fields": fields,
  127. })
  128. }
  129. func (a *API) StartBuild(w http.ResponseWriter, r *http.Request) {
  130. var req buildsvc.StartBuildRequest
  131. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  132. writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid JSON body"})
  133. return
  134. }
  135. result, err := a.buildSvc.StartBuild(r.Context(), req)
  136. if err != nil {
  137. writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
  138. return
  139. }
  140. writeJSON(w, http.StatusAccepted, result)
  141. }
  142. type upsertDraftRequest struct {
  143. TemplateID int64 `json:"templateId"`
  144. ManifestID string `json:"manifestId"`
  145. Source string `json:"source"`
  146. RequestName string `json:"requestName"`
  147. GlobalData map[string]any `json:"globalData"`
  148. FieldValues map[string]string `json:"fieldValues"`
  149. Status string `json:"status"`
  150. Notes string `json:"notes"`
  151. }
  152. func (a *API) IntakeDraft(w http.ResponseWriter, r *http.Request) {
  153. var req upsertDraftRequest
  154. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  155. writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid JSON body"})
  156. return
  157. }
  158. draft, err := a.draftSvc.SaveDraft(r.Context(), draftsvc.UpsertDraftRequest{
  159. TemplateID: req.TemplateID,
  160. ManifestID: req.ManifestID,
  161. Source: defaultStr(req.Source, "intake-api"),
  162. RequestName: req.RequestName,
  163. GlobalData: req.GlobalData,
  164. FieldValues: req.FieldValues,
  165. Status: defaultStr(req.Status, "draft"),
  166. Notes: req.Notes,
  167. })
  168. if err != nil {
  169. writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
  170. return
  171. }
  172. writeJSON(w, http.StatusCreated, draft)
  173. }
  174. func (a *API) ListDrafts(w http.ResponseWriter, r *http.Request) {
  175. limit, _ := strconv.Atoi(strings.TrimSpace(r.URL.Query().Get("limit")))
  176. drafts, err := a.draftSvc.ListDrafts(r.Context(), limit)
  177. if err != nil {
  178. writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
  179. return
  180. }
  181. writeJSON(w, http.StatusOK, map[string]any{"count": len(drafts), "drafts": drafts})
  182. }
  183. func (a *API) GetDraft(w http.ResponseWriter, r *http.Request) {
  184. draftID := strings.TrimSpace(chi.URLParam(r, "id"))
  185. draft, err := a.draftSvc.GetDraft(r.Context(), draftID)
  186. if err != nil {
  187. writeJSON(w, http.StatusNotFound, map[string]any{"error": err.Error()})
  188. return
  189. }
  190. writeJSON(w, http.StatusOK, draft)
  191. }
  192. func (a *API) UpdateDraft(w http.ResponseWriter, r *http.Request) {
  193. draftID := strings.TrimSpace(chi.URLParam(r, "id"))
  194. var req upsertDraftRequest
  195. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  196. writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid JSON body"})
  197. return
  198. }
  199. draft, err := a.draftSvc.SaveDraft(r.Context(), draftsvc.UpsertDraftRequest{
  200. DraftID: draftID,
  201. TemplateID: req.TemplateID,
  202. ManifestID: req.ManifestID,
  203. Source: req.Source,
  204. RequestName: req.RequestName,
  205. GlobalData: req.GlobalData,
  206. FieldValues: req.FieldValues,
  207. Status: req.Status,
  208. Notes: req.Notes,
  209. })
  210. if err != nil {
  211. writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
  212. return
  213. }
  214. writeJSON(w, http.StatusOK, draft)
  215. }
  216. func (a *API) GetBuild(w http.ResponseWriter, r *http.Request) {
  217. buildID := chi.URLParam(r, "id")
  218. build, err := a.buildSvc.GetBuild(r.Context(), buildID)
  219. if err != nil {
  220. writeJSON(w, http.StatusNotFound, map[string]any{"error": err.Error()})
  221. return
  222. }
  223. writeJSON(w, http.StatusOK, build)
  224. }
  225. func (a *API) PollBuildOnce(w http.ResponseWriter, r *http.Request) {
  226. buildID := chi.URLParam(r, "id")
  227. if err := a.buildSvc.PollOnce(r.Context(), buildID); err != nil {
  228. writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
  229. return
  230. }
  231. build, err := a.buildSvc.GetBuild(r.Context(), buildID)
  232. if err != nil {
  233. writeJSON(w, http.StatusNotFound, map[string]any{"error": err.Error()})
  234. return
  235. }
  236. writeJSON(w, http.StatusOK, build)
  237. }
  238. func (a *API) FetchBuildEditorURL(w http.ResponseWriter, r *http.Request) {
  239. buildID := chi.URLParam(r, "id")
  240. if err := a.buildSvc.FetchEditorURL(r.Context(), buildID); err != nil {
  241. writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
  242. return
  243. }
  244. build, err := a.buildSvc.GetBuild(r.Context(), buildID)
  245. if err != nil {
  246. writeJSON(w, http.StatusNotFound, map[string]any{"error": err.Error()})
  247. return
  248. }
  249. writeJSON(w, http.StatusOK, build)
  250. }
  251. func writeJSON(w http.ResponseWriter, status int, v any) {
  252. w.Header().Set("Content-Type", "application/json")
  253. w.WriteHeader(status)
  254. _ = json.NewEncoder(w).Encode(v)
  255. }
  256. func defaultStr(v, fallback string) string {
  257. if strings.TrimSpace(v) == "" {
  258. return fallback
  259. }
  260. return strings.TrimSpace(v)
  261. }