@adobe/spacecat-shared-data-access 3.63.0 → 3.65.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,15 @@
|
|
|
1
|
+
## [@adobe/spacecat-shared-data-access-v3.65.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.64.0...@adobe/spacecat-shared-data-access-v3.65.0) (2026-05-16)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* submodule support — reinstate clone, fix pull, add hasSubmodules flag ([#1550](https://github.com/adobe/spacecat-shared/issues/1550)) ([3329ff8](https://github.com/adobe/spacecat-shared/commit/3329ff82740c2311545bd7a1ab693eb92ecb8f5d))
|
|
6
|
+
|
|
7
|
+
## [@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)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* 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))
|
|
12
|
+
|
|
1
13
|
## [@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
14
|
|
|
3
15
|
### Features
|
package/package.json
CHANGED
|
@@ -34,6 +34,92 @@ export interface HlxConfig {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* One entry per submodule declared in the parent repo's `.gitmodules`.
|
|
39
|
+
* Each row carries both the importer-detected facts (declared section,
|
|
40
|
+
* original URL, external flag) and any onboarding-resolved CM URL the
|
|
41
|
+
* cm-client uses at clone/pull time to rewrite `.git/config`.
|
|
42
|
+
*
|
|
43
|
+
* Lifecycle:
|
|
44
|
+
* - Importer-written fields (`sectionName`, `gitmodulesUrl`, `external`)
|
|
45
|
+
* are refreshed on every import. Entries whose `sectionName` is no
|
|
46
|
+
* longer present in the parent's `.gitmodules` are dropped.
|
|
47
|
+
* - Onboarding-written fields (`resolvedUrl`) are preserved across
|
|
48
|
+
* imports for surviving entries — the importer cannot re-derive
|
|
49
|
+
* them (no CM Management API access from Lambda) and onboarding
|
|
50
|
+
* refreshes them out-of-band.
|
|
51
|
+
*/
|
|
52
|
+
export interface SubmoduleEntry {
|
|
53
|
+
/**
|
|
54
|
+
* The `<X>` from `[submodule "<X>"]` in `.gitmodules`. Used by
|
|
55
|
+
* cm-client as the `.git/config` key when rewriting URLs (i.e.
|
|
56
|
+
* `submodule.<sectionName>.url`). In every customer `.gitmodules`
|
|
57
|
+
* we've seen this matches the `path = …` value, but git's lookup
|
|
58
|
+
* is by section name so we capture it here.
|
|
59
|
+
*/
|
|
60
|
+
sectionName: string;
|
|
61
|
+
/**
|
|
62
|
+
* The submodule URL exactly as declared in `.gitmodules`, with
|
|
63
|
+
* basic-auth credentials stripped from https/http forms. Relative
|
|
64
|
+
* (`../foo.git`) and SSH (`git@host:path`) forms are preserved as-is.
|
|
65
|
+
*/
|
|
66
|
+
gitmodulesUrl: string;
|
|
67
|
+
/**
|
|
68
|
+
* True when this submodule's URL points to a host other than the
|
|
69
|
+
* parent repo's host. Relative URLs (`../foo.git`) and SSH URLs
|
|
70
|
+
* targeting the parent's host classify as internal (`false`).
|
|
71
|
+
*/
|
|
72
|
+
external: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* BYOG-only. Onboarding-populated URL the cm-client writes into
|
|
75
|
+
* `.git/config submodule.<sectionName>.url` at clone/pull time.
|
|
76
|
+
*
|
|
77
|
+
* The CM repo service proxies BYOG clones through URLs of the form
|
|
78
|
+
* `{CM_REPO_URL}/api/program/{programId}/repository/{numericId}.git`.
|
|
79
|
+
* When `.gitmodules` uses relative or SSH URLs, git resolves them to
|
|
80
|
+
* paths the proxy can't serve. The cm-client rewrites each submodule's
|
|
81
|
+
* `.git/config` URL to a CM-reachable form before running
|
|
82
|
+
* `git submodule update`. `.gitmodules` itself is never modified.
|
|
83
|
+
*
|
|
84
|
+
* URL form depends on the underlying repo type, decided at onboarding:
|
|
85
|
+
* - BYOG (`github`/`gitlab`/`bitbucket`/`azure_devops`):
|
|
86
|
+
* `{cmRepoUrl}/api/program/{programId}/repository/{numericId}.git`
|
|
87
|
+
* - `standard`:
|
|
88
|
+
* `https://git.cloudmanager.adobe.com/{orgName}/{repoName}/`
|
|
89
|
+
*
|
|
90
|
+
* The cm-client picks the auth scope to apply by parsing each
|
|
91
|
+
* `resolvedUrl`'s host. URLs on the CM proxy host get
|
|
92
|
+
* Bearer + x-api-key + x-gw-ims-org-id; URLs on
|
|
93
|
+
* `https://git.cloudmanager.adobe.com/{orgName}/` get Basic auth
|
|
94
|
+
* from `CM_STANDARD_REPO_CREDENTIALS[programId]` scoped to the org
|
|
95
|
+
* prefix.
|
|
96
|
+
*
|
|
97
|
+
* Absent when:
|
|
98
|
+
* - the parent is `standard` (cm-client takes the native
|
|
99
|
+
* `--recurse-submodules` path and ignores this array entirely),
|
|
100
|
+
* - this submodule is `external` to a host outside CM's reach,
|
|
101
|
+
* - onboarding hasn't run for this site yet.
|
|
102
|
+
*
|
|
103
|
+
* When absent on a BYOG parent, cm-client logs a warning and skips
|
|
104
|
+
* that submodule during `submodule update`.
|
|
105
|
+
*/
|
|
106
|
+
resolvedUrl?: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Per-submodule list captured during code import. Empty array when the
|
|
111
|
+
* cloned repo has no `.gitmodules` file (and therefore no submodules).
|
|
112
|
+
*/
|
|
113
|
+
export type SubmodulesMetadata = SubmoduleEntry[];
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Metadata extracted during code import. Consumers should assume an
|
|
117
|
+
* empty object when a field is absent.
|
|
118
|
+
*/
|
|
119
|
+
export interface CodeMetadata {
|
|
120
|
+
submodules?: SubmodulesMetadata;
|
|
121
|
+
}
|
|
122
|
+
|
|
37
123
|
export interface CodeConfig {
|
|
38
124
|
type: string;
|
|
39
125
|
owner: string;
|
|
@@ -41,7 +127,17 @@ export interface CodeConfig {
|
|
|
41
127
|
ref: string;
|
|
42
128
|
installationId?: string;
|
|
43
129
|
url: string;
|
|
130
|
+
/**
|
|
131
|
+
* S3 key (not full URL) where the imported repository ZIP is stored.
|
|
132
|
+
* Written by the code importer after successful ingestion.
|
|
133
|
+
*/
|
|
44
134
|
s3StoragePath?: string;
|
|
135
|
+
/**
|
|
136
|
+
* Metadata extracted from the cloned repository. Always overwritten
|
|
137
|
+
* on each successful import — a re-import that finds no submodules
|
|
138
|
+
* clears any submodule entries from an earlier import.
|
|
139
|
+
*/
|
|
140
|
+
metadata?: CodeMetadata;
|
|
45
141
|
}
|
|
46
142
|
|
|
47
143
|
export interface DeliveryConfig {
|
|
@@ -76,6 +76,27 @@ const schema = new SchemaBuilder(Site, SiteCollection)
|
|
|
76
76
|
validate: (value) => isNonEmptyObject(validateConfiguration(value)),
|
|
77
77
|
get: (value) => Config(value),
|
|
78
78
|
})
|
|
79
|
+
/**
|
|
80
|
+
* Repository configuration used by the code importer and downstream
|
|
81
|
+
* consumers (autofix, suggestion generation, code analysis).
|
|
82
|
+
*
|
|
83
|
+
* Fields written by the importer after a successful clone:
|
|
84
|
+
* - s3StoragePath: S3 key (not full URL) of the imported repository ZIP
|
|
85
|
+
* - metadata.submodules: per-submodule array. Each entry carries
|
|
86
|
+
* `{ sectionName, gitmodulesUrl, external }` from the parent's
|
|
87
|
+
* `.gitmodules`. Empty array when the repo has no `.gitmodules`.
|
|
88
|
+
* The importer refreshes these fields on every import; entries whose
|
|
89
|
+
* `sectionName` is no longer present are dropped.
|
|
90
|
+
*
|
|
91
|
+
* Fields populated at onboarding (not by the importer):
|
|
92
|
+
* - metadata.submodules[].resolvedUrl: BYOG-only. Pre-resolved CM URL
|
|
93
|
+
* the cm-client writes into `.git/config submodule.<sectionName>.url`
|
|
94
|
+
* at clone/pull time so submodules can fetch through the CM proxy
|
|
95
|
+
* (or `git.cloudmanager.adobe.com` for standard submodules of BYOG
|
|
96
|
+
* parents). Preserved across re-imports for surviving sectionNames.
|
|
97
|
+
*
|
|
98
|
+
* See SubmoduleEntry / CodeConfig in index.d.ts for the full TypeScript shape.
|
|
99
|
+
*/
|
|
79
100
|
.addAttribute('code', {
|
|
80
101
|
type: 'any',
|
|
81
102
|
required: false,
|
|
@@ -89,6 +110,7 @@ const schema = new SchemaBuilder(Site, SiteCollection)
|
|
|
89
110
|
installationId: { type: 'string', required: false },
|
|
90
111
|
url: { type: 'string', required: true, validate: (value) => isValidUrl(value) },
|
|
91
112
|
s3StoragePath: { type: 'string', required: false },
|
|
113
|
+
metadata: { type: 'any', required: false },
|
|
92
114
|
},
|
|
93
115
|
})
|
|
94
116
|
.addAttribute('deliveryType', {
|
|
@@ -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(
|
|
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),
|