You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

238 lines
5.4KB

  1. package memory
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "sync"
  7. "time"
  8. "qctextbuilder/internal/domain"
  9. )
  10. var ErrNotFound = errors.New("not found")
  11. type Store struct {
  12. mu sync.RWMutex
  13. templates map[int64]domain.Template
  14. manifests map[int64]domain.TemplateManifest
  15. manifestField map[string][]domain.TemplateField
  16. builds map[string]domain.SiteBuild
  17. }
  18. func New() *Store {
  19. return &Store{
  20. templates: make(map[int64]domain.Template),
  21. manifests: make(map[int64]domain.TemplateManifest),
  22. manifestField: make(map[string][]domain.TemplateField),
  23. builds: make(map[string]domain.SiteBuild),
  24. }
  25. }
  26. func (s *Store) UpsertTemplates(_ context.Context, templates []domain.Template) error {
  27. s.mu.Lock()
  28. defer s.mu.Unlock()
  29. for _, t := range templates {
  30. existing, ok := s.templates[t.ID]
  31. if ok {
  32. t.IsOnboarded = existing.IsOnboarded
  33. t.ManifestStatus = existing.ManifestStatus
  34. t.LastDiscoveredAt = existing.LastDiscoveredAt
  35. }
  36. s.templates[t.ID] = t
  37. }
  38. return nil
  39. }
  40. func (s *Store) GetTemplateByID(_ context.Context, id int64) (*domain.Template, error) {
  41. s.mu.RLock()
  42. defer s.mu.RUnlock()
  43. t, ok := s.templates[id]
  44. if !ok {
  45. return nil, ErrNotFound
  46. }
  47. copy := t
  48. return &copy, nil
  49. }
  50. func (s *Store) ListTemplates(_ context.Context) ([]domain.Template, error) {
  51. s.mu.RLock()
  52. defer s.mu.RUnlock()
  53. out := make([]domain.Template, 0, len(s.templates))
  54. for _, t := range s.templates {
  55. out = append(out, t)
  56. }
  57. return out, nil
  58. }
  59. func (s *Store) SetTemplateManifestStatus(_ context.Context, templateID int64, status string, onboarded bool) error {
  60. s.mu.Lock()
  61. defer s.mu.Unlock()
  62. t, ok := s.templates[templateID]
  63. if !ok {
  64. return ErrNotFound
  65. }
  66. t.ManifestStatus = status
  67. t.IsOnboarded = onboarded
  68. s.templates[templateID] = t
  69. return nil
  70. }
  71. func (s *Store) CreateManifest(_ context.Context, manifest domain.TemplateManifest, fields []domain.TemplateField) error {
  72. s.mu.Lock()
  73. defer s.mu.Unlock()
  74. s.manifests[manifest.TemplateID] = manifest
  75. s.manifestField[manifest.ID] = fields
  76. return nil
  77. }
  78. func (s *Store) GetActiveManifestByTemplateID(_ context.Context, templateID int64) (*domain.TemplateManifest, error) {
  79. s.mu.RLock()
  80. defer s.mu.RUnlock()
  81. m, ok := s.manifests[templateID]
  82. if !ok {
  83. return nil, ErrNotFound
  84. }
  85. copy := m
  86. return &copy, nil
  87. }
  88. func (s *Store) ListFieldsByManifestID(_ context.Context, manifestID string) ([]domain.TemplateField, error) {
  89. s.mu.RLock()
  90. defer s.mu.RUnlock()
  91. fields, ok := s.manifestField[manifestID]
  92. if !ok {
  93. return nil, ErrNotFound
  94. }
  95. out := make([]domain.TemplateField, 0, len(fields))
  96. out = append(out, fields...)
  97. return out, nil
  98. }
  99. func (s *Store) UpdateFields(_ context.Context, manifestID string, fields []domain.TemplateField) error {
  100. s.mu.Lock()
  101. defer s.mu.Unlock()
  102. if _, ok := s.manifestField[manifestID]; !ok {
  103. return ErrNotFound
  104. }
  105. next := make([]domain.TemplateField, len(fields))
  106. copy(next, fields)
  107. s.manifestField[manifestID] = next
  108. return nil
  109. }
  110. func (s *Store) CreateBuild(_ context.Context, build domain.SiteBuild) error {
  111. s.mu.Lock()
  112. defer s.mu.Unlock()
  113. if _, exists := s.builds[build.ID]; exists {
  114. return errors.New("build already exists")
  115. }
  116. s.builds[build.ID] = build
  117. return nil
  118. }
  119. func (s *Store) GetBuildByID(_ context.Context, id string) (*domain.SiteBuild, error) {
  120. s.mu.RLock()
  121. defer s.mu.RUnlock()
  122. build, ok := s.builds[id]
  123. if !ok {
  124. return nil, ErrNotFound
  125. }
  126. copy := build
  127. return &copy, nil
  128. }
  129. func (s *Store) ListBuildsByStatuses(_ context.Context, statuses []string, limit int) ([]domain.SiteBuild, error) {
  130. s.mu.RLock()
  131. defer s.mu.RUnlock()
  132. allowed := make(map[string]struct{}, len(statuses))
  133. for _, status := range statuses {
  134. allowed[status] = struct{}{}
  135. }
  136. out := make([]domain.SiteBuild, 0)
  137. for _, build := range s.builds {
  138. if len(allowed) > 0 {
  139. if _, ok := allowed[build.QCStatus]; !ok {
  140. continue
  141. }
  142. }
  143. out = append(out, build)
  144. if limit > 0 && len(out) >= limit {
  145. break
  146. }
  147. }
  148. return out, nil
  149. }
  150. func (s *Store) MarkBuildSubmitted(_ context.Context, buildID string, jobID int64, status string, qcResult json.RawMessage, startedAt time.Time) error {
  151. s.mu.Lock()
  152. defer s.mu.Unlock()
  153. build, ok := s.builds[buildID]
  154. if !ok {
  155. return ErrNotFound
  156. }
  157. build.QCJobID = &jobID
  158. build.QCStatus = status
  159. build.QCResultJSON = cloneRaw(qcResult)
  160. build.StartedAt = &startedAt
  161. s.builds[buildID] = build
  162. return nil
  163. }
  164. func (s *Store) UpdateBuildFromJob(_ context.Context, buildID string, status string, siteID *int64, previewURL string, qcResult json.RawMessage, qcError json.RawMessage, finishedAt *time.Time) error {
  165. s.mu.Lock()
  166. defer s.mu.Unlock()
  167. build, ok := s.builds[buildID]
  168. if !ok {
  169. return ErrNotFound
  170. }
  171. build.QCStatus = status
  172. build.QCResultJSON = cloneRaw(qcResult)
  173. build.QCErrorJSON = cloneRaw(qcError)
  174. build.QCPreviewURL = previewURL
  175. if siteID != nil {
  176. id := *siteID
  177. build.QCSiteID = &id
  178. }
  179. build.FinishedAt = finishedAt
  180. s.builds[buildID] = build
  181. return nil
  182. }
  183. func (s *Store) UpdateBuildEditorURL(_ context.Context, buildID string, editorURL string, qcResult json.RawMessage) error {
  184. s.mu.Lock()
  185. defer s.mu.Unlock()
  186. build, ok := s.builds[buildID]
  187. if !ok {
  188. return ErrNotFound
  189. }
  190. build.QCEditorURL = editorURL
  191. build.QCResultJSON = cloneRaw(qcResult)
  192. s.builds[buildID] = build
  193. return nil
  194. }
  195. func cloneRaw(raw json.RawMessage) json.RawMessage {
  196. if raw == nil {
  197. return nil
  198. }
  199. out := make([]byte, len(raw))
  200. copy(out, raw)
  201. return json.RawMessage(out)
  202. }