@adobe/spacecat-shared-data-access 3.59.0 → 3.61.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 +12 -0
- package/package.json +1 -1
- package/src/models/opportunity/index.d.ts +8 -3
- package/src/models/opportunity/opportunity.collection.js +88 -1
- package/src/models/opportunity/opportunity.model.js +24 -0
- package/src/models/opportunity/opportunity.schema.js +13 -2
- package/src/models/organization/organization.model.js +9 -1
- package/src/models/organization/organization.schema.js +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [@adobe/spacecat-shared-data-access-v3.61.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.60.0...@adobe/spacecat-shared-data-access-v3.61.0) (2026-05-12)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **data-access:** add scopeType and scopeId to Opportunity model ([#1576](https://github.com/adobe/spacecat-shared/issues/1576)) ([3f0f8f2](https://github.com/adobe/spacecat-shared/commit/3f0f8f25c058c5c885fd1f553dd43e4ed07b3d67)), closes [#32-42](https://github.com/adobe/spacecat-shared/issues/32-42)
|
|
6
|
+
|
|
7
|
+
## [@adobe/spacecat-shared-data-access-v3.60.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.59.0...@adobe/spacecat-shared-data-access-v3.60.0) (2026-05-11)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **data-access:** add llmBackend field to Organization entity ([#1588](https://github.com/adobe/spacecat-shared/issues/1588)) ([ebccbe5](https://github.com/adobe/spacecat-shared/commit/ebccbe55688fb4b8fb3ffd76cc29650c2cc2fc65)), closes [mysticat-data-service#534](https://github.com/adobe/mysticat-data-service/issues/534)
|
|
12
|
+
|
|
1
13
|
## [@adobe/spacecat-shared-data-access-v3.59.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.58.0...@adobe/spacecat-shared-data-access-v3.59.0) (2026-05-07)
|
|
2
14
|
|
|
3
15
|
### Features
|
package/package.json
CHANGED
|
@@ -21,27 +21,31 @@ export interface Opportunity extends BaseModel {
|
|
|
21
21
|
getAuditId(): string;
|
|
22
22
|
getData(): object;
|
|
23
23
|
getDescription(): string;
|
|
24
|
+
getFixEntities(): Promise<FixEntity[]>;
|
|
24
25
|
getGuidance(): string;
|
|
26
|
+
getLastAuditedAt(): string;
|
|
25
27
|
getOrigin(): string;
|
|
26
28
|
getRunbook(): string;
|
|
29
|
+
getScopeId(): string | undefined;
|
|
30
|
+
getScopeType(): string | undefined;
|
|
27
31
|
getSite(): Promise<Site>;
|
|
28
32
|
getSiteId(): string;
|
|
29
33
|
getStatus(): string;
|
|
30
|
-
getFixEntities(): Promise<FixEntity[]>;
|
|
31
34
|
getSuggestions(): Promise<Suggestion[]>;
|
|
32
35
|
getSuggestionsByStatus(status: string): Promise<Suggestion[]>;
|
|
33
36
|
getSuggestionsByStatusAndRank(status: string, rank: string): Promise<Suggestion[]>;
|
|
34
|
-
getLastAuditedAt(): string;
|
|
35
37
|
getTags(): string[];
|
|
36
38
|
getTitle(): string;
|
|
37
39
|
getType(): string;
|
|
38
40
|
setAuditId(auditId: string): Opportunity;
|
|
39
41
|
setData(data: object): Opportunity;
|
|
40
42
|
setDescription(description: string): Opportunity;
|
|
41
|
-
setLastAuditedAt(lastAuditedAt: string): Opportunity;
|
|
42
43
|
setGuidance(guidance: string): Opportunity;
|
|
44
|
+
setLastAuditedAt(lastAuditedAt: string): Opportunity;
|
|
43
45
|
setOrigin(origin: string): Opportunity;
|
|
44
46
|
setRunbook(runbook: string): Opportunity;
|
|
47
|
+
setScopeId(scopeId: string | null | undefined): Opportunity;
|
|
48
|
+
setScopeType(scopeType: 'brand' | null | undefined): Opportunity;
|
|
45
49
|
setSiteId(siteId: string): Opportunity;
|
|
46
50
|
setStatus(status: string): Opportunity;
|
|
47
51
|
setTags(tags: string[]): Opportunity;
|
|
@@ -49,6 +53,7 @@ export interface Opportunity extends BaseModel {
|
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
export interface OpportunityCollection extends BaseCollection<Opportunity> {
|
|
56
|
+
allByScope(scopeType: string, scopeId: string): Promise<Opportunity[]>;
|
|
52
57
|
allByAuditId(auditId: string): Promise<Opportunity[]>;
|
|
53
58
|
allByAuditIdAndUpdatedAt(auditId: string, updatedAt: string): Promise<Opportunity[]>;
|
|
54
59
|
allBySiteId(siteId: string): Promise<Opportunity[]>;
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { hasText } from '@adobe/spacecat-shared-utils';
|
|
14
|
+
|
|
15
|
+
import { ValidationError } from '../../errors/index.js';
|
|
13
16
|
import BaseCollection from '../base/base.collection.js';
|
|
14
17
|
|
|
15
18
|
/**
|
|
@@ -22,7 +25,91 @@ import BaseCollection from '../base/base.collection.js';
|
|
|
22
25
|
class OpportunityCollection extends BaseCollection {
|
|
23
26
|
static COLLECTION_NAME = 'OpportunityCollection';
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Validates and creates a new Opportunity. Enforces that scopeType and scopeId
|
|
30
|
+
* must both be present or both be absent — a half-scoped record is invalid.
|
|
31
|
+
*
|
|
32
|
+
* @param {object} item - The opportunity data.
|
|
33
|
+
* @param {object} [options] - Optional create options (e.g. { upsert: true }).
|
|
34
|
+
* @returns {Promise<Opportunity>} The created opportunity instance.
|
|
35
|
+
*/
|
|
36
|
+
async create(item, options) {
|
|
37
|
+
const { scopeType, scopeId } = item || {};
|
|
38
|
+
if (hasText(scopeType) !== hasText(scopeId)) {
|
|
39
|
+
throw new ValidationError('scopeType and scopeId must both be set or both be absent', this);
|
|
40
|
+
}
|
|
41
|
+
return super.create(item, options);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Validates and bulk-creates Opportunities. Enforces the scopeType/scopeId
|
|
46
|
+
* co-presence invariant on every item — half-scoped records are invalid.
|
|
47
|
+
*
|
|
48
|
+
* Overrides BaseCollection.createMany() which would otherwise bypass the
|
|
49
|
+
* single-item create() guard and persist invalid scope tuples directly.
|
|
50
|
+
*
|
|
51
|
+
* @param {object[]} items - The opportunity items to create.
|
|
52
|
+
* @param {...*} rest - Additional arguments forwarded to BaseCollection.createMany.
|
|
53
|
+
* @returns {Promise<*>} The result from BaseCollection.createMany.
|
|
54
|
+
*/
|
|
55
|
+
async createMany(items, ...rest) {
|
|
56
|
+
if (Array.isArray(items)) {
|
|
57
|
+
for (const item of items) {
|
|
58
|
+
const { scopeType, scopeId } = item || {};
|
|
59
|
+
if (hasText(scopeType) !== hasText(scopeId)) {
|
|
60
|
+
throw new ValidationError('scopeType and scopeId must both be set or both be absent', this);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return super.createMany(items, ...rest);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Validates and updates an Opportunity by its keys. Enforces the scopeType/scopeId
|
|
69
|
+
* co-presence invariant: if either field appears in the update payload, both must
|
|
70
|
+
* be set or both must be absent — a half-scoped update is invalid.
|
|
71
|
+
*
|
|
72
|
+
* Note: this guard enforces co-presence but allows scope re-attribution
|
|
73
|
+
* (moving an opportunity from one scopeId to another within the same scopeType).
|
|
74
|
+
* scopeId is the tenant boundary; callers must verify authorization before mutating it.
|
|
75
|
+
*
|
|
76
|
+
* @param {object} keys - The key attributes identifying the record.
|
|
77
|
+
* @param {object} updates - The fields to update.
|
|
78
|
+
* @returns {Promise<void>}
|
|
79
|
+
*/
|
|
80
|
+
async updateByKeys(keys, updates) {
|
|
81
|
+
const hasScopeType = updates != null && 'scopeType' in updates;
|
|
82
|
+
const hasScopeId = updates != null && 'scopeId' in updates;
|
|
83
|
+
// If either field is included in the update, both must be included together,
|
|
84
|
+
// and their combined values must satisfy co-presence (both set or both absent).
|
|
85
|
+
if (hasScopeType !== hasScopeId) {
|
|
86
|
+
throw new ValidationError('scopeType and scopeId must both be set or both be absent', this);
|
|
87
|
+
}
|
|
88
|
+
if (hasScopeType && hasScopeId) {
|
|
89
|
+
const { scopeType, scopeId } = updates;
|
|
90
|
+
if (hasText(scopeType) !== hasText(scopeId)) {
|
|
91
|
+
throw new ValidationError('scopeType and scopeId must both be set or both be absent', this);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return super.updateByKeys(keys, updates);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Returns all opportunities matching a given scope type and scope ID.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} scopeType - The scope type (e.g. 'brand').
|
|
101
|
+
* @param {string} scopeId - The scope entity UUID.
|
|
102
|
+
* @returns {Promise<Opportunity[]>} The matching opportunities.
|
|
103
|
+
*/
|
|
104
|
+
async allByScope(scopeType, scopeId) {
|
|
105
|
+
if (!hasText(scopeType)) {
|
|
106
|
+
throw new Error('allByScope: scopeType is required');
|
|
107
|
+
}
|
|
108
|
+
if (!hasText(scopeId)) {
|
|
109
|
+
throw new Error('allByScope: scopeId is required');
|
|
110
|
+
}
|
|
111
|
+
return this.allByIndexKeys({ scopeType, scopeId });
|
|
112
|
+
}
|
|
26
113
|
}
|
|
27
114
|
|
|
28
115
|
export default OpportunityCollection;
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { hasText } from '@adobe/spacecat-shared-utils';
|
|
14
|
+
|
|
15
|
+
import { ValidationError } from '../../errors/index.js';
|
|
13
16
|
import BaseModel from '../base/base.model.js';
|
|
14
17
|
|
|
15
18
|
/**
|
|
@@ -37,6 +40,27 @@ class Opportunity extends BaseModel {
|
|
|
37
40
|
RESOLVED: 'RESOLVED',
|
|
38
41
|
};
|
|
39
42
|
|
|
43
|
+
static SCOPE_TYPES = {
|
|
44
|
+
BRAND: 'brand',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Overrides BaseModel.save() to enforce the co-presence invariant: scopeType and scopeId
|
|
49
|
+
* must both be set or both be absent. Guards the setter+save path in addition to create().
|
|
50
|
+
*
|
|
51
|
+
* @returns {Promise<Opportunity>} The saved opportunity.
|
|
52
|
+
* @throws {ValidationError} When only one of scopeType / scopeId is set.
|
|
53
|
+
*/
|
|
54
|
+
async save() {
|
|
55
|
+
if (hasText(this.getScopeType()) !== hasText(this.getScopeId())) {
|
|
56
|
+
throw new ValidationError(
|
|
57
|
+
'scopeType and scopeId must both be set or both be absent',
|
|
58
|
+
this,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return super.save();
|
|
62
|
+
}
|
|
63
|
+
|
|
40
64
|
/**
|
|
41
65
|
* Adds the given suggestions to this Opportunity. Sets this opportunity as the parent
|
|
42
66
|
* of each suggestion, as such the opportunity ID does not need to be provided.
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
|
|
13
13
|
/* c8 ignore start */
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
isIsoDate, isNonEmptyObject, isValidUrl, isValidUUID,
|
|
17
|
+
} from '@adobe/spacecat-shared-utils';
|
|
16
18
|
|
|
17
19
|
import SchemaBuilder from '../base/schema.builder.js';
|
|
18
20
|
import Opportunity from './opportunity.model.js';
|
|
@@ -64,6 +66,15 @@ const schema = new SchemaBuilder(Opportunity, OpportunityCollection)
|
|
|
64
66
|
.addAttribute('lastAuditedAt', {
|
|
65
67
|
type: 'string',
|
|
66
68
|
validate: (value) => !value || isIsoDate(value),
|
|
67
|
-
})
|
|
69
|
+
})
|
|
70
|
+
.addAttribute('scopeType', {
|
|
71
|
+
type: 'string',
|
|
72
|
+
validate: (value) => !value || Object.values(Opportunity.SCOPE_TYPES).includes(value),
|
|
73
|
+
})
|
|
74
|
+
.addAttribute('scopeId', {
|
|
75
|
+
type: 'string',
|
|
76
|
+
validate: (value) => !value || isValidUUID(value),
|
|
77
|
+
})
|
|
78
|
+
.addIndex({ composite: ['scopeId'] }, { composite: ['scopeType'] });
|
|
68
79
|
|
|
69
80
|
export default schema.build();
|
|
@@ -24,7 +24,15 @@ class Organization extends BaseModel {
|
|
|
24
24
|
|
|
25
25
|
static IMS_ORG_ID_REGEX = /[a-z0-9]{24}@AdobeOrg/i;
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
static LLM_BACKEND_AZURE = 'azure';
|
|
28
|
+
|
|
29
|
+
static LLM_BACKEND_BEDROCK = 'bedrock';
|
|
30
|
+
|
|
31
|
+
static LLM_BACKENDS = ['azure', 'bedrock'];
|
|
32
|
+
|
|
33
|
+
getLlmBackend() {
|
|
34
|
+
return this.record.llmBackend ?? Organization.LLM_BACKEND_AZURE;
|
|
35
|
+
}
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
export default Organization;
|
|
@@ -40,6 +40,10 @@ const schema = new SchemaBuilder(Organization, OrganizationCollection)
|
|
|
40
40
|
type: 'string',
|
|
41
41
|
validate: (value) => !value || Organization.IMS_ORG_ID_REGEX.test(value),
|
|
42
42
|
})
|
|
43
|
+
.addAttribute('llmBackend', {
|
|
44
|
+
type: Organization.LLM_BACKENDS,
|
|
45
|
+
validate: (value) => !value || Organization.LLM_BACKENDS.includes(value),
|
|
46
|
+
})
|
|
43
47
|
.addAttribute('fulfillableItems', {
|
|
44
48
|
type: 'any',
|
|
45
49
|
validate: (value) => !value || isNonEmptyObject(value),
|