@adobe/spacecat-shared-data-access 3.71.1 → 3.72.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 +16 -0
- package/package.json +1 -1
- package/src/models/base/entity.registry.js +3 -0
- package/src/models/base/schema.builder.js +5 -6
- package/src/models/fix-entity/fix-entity.collection.js +83 -48
- package/src/models/fix-entity/index.d.ts +2 -0
- package/src/models/index.js +1 -0
- package/src/models/preflight/index.d.ts +42 -0
- package/src/models/preflight/index.js +19 -0
- package/src/models/preflight/preflight.collection.js +45 -0
- package/src/models/preflight/preflight.model.js +40 -0
- package/src/models/preflight/preflight.schema.js +55 -0
- package/src/util/uuid.js +43 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## [@adobe/spacecat-shared-data-access-v3.72.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.71.2...@adobe/spacecat-shared-data-access-v3.72.0) (2026-05-27)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **data-access:** add Preflight entity with AsyncJob relationship (SITES-45417) ([2b57e6a](https://github.com/adobe/spacecat-shared/commit/2b57e6a1af8de07cee588409faab0ec4827a5a7d))
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* **data-access:** mint UUID v7 ids to match DB schema default ([#1633](https://github.com/adobe/spacecat-shared/issues/1633)) ([5c8f6fc](https://github.com/adobe/spacecat-shared/commit/5c8f6fcd454975703b4f670c0f560f43a768736a))
|
|
10
|
+
|
|
11
|
+
## [@adobe/spacecat-shared-data-access-v3.71.2](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.71.1...@adobe/spacecat-shared-data-access-v3.71.2) (2026-05-26)
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* (data-access) batch getAllFixesWithSuggestionsByOpportunityId (SITES-45307) ([#1619](https://github.com/adobe/spacecat-shared/issues/1619)) ([608f4cd](https://github.com/adobe/spacecat-shared/commit/608f4cda59af41c701426b8b53c29770efd42a1c))
|
|
16
|
+
|
|
1
17
|
## [@adobe/spacecat-shared-data-access-v3.71.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.71.0...@adobe/spacecat-shared-data-access-v3.71.1) (2026-05-25)
|
|
2
18
|
|
|
3
19
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -43,6 +43,7 @@ import SiteTopPageCollection from '../site-top-page/site-top-page.collection.js'
|
|
|
43
43
|
import SuggestionCollection from '../suggestion/suggestion.collection.js';
|
|
44
44
|
import SuggestionGrantCollection from '../suggestion-grant/suggestion-grant.collection.js';
|
|
45
45
|
import PageIntentCollection from '../page-intent/page-intent.collection.js';
|
|
46
|
+
import PreflightCollection from '../preflight/preflight.collection.js';
|
|
46
47
|
import ReportCollection from '../report/report.collection.js';
|
|
47
48
|
import TrialUserCollection from '../trial-user/trial-user.collection.js';
|
|
48
49
|
import TrialUserActivityCollection from '../trial-user-activity/trial-user-activity.collection.js';
|
|
@@ -83,6 +84,7 @@ import SiteTopPageSchema from '../site-top-page/site-top-page.schema.js';
|
|
|
83
84
|
import SuggestionSchema from '../suggestion/suggestion.schema.js';
|
|
84
85
|
import SuggestionGrantSchema from '../suggestion-grant/suggestion-grant.schema.js';
|
|
85
86
|
import PageIntentSchema from '../page-intent/page-intent.schema.js';
|
|
87
|
+
import PreflightSchema from '../preflight/preflight.schema.js';
|
|
86
88
|
import ReportSchema from '../report/report.schema.js';
|
|
87
89
|
import TrialUserSchema from '../trial-user/trial-user.schema.js';
|
|
88
90
|
import TrialUserActivitySchema from '../trial-user-activity/trial-user-activity.schema.js';
|
|
@@ -218,6 +220,7 @@ EntityRegistry.registerEntity(SiteTopPageSchema, SiteTopPageCollection);
|
|
|
218
220
|
EntityRegistry.registerEntity(SuggestionSchema, SuggestionCollection);
|
|
219
221
|
EntityRegistry.registerEntity(SuggestionGrantSchema, SuggestionGrantCollection);
|
|
220
222
|
EntityRegistry.registerEntity(PageIntentSchema, PageIntentCollection);
|
|
223
|
+
EntityRegistry.registerEntity(PreflightSchema, PreflightCollection);
|
|
221
224
|
EntityRegistry.registerEntity(ReportSchema, ReportCollection);
|
|
222
225
|
EntityRegistry.registerEntity(TrialUserSchema, TrialUserCollection);
|
|
223
226
|
EntityRegistry.registerEntity(TrialUserActivitySchema, TrialUserActivityCollection);
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from '@adobe/spacecat-shared-utils';
|
|
16
16
|
|
|
17
17
|
import { SchemaBuilderError } from '../../errors/index.js';
|
|
18
|
+
import { uuidv7 } from '../../util/uuid.js';
|
|
18
19
|
import {
|
|
19
20
|
decapitalize,
|
|
20
21
|
entityNameToAllPKValue,
|
|
@@ -29,17 +30,15 @@ import Schema from './schema.js';
|
|
|
29
30
|
|
|
30
31
|
const DEFAULT_SERVICE_NAME = 'SpaceCat';
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
* @type {object}
|
|
36
|
-
*/
|
|
33
|
+
// The DB column default is `uuid_generate_v7()` (mysticat-data-service migration
|
|
34
|
+
// 20250130000001_initial_setup.sql). We mint v7 client-side so the in-memory
|
|
35
|
+
// record matches what the DB would have minted — sortable, locality-friendly.
|
|
37
36
|
const ID_ATTRIBUTE_DATA = {
|
|
38
37
|
type: 'string',
|
|
39
38
|
postgrestField: 'id',
|
|
40
39
|
required: true,
|
|
41
40
|
readOnly: true,
|
|
42
|
-
default: () =>
|
|
41
|
+
default: () => uuidv7(),
|
|
43
42
|
validate: (value) => isValidUUID(value),
|
|
44
43
|
};
|
|
45
44
|
|
|
@@ -177,69 +177,104 @@ class FixEntityCollection extends BaseCollection {
|
|
|
177
177
|
|
|
178
178
|
try {
|
|
179
179
|
const fixEntitySuggestionCollection = this.entityRegistry.getCollection('FixEntitySuggestionCollection');
|
|
180
|
-
const suggestionCollection = this.entityRegistry.getCollection('SuggestionCollection');
|
|
181
180
|
|
|
182
|
-
// Query fix entity suggestions by opportunity ID and created date
|
|
183
181
|
const fixEntitySuggestions = await fixEntitySuggestionCollection
|
|
184
182
|
.allByOpportunityIdAndFixEntityCreatedDate(opportunityId, fixEntityCreatedDate);
|
|
185
183
|
|
|
186
|
-
|
|
187
|
-
|
|
184
|
+
return this.#buildFixesWithSuggestions(fixEntitySuggestions);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (error instanceof DataAccessError) {
|
|
187
|
+
throw error;
|
|
188
188
|
}
|
|
189
|
+
this.log.error('Failed to get all fixes with suggestions by created date', error);
|
|
190
|
+
throw new DataAccessError('Failed to get all fixes with suggestions by created date', this, error);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
189
193
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Gets all fixes with their suggestions for a specific opportunity.
|
|
196
|
+
* Fetches all junction records for the opportunity, then batch-loads
|
|
197
|
+
* fix entities and suggestions. Actual PostgREST call count is
|
|
198
|
+
* 1 + ceil(N/50) + ceil(M/50) due to batchGetByKeys chunking.
|
|
199
|
+
*
|
|
200
|
+
* @async
|
|
201
|
+
* @param {string} opportunityId - The ID of the opportunity.
|
|
202
|
+
* @returns {Promise<Array>} - A promise that resolves to an array of objects containing:
|
|
203
|
+
* - fixEntity: The FixEntity model
|
|
204
|
+
* - suggestions: Array of associated Suggestion models
|
|
205
|
+
* @throws {DataAccessError} - Throws an error if the query fails.
|
|
206
|
+
* @throws {ValidationError} - Throws an error if opportunityId is not provided.
|
|
207
|
+
*/
|
|
208
|
+
async getAllFixesWithSuggestionsByOpportunityId(opportunityId) {
|
|
209
|
+
guardId('opportunityId', opportunityId, 'FixEntityCollection');
|
|
193
210
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const suggestionId = fixEntitySuggestion.getSuggestionId();
|
|
211
|
+
try {
|
|
212
|
+
const fixEntitySuggestionCollection = this.entityRegistry.getCollection('FixEntitySuggestionCollection');
|
|
197
213
|
|
|
198
|
-
|
|
214
|
+
const fixEntitySuggestions = await fixEntitySuggestionCollection
|
|
215
|
+
.allByIndexKeys({ opportunityId });
|
|
199
216
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
217
|
+
return this.#buildFixesWithSuggestions(fixEntitySuggestions);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (error instanceof DataAccessError) {
|
|
220
|
+
throw error;
|
|
204
221
|
}
|
|
222
|
+
this.log.error('Failed to get all fixes with suggestions by opportunity ID', error);
|
|
223
|
+
throw new DataAccessError('Failed to get all fixes with suggestions by opportunity ID', this, error);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
205
226
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
227
|
+
async #buildFixesWithSuggestions(fixEntitySuggestions) {
|
|
228
|
+
if (fixEntitySuggestions.length === 0) {
|
|
229
|
+
return [];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const suggestionCollection = this.entityRegistry.getCollection('SuggestionCollection');
|
|
233
|
+
|
|
234
|
+
const suggestionsByFixEntityId = {};
|
|
235
|
+
const fixEntityIds = new Set();
|
|
236
|
+
|
|
237
|
+
for (const fixEntitySuggestion of fixEntitySuggestions) {
|
|
238
|
+
const fixEntityId = fixEntitySuggestion.getFixEntityId();
|
|
239
|
+
const suggestionId = fixEntitySuggestion.getSuggestionId();
|
|
240
|
+
|
|
241
|
+
fixEntityIds.add(fixEntityId);
|
|
222
242
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
for (const fixEntity of fixEntities.data) {
|
|
226
|
-
const fixEntityId = fixEntity.getId();
|
|
227
|
-
const suggestionIds = suggestionsByFixEntityId[fixEntityId] || [];
|
|
228
|
-
const suggestionsForFixEntity = suggestionIds
|
|
229
|
-
.map((id) => suggestionsById[id])
|
|
230
|
-
.filter(Boolean);
|
|
231
|
-
|
|
232
|
-
result.push({
|
|
233
|
-
fixEntity,
|
|
234
|
-
suggestions: suggestionsForFixEntity,
|
|
235
|
-
});
|
|
243
|
+
if (!suggestionsByFixEntityId[fixEntityId]) {
|
|
244
|
+
suggestionsByFixEntityId[fixEntityId] = [];
|
|
236
245
|
}
|
|
246
|
+
suggestionsByFixEntityId[fixEntityId].push(suggestionId);
|
|
247
|
+
}
|
|
237
248
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
249
|
+
const fixEntities = await this.batchGetByKeys(
|
|
250
|
+
Array.from(fixEntityIds).map((id) => ({ [this.idName]: id })),
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const allSuggestionIds = Object.values(suggestionsByFixEntityId).flat();
|
|
254
|
+
const suggestions = await suggestionCollection.batchGetByKeys(
|
|
255
|
+
allSuggestionIds.map((id) => ({ [suggestionCollection.idName]: id })),
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const suggestionsById = {};
|
|
259
|
+
for (const suggestion of suggestions.data) {
|
|
260
|
+
suggestionsById[suggestion.getId()] = suggestion;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const result = [];
|
|
264
|
+
for (const fixEntity of fixEntities.data) {
|
|
265
|
+
const fixEntityId = fixEntity.getId();
|
|
266
|
+
const suggestionIds = suggestionsByFixEntityId[fixEntityId] || [];
|
|
267
|
+
const suggestionsForFixEntity = suggestionIds
|
|
268
|
+
.map((id) => suggestionsById[id])
|
|
269
|
+
.filter(Boolean);
|
|
270
|
+
|
|
271
|
+
result.push({
|
|
272
|
+
fixEntity,
|
|
273
|
+
suggestions: suggestionsForFixEntity,
|
|
274
|
+
});
|
|
242
275
|
}
|
|
276
|
+
|
|
277
|
+
return result;
|
|
243
278
|
}
|
|
244
279
|
}
|
|
245
280
|
|
|
@@ -40,4 +40,6 @@ export interface FixEntityCollection extends BaseCollection<FixEntity> {
|
|
|
40
40
|
findByOpportunityIdAndStatus(opportunityId: string, status: string): Promise<FixEntity | null>;
|
|
41
41
|
getSuggestionsByFixEntityId(fixEntityId: string): Promise<{data: Array<Suggestion>, unprocessed: Array<string>}>;
|
|
42
42
|
setSuggestionsForFixEntity(opportunityId: string, fixEntity: FixEntity, suggestions: Array<Suggestion>): Promise<{createdItems: Array<FixEntitySuggestion>, errorItems: Array<FixEntitySuggestion>, removedCount: number}>;
|
|
43
|
+
getAllFixesWithSuggestionByCreatedAt(opportunityId: string, fixEntityCreatedDate: string): Promise<Array<{fixEntity: FixEntity, suggestions: Array<Suggestion>}>>;
|
|
44
|
+
getAllFixesWithSuggestionsByOpportunityId(opportunityId: string): Promise<Array<{fixEntity: FixEntity, suggestions: Array<Suggestion>}>>;
|
|
43
45
|
}
|
package/src/models/index.js
CHANGED
|
@@ -43,6 +43,7 @@ export * from './site/index.js';
|
|
|
43
43
|
export * from './suggestion/index.js';
|
|
44
44
|
export * from './suggestion-grant/index.js';
|
|
45
45
|
export * from './page-intent/index.js';
|
|
46
|
+
export * from './preflight/index.js';
|
|
46
47
|
export * from './report/index.js';
|
|
47
48
|
export * from './trial-user/index.js';
|
|
48
49
|
export * from './trial-user-activity/index.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { AsyncJob, BaseCollection, BaseModel, Site } from '../index.js';
|
|
14
|
+
|
|
15
|
+
export interface Preflight extends BaseModel {
|
|
16
|
+
getSiteId(): string;
|
|
17
|
+
getSite(): Promise<Site>;
|
|
18
|
+
getAsyncJobId(): string;
|
|
19
|
+
getAsyncJob(): Promise<AsyncJob>;
|
|
20
|
+
getUrl(): string;
|
|
21
|
+
getStatus(): string;
|
|
22
|
+
getCreatedBy(): { email: string; displayName?: string };
|
|
23
|
+
getStartedAt(): string | null;
|
|
24
|
+
getEndedAt(): string | null;
|
|
25
|
+
getResult(): object | null;
|
|
26
|
+
getError(): { code: string; message: string } | null;
|
|
27
|
+
|
|
28
|
+
setUrl(url: string): Preflight;
|
|
29
|
+
setStatus(status: 'IN_PROGRESS' | 'COMPLETED' | 'FAILED' | 'CANCELLED'): Preflight;
|
|
30
|
+
setCreatedBy(createdBy: { email: string; displayName?: string }): Preflight;
|
|
31
|
+
setStartedAt(startedAt: string): Preflight;
|
|
32
|
+
setEndedAt(endedAt: string): Preflight;
|
|
33
|
+
setResult(result: object): Preflight;
|
|
34
|
+
setError(error: { code: string; message: string }): Preflight;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface PreflightCollection extends BaseCollection<Preflight> {
|
|
38
|
+
allBySiteId(siteId: string): Promise<Preflight[]>;
|
|
39
|
+
findBySiteId(siteId: string): Promise<Preflight | null>;
|
|
40
|
+
findByAsyncJobId(asyncJobId: string): Promise<Preflight | null>;
|
|
41
|
+
allBySiteIdAndUrl(siteId: string, url?: string): Promise<Preflight[]>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import Preflight from './preflight.model.js';
|
|
14
|
+
import PreflightCollection from './preflight.collection.js';
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
Preflight,
|
|
18
|
+
PreflightCollection,
|
|
19
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import BaseCollection from '../base/base.collection.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* PreflightCollection - A collection class responsible for managing Preflight entities.
|
|
17
|
+
* Extends the BaseCollection to provide specific methods for interacting with Preflight records.
|
|
18
|
+
*
|
|
19
|
+
* allBySiteId(siteId) is auto-generated from the BELONGS_TO Site reference in the schema.
|
|
20
|
+
*
|
|
21
|
+
* @class PreflightCollection
|
|
22
|
+
* @extends BaseCollection
|
|
23
|
+
*/
|
|
24
|
+
class PreflightCollection extends BaseCollection {
|
|
25
|
+
static COLLECTION_NAME = 'PreflightCollection';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Returns all preflights for a site, optionally filtered by URL.
|
|
29
|
+
* URL filtering is applied in-memory; volume is bounded by AsyncJob lifecycle
|
|
30
|
+
* (preflights cascade-delete when their backing AsyncJob is removed).
|
|
31
|
+
*
|
|
32
|
+
* @param {string} siteId - The site ID to query.
|
|
33
|
+
* @param {string} [url] - Optional URL to filter by.
|
|
34
|
+
* @returns {Promise<Preflight[]>}
|
|
35
|
+
*/
|
|
36
|
+
async allBySiteIdAndUrl(siteId, url) {
|
|
37
|
+
const all = await this.allBySiteId(siteId);
|
|
38
|
+
if (!url) {
|
|
39
|
+
return all;
|
|
40
|
+
}
|
|
41
|
+
return all.filter((p) => p.getUrl() === url);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default PreflightCollection;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import BaseModel from '../base/base.model.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Preflight - A class representing a Preflight entity.
|
|
17
|
+
* Provides methods to access and manipulate Preflight-specific data.
|
|
18
|
+
*
|
|
19
|
+
* @class Preflight
|
|
20
|
+
* @extends BaseModel
|
|
21
|
+
*/
|
|
22
|
+
class Preflight extends BaseModel {
|
|
23
|
+
static ENTITY_NAME = 'Preflight';
|
|
24
|
+
|
|
25
|
+
static Status = {
|
|
26
|
+
IN_PROGRESS: 'IN_PROGRESS',
|
|
27
|
+
COMPLETED: 'COMPLETED',
|
|
28
|
+
FAILED: 'FAILED',
|
|
29
|
+
CANCELLED: 'CANCELLED',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
toJSON() {
|
|
33
|
+
const json = super.toJSON();
|
|
34
|
+
// asyncJobId is an internal FK reference — never exposed to API consumers
|
|
35
|
+
delete json.asyncJobId;
|
|
36
|
+
return json;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default Preflight;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
isObject, isValidUrl, isIsoDate,
|
|
15
|
+
} from '@adobe/spacecat-shared-utils';
|
|
16
|
+
import SchemaBuilder from '../base/schema.builder.js';
|
|
17
|
+
import Preflight from './preflight.model.js';
|
|
18
|
+
import PreflightCollection from './preflight.collection.js';
|
|
19
|
+
|
|
20
|
+
const schema = new SchemaBuilder(Preflight, PreflightCollection)
|
|
21
|
+
.addReference('belongs_to', 'Site', [], { required: true })
|
|
22
|
+
.addReference('belongs_to', 'AsyncJob', [], { required: true })
|
|
23
|
+
.addAttribute('url', {
|
|
24
|
+
type: 'string',
|
|
25
|
+
required: true,
|
|
26
|
+
validate: (value) => isValidUrl(value) && value.length <= 2048,
|
|
27
|
+
})
|
|
28
|
+
.addAttribute('status', {
|
|
29
|
+
type: Object.values(Preflight.Status),
|
|
30
|
+
required: true,
|
|
31
|
+
default: Preflight.Status.IN_PROGRESS,
|
|
32
|
+
})
|
|
33
|
+
.addAttribute('createdBy', {
|
|
34
|
+
type: 'map',
|
|
35
|
+
required: true,
|
|
36
|
+
validate: (value) => isObject(value) && typeof value.email === 'string' && value.email.length > 0,
|
|
37
|
+
})
|
|
38
|
+
.addAttribute('startedAt', {
|
|
39
|
+
type: 'string',
|
|
40
|
+
validate: (value) => !value || isIsoDate(value),
|
|
41
|
+
})
|
|
42
|
+
.addAttribute('endedAt', {
|
|
43
|
+
type: 'string',
|
|
44
|
+
validate: (value) => !value || isIsoDate(value),
|
|
45
|
+
})
|
|
46
|
+
.addAttribute('result', {
|
|
47
|
+
type: 'any',
|
|
48
|
+
validate: (value) => !value || isObject(value),
|
|
49
|
+
})
|
|
50
|
+
.addAttribute('error', {
|
|
51
|
+
type: 'map',
|
|
52
|
+
validate: (value) => !value || (isObject(value) && typeof value.code === 'string' && value.code.length > 0 && typeof value.message === 'string' && value.message.length > 0),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export default schema.build();
|
package/src/util/uuid.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { randomBytes } from 'crypto';
|
|
14
|
+
|
|
15
|
+
// Mirrors the database's `uuid_generate_v7()` plpgsql function defined in
|
|
16
|
+
// mysticat-data-service migration 20250130000001_initial_setup.sql so client-
|
|
17
|
+
// minted ids share the same time-sortable layout as DB-defaulted rows. The
|
|
18
|
+
// schema has been v7-by-design since Jan 2025; this keeps the ORM aligned.
|
|
19
|
+
/* eslint-disable no-bitwise */
|
|
20
|
+
function uuidv7() {
|
|
21
|
+
// Every byte is overwritten below (0-5 from the timestamp, 6-15 from
|
|
22
|
+
// randomBytes), so allocUnsafe avoids the zero-fill that alloc would do.
|
|
23
|
+
const bytes = Buffer.allocUnsafe(16);
|
|
24
|
+
const ms = BigInt(Date.now());
|
|
25
|
+
|
|
26
|
+
bytes[0] = Number((ms >> 40n) & 0xffn);
|
|
27
|
+
bytes[1] = Number((ms >> 32n) & 0xffn);
|
|
28
|
+
bytes[2] = Number((ms >> 24n) & 0xffn);
|
|
29
|
+
bytes[3] = Number((ms >> 16n) & 0xffn);
|
|
30
|
+
bytes[4] = Number((ms >> 8n) & 0xffn);
|
|
31
|
+
bytes[5] = Number(ms & 0xffn);
|
|
32
|
+
|
|
33
|
+
randomBytes(10).copy(bytes, 6);
|
|
34
|
+
|
|
35
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x70;
|
|
36
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
37
|
+
|
|
38
|
+
const hex = bytes.toString('hex');
|
|
39
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
40
|
+
}
|
|
41
|
+
/* eslint-enable no-bitwise */
|
|
42
|
+
|
|
43
|
+
export { uuidv7 };
|