@adobe/spacecat-shared-data-access 3.63.0 → 3.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [@adobe/spacecat-shared-data-access-v3.64.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.63.0...@adobe/spacecat-shared-data-access-v3.64.0) (2026-05-15)
2
+
3
+ ### Features
4
+
5
+ * strict schema for data.issues[] with per-issue lifecycle fields ([#1594](https://github.com/adobe/spacecat-shared/issues/1594)) ([d101db4](https://github.com/adobe/spacecat-shared/commit/d101db4e6407cfe9cbb742d54b500439cf62b64f))
6
+
1
7
  ## [@adobe/spacecat-shared-data-access-v3.63.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.62.0...@adobe/spacecat-shared-data-access-v3.63.0) (2026-05-14)
2
8
 
3
9
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-data-access",
3
- "version": "3.63.0",
3
+ "version": "3.64.0",
4
4
  "description": "Shared modules of the Spacecat Services - Data Access",
5
5
  "type": "module",
6
6
  "engines": {
@@ -20,3 +20,11 @@ export {
20
20
 
21
21
  // Export DATA_SCHEMAS for api-service to reference
22
22
  export const { DATA_SCHEMAS } = Suggestion;
23
+
24
+ // Re-export per-issue lifecycle constants so consumers (audit-worker, autofix-worker,
25
+ // api-service, Mystique) can validate per-issue values without duplicating enum lists.
26
+ export {
27
+ CWV_METRIC_TYPES,
28
+ ISSUE_STATUSES,
29
+ ISSUE_SKIP_REASONS,
30
+ } from './suggestion.data-schemas.js';
@@ -26,6 +26,85 @@
26
26
  import Joi from 'joi';
27
27
  import { OPPORTUNITY_TYPES } from '@adobe/spacecat-shared-utils';
28
28
 
29
+ /**
30
+ * Status and skip-reason values for per-issue lifecycle inside `data.issues[]`.
31
+ *
32
+ * Exported for consumers that need to construct or validate per-issue values
33
+ * (audit-worker, autofix-worker, api-service controllers, Mystique).
34
+ *
35
+ * Duplicated from `Suggestion.STATUSES` / `Suggestion.SKIP_REASONS` (suggestion.model.js)
36
+ * to avoid a circular import — the model already imports DATA_SCHEMAS from this file.
37
+ * Keep in sync if either set ever changes.
38
+ */
39
+ export const ISSUE_STATUSES = [
40
+ 'NEW',
41
+ 'APPROVED',
42
+ 'IN_PROGRESS',
43
+ 'SKIPPED',
44
+ 'FIXED',
45
+ 'ERROR',
46
+ 'OUTDATED',
47
+ 'PENDING_VALIDATION',
48
+ 'REJECTED',
49
+ ];
50
+
51
+ export const ISSUE_SKIP_REASONS = [
52
+ 'ALREADY_IMPLEMENTED',
53
+ 'INACCURATE_OR_INCOMPLETE',
54
+ 'TOO_RISKY',
55
+ 'NO_REASON',
56
+ 'OTHER',
57
+ ];
58
+
59
+ /**
60
+ * Allowed CWV metric types for `data.issues[].type`.
61
+ * Exported so audit-worker / Mystique / api-service can reuse the same source of truth.
62
+ */
63
+ export const CWV_METRIC_TYPES = ['lcp', 'cls', 'inp'];
64
+
65
+ /**
66
+ * Strict per-issue schema for `data.issues[]` on CWV suggestions.
67
+ *
68
+ * All fields are optional initially so existing prod rows (issues without `id` / `type` /
69
+ * `status`) keep validating. Tighten to required after a backfill populates `id` and `type`
70
+ * on every existing issue.
71
+ *
72
+ * Field ownership:
73
+ * - `id`, `type`, `value`, `title`, `cwvValue`, `patchContent`, `isCodeChangeAvailable`,
74
+ * initial `status: 'NEW'` — written by Mystique (guidance + code-fix tasks).
75
+ * - `status` transitions (APPROVED/REJECTED/SKIPPED/IN_PROGRESS) — written by api-service
76
+ * via the existing PATCH /suggestions/:id endpoint (UI does read-modify-write of the
77
+ * whole `data` payload).
78
+ * - `status: FIXED | ERROR` — written by autofix-worker after PR creation.
79
+ * - `status: OUTDATED` — written by audit-worker `handleOutdatedSuggestions` on re-audit.
80
+ * - `fixEntityId` — written by autofix-worker (or Mystique `_create_fix_entity`).
81
+ * - `jiraLink` — written by whichever service files the Jira ticket (today: UI or api-service).
82
+ * - `skipReason`, `skipDetail` — written by api-service when `status` transitions to SKIPPED.
83
+ */
84
+ const CWV_ISSUE_SCHEMA = Joi.object({
85
+ // Identity
86
+ id: Joi.string().optional(),
87
+
88
+ // Semantic
89
+ type: Joi.string().valid(...CWV_METRIC_TYPES).optional(),
90
+ title: Joi.string().optional(),
91
+ value: Joi.string().optional(),
92
+ cwvValue: Joi.number().optional(),
93
+
94
+ // Patch
95
+ patchContent: Joi.string().allow('').optional(),
96
+ isCodeChangeAvailable: Joi.boolean().optional(),
97
+
98
+ // Lifecycle
99
+ status: Joi.string().valid(...ISSUE_STATUSES).optional(),
100
+ skipReason: Joi.string().valid(...ISSUE_SKIP_REASONS).optional(),
101
+ skipDetail: Joi.string().max(1000).optional(),
102
+
103
+ // Linkage
104
+ jiraLink: Joi.string().uri().allow(null).optional(),
105
+ fixEntityId: Joi.string().uuid().optional(),
106
+ }).unknown(true);
107
+
29
108
  /**
30
109
  * Custom Joi validator that accepts malformed HTTP/HTTPS URLs and relative paths
31
110
  * while rejecting dangerous URI schemes (javascript:, data:, blob:, etc.).
@@ -148,22 +227,26 @@ export const DATA_SCHEMAS = {
148
227
  url: Joi.string().uri().optional(),
149
228
  pageviews: Joi.number().optional(),
150
229
  organic: Joi.number().optional(),
230
+ // RUM metrics are nullable in practice. INP requires user interaction events,
231
+ // so devices/pages without interactions report `inp: null`. The *Count fields
232
+ // similarly are null when the source RUM bundle has no sample for that metric.
233
+ // Mirror the existing `.allow(null)` already on lcp/ttfb/cls.
151
234
  metrics: Joi.array().items(
152
235
  Joi.object({
153
236
  deviceType: Joi.string().optional(),
154
- pageviews: Joi.number().optional(),
155
- clsCount: Joi.number().optional(),
156
- ttfbCount: Joi.number().optional(),
237
+ pageviews: Joi.number().allow(null).optional(),
238
+ clsCount: Joi.number().allow(null).optional(),
239
+ ttfbCount: Joi.number().allow(null).optional(),
157
240
  lcp: Joi.number().allow(null).optional(),
158
- inpCount: Joi.number().optional(),
159
- inp: Joi.number().optional(),
241
+ inpCount: Joi.number().allow(null).optional(),
242
+ inp: Joi.number().allow(null).optional(),
160
243
  ttfb: Joi.number().allow(null).optional(),
161
244
  cls: Joi.number().allow(null).optional(),
162
- lcpCount: Joi.number().optional(),
163
- organic: Joi.number().optional(),
245
+ lcpCount: Joi.number().allow(null).optional(),
246
+ organic: Joi.number().allow(null).optional(),
164
247
  }).unknown(true),
165
248
  ).required(),
166
- issues: Joi.array().items(Joi.object()).optional().default([]),
249
+ issues: Joi.array().items(CWV_ISSUE_SCHEMA).optional().default([]),
167
250
  jiraLink: Joi.string().uri().allow(null).optional(),
168
251
  aggregationKey: Joi.string().allow(null).optional(),
169
252
  }).unknown(true),