@adobe/spacecat-shared-data-access 2.102.0 → 2.103.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 +7 -0
- package/package.json +1 -1
- package/src/models/base/entity.registry.js +6 -0
- package/src/models/index.d.ts +2 -0
- package/src/models/index.js +2 -0
- package/src/models/sentiment-guideline/index.d.ts +51 -0
- package/src/models/sentiment-guideline/index.js +19 -0
- package/src/models/sentiment-guideline/sentiment-guideline.collection.js +174 -0
- package/src/models/sentiment-guideline/sentiment-guideline.model.js +90 -0
- package/src/models/sentiment-guideline/sentiment-guideline.schema.js +79 -0
- package/src/models/sentiment-topic/index.d.ts +47 -0
- package/src/models/sentiment-topic/index.js +19 -0
- package/src/models/sentiment-topic/sentiment-topic.collection.js +116 -0
- package/src/models/sentiment-topic/sentiment-topic.model.js +70 -0
- package/src/models/sentiment-topic/sentiment-topic.schema.js +76 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-data-access-v2.103.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v2.102.0...@adobe/spacecat-shared-data-access-v2.103.0) (2026-02-04)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add brand sentiment guidelines ([#1297](https://github.com/adobe/spacecat-shared/issues/1297)) ([93f0a1d](https://github.com/adobe/spacecat-shared/commit/93f0a1dada56c1a3b6c40cff32a4b7be186e82e6))
|
|
7
|
+
|
|
1
8
|
# [@adobe/spacecat-shared-data-access-v2.102.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v2.101.0...@adobe/spacecat-shared-data-access-v2.102.0) (2026-02-04)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
|
@@ -42,6 +42,8 @@ import ReportCollection from '../report/report.collection.js';
|
|
|
42
42
|
import TrialUserCollection from '../trial-user/trial-user.collection.js';
|
|
43
43
|
import TrialUserActivityCollection from '../trial-user-activity/trial-user-activity.collection.js';
|
|
44
44
|
import PageCitabilityCollection from '../page-citability/page-citability.collection.js';
|
|
45
|
+
import SentimentGuidelineCollection from '../sentiment-guideline/sentiment-guideline.collection.js';
|
|
46
|
+
import SentimentTopicCollection from '../sentiment-topic/sentiment-topic.collection.js';
|
|
45
47
|
|
|
46
48
|
import ApiKeySchema from '../api-key/api-key.schema.js';
|
|
47
49
|
import AsyncJobSchema from '../async-job/async-job.schema.js';
|
|
@@ -71,6 +73,8 @@ import ReportSchema from '../report/report.schema.js';
|
|
|
71
73
|
import TrialUserSchema from '../trial-user/trial-user.schema.js';
|
|
72
74
|
import TrialUserActivitySchema from '../trial-user-activity/trial-user-activity.schema.js';
|
|
73
75
|
import PageCitabilitySchema from '../page-citability/page-citability.schema.js';
|
|
76
|
+
import SentimentGuidelineSchema from '../sentiment-guideline/sentiment-guideline.schema.js';
|
|
77
|
+
import SentimentTopicSchema from '../sentiment-topic/sentiment-topic.schema.js';
|
|
74
78
|
|
|
75
79
|
/**
|
|
76
80
|
* EntityRegistry - A registry class responsible for managing entities, their schema and collection.
|
|
@@ -179,5 +183,7 @@ EntityRegistry.registerEntity(ReportSchema, ReportCollection);
|
|
|
179
183
|
EntityRegistry.registerEntity(TrialUserSchema, TrialUserCollection);
|
|
180
184
|
EntityRegistry.registerEntity(TrialUserActivitySchema, TrialUserActivityCollection);
|
|
181
185
|
EntityRegistry.registerEntity(PageCitabilitySchema, PageCitabilityCollection);
|
|
186
|
+
EntityRegistry.registerEntity(SentimentGuidelineSchema, SentimentGuidelineCollection);
|
|
187
|
+
EntityRegistry.registerEntity(SentimentTopicSchema, SentimentTopicCollection);
|
|
182
188
|
|
|
183
189
|
export default EntityRegistry;
|
package/src/models/index.d.ts
CHANGED
package/src/models/index.js
CHANGED
|
@@ -40,3 +40,5 @@ export * from './report/index.js';
|
|
|
40
40
|
export * from './trial-user/index.js';
|
|
41
41
|
export * from './trial-user-activity/index.js';
|
|
42
42
|
export * from './page-citability/index.js';
|
|
43
|
+
export * from './sentiment-guideline/index.js';
|
|
44
|
+
export * from './sentiment-topic/index.js';
|
|
@@ -0,0 +1,51 @@
|
|
|
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 type { BaseCollection, BaseModel, Site } from '../index';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* SentimentGuideline entity representing a guideline for sentiment analysis.
|
|
17
|
+
* Composite primary key: siteId (PK) + guidelineId (SK)
|
|
18
|
+
*/
|
|
19
|
+
export interface SentimentGuideline extends BaseModel {
|
|
20
|
+
getGuidelineId(): string;
|
|
21
|
+
getName(): string;
|
|
22
|
+
getInstruction(): string;
|
|
23
|
+
getAudits(): string[];
|
|
24
|
+
getEnabled(): boolean;
|
|
25
|
+
getCreatedAt(): string;
|
|
26
|
+
getCreatedBy(): string;
|
|
27
|
+
getUpdatedAt(): string;
|
|
28
|
+
getUpdatedBy(): string;
|
|
29
|
+
getSite(): Promise<Site>;
|
|
30
|
+
getSiteId(): string;
|
|
31
|
+
|
|
32
|
+
setName(name: string): SentimentGuideline;
|
|
33
|
+
setInstruction(instruction: string): SentimentGuideline;
|
|
34
|
+
setAudits(audits: string[]): SentimentGuideline;
|
|
35
|
+
setEnabled(enabled: boolean): SentimentGuideline;
|
|
36
|
+
setUpdatedBy(updatedBy: string): SentimentGuideline;
|
|
37
|
+
|
|
38
|
+
isEnabled(): boolean;
|
|
39
|
+
isAuditEnabled(auditType: string): boolean;
|
|
40
|
+
enableAudit(auditType: string): SentimentGuideline;
|
|
41
|
+
disableAudit(auditType: string): SentimentGuideline;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface SentimentGuidelineCollection extends BaseCollection<SentimentGuideline> {
|
|
45
|
+
findById(siteId: string, guidelineId: string): Promise<SentimentGuideline | null>;
|
|
46
|
+
allBySiteId(siteId: string, options?: { limit?: number; cursor?: string }): Promise<{ data: SentimentGuideline[]; cursor: string | null }>;
|
|
47
|
+
allBySiteIdEnabled(siteId: string, options?: { limit?: number; cursor?: string }): Promise<{ data: SentimentGuideline[]; cursor: string | null }>;
|
|
48
|
+
allBySiteIdAndAuditType(siteId: string, auditType: string, options?: { limit?: number; cursor?: string }): Promise<{ data: SentimentGuideline[]; cursor: string | null }>;
|
|
49
|
+
findByIds(siteId: string, guidelineIds: string[]): Promise<SentimentGuideline[]>;
|
|
50
|
+
removeForSiteId(siteId: string): Promise<void>;
|
|
51
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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 SentimentGuideline from './sentiment-guideline.model.js';
|
|
14
|
+
import SentimentGuidelineCollection from './sentiment-guideline.collection.js';
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
SentimentGuideline,
|
|
18
|
+
SentimentGuidelineCollection,
|
|
19
|
+
};
|
|
@@ -0,0 +1,174 @@
|
|
|
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 { hasText } from '@adobe/spacecat-shared-utils';
|
|
14
|
+
|
|
15
|
+
import BaseCollection from '../base/base.collection.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* SentimentGuidelineCollection - A collection class for managing SentimentGuideline entities.
|
|
19
|
+
* Extends BaseCollection to provide specific methods for sentiment guidelines.
|
|
20
|
+
*
|
|
21
|
+
* @class SentimentGuidelineCollection
|
|
22
|
+
* @extends BaseCollection
|
|
23
|
+
*/
|
|
24
|
+
class SentimentGuidelineCollection extends BaseCollection {
|
|
25
|
+
static COLLECTION_NAME = 'SentimentGuidelineCollection';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Finds a sentiment guideline by its composite primary key (siteId + guidelineId).
|
|
29
|
+
*
|
|
30
|
+
* @param {string} siteId - The site ID (partition key).
|
|
31
|
+
* @param {string} guidelineId - The guideline ID (sort key).
|
|
32
|
+
* @returns {Promise<SentimentGuideline|null>} The found SentimentGuideline or null.
|
|
33
|
+
*/
|
|
34
|
+
async findById(siteId, guidelineId) {
|
|
35
|
+
if (!hasText(siteId) || !hasText(guidelineId)) {
|
|
36
|
+
throw new Error('Both siteId and guidelineId are required');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return this.findByIndexKeys({ siteId, guidelineId });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Gets all sentiment guidelines for a site with pagination.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} siteId - The site ID.
|
|
46
|
+
* @param {object} [options={}] - Query options (limit, cursor).
|
|
47
|
+
* @returns {Promise<{data: SentimentGuideline[], cursor: string|null}>} Paginated results.
|
|
48
|
+
*/
|
|
49
|
+
async allBySiteId(siteId, options = {}) {
|
|
50
|
+
if (!hasText(siteId)) {
|
|
51
|
+
throw new Error('SiteId is required');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const result = await this.allByIndexKeys(
|
|
55
|
+
{ siteId },
|
|
56
|
+
{ ...options, returnCursor: true },
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
data: result.data || [],
|
|
61
|
+
cursor: result.cursor,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Gets all enabled sentiment guidelines for a site.
|
|
67
|
+
*
|
|
68
|
+
* @param {string} siteId - The site ID.
|
|
69
|
+
* @param {object} [options={}] - Query options (limit, cursor).
|
|
70
|
+
* @returns {Promise<{data: SentimentGuideline[], cursor: string|null}>} Paginated results.
|
|
71
|
+
*/
|
|
72
|
+
async allBySiteIdEnabled(siteId, options = {}) {
|
|
73
|
+
if (!hasText(siteId)) {
|
|
74
|
+
throw new Error('SiteId is required');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const result = await this.allByIndexKeys(
|
|
78
|
+
{ siteId },
|
|
79
|
+
{
|
|
80
|
+
...options,
|
|
81
|
+
returnCursor: true,
|
|
82
|
+
where: (attr, op) => op.eq(attr.enabled, true),
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
data: result.data || [],
|
|
88
|
+
cursor: result.cursor,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Gets all sentiment guidelines for a site that have a specific audit type enabled.
|
|
94
|
+
* Uses FilterExpression to filter at the database level.
|
|
95
|
+
*
|
|
96
|
+
* @param {string} siteId - The site ID.
|
|
97
|
+
* @param {string} auditType - The audit type to filter by.
|
|
98
|
+
* @param {object} [options={}] - Query options (limit, cursor).
|
|
99
|
+
* @returns {Promise<{data: SentimentGuideline[], cursor: string|null}>} Paginated results.
|
|
100
|
+
*/
|
|
101
|
+
async allBySiteIdAndAuditType(siteId, auditType, options = {}) {
|
|
102
|
+
if (!hasText(siteId) || !hasText(auditType)) {
|
|
103
|
+
throw new Error('Both siteId and auditType are required');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const result = await this.allByIndexKeys(
|
|
107
|
+
{ siteId },
|
|
108
|
+
{
|
|
109
|
+
...options,
|
|
110
|
+
returnCursor: true,
|
|
111
|
+
where: (attr, op) => op.contains(attr.audits, auditType),
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
data: result.data || [],
|
|
117
|
+
cursor: result.cursor,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Finds multiple guidelines by their IDs using batch get.
|
|
123
|
+
* Useful for resolving guidelineIds from a SentimentTopic.
|
|
124
|
+
*
|
|
125
|
+
* @param {string} siteId - The site ID.
|
|
126
|
+
* @param {string[]} guidelineIds - Array of guideline IDs to fetch.
|
|
127
|
+
* @returns {Promise<SentimentGuideline[]>} Array of found guidelines.
|
|
128
|
+
*/
|
|
129
|
+
async findByIds(siteId, guidelineIds) {
|
|
130
|
+
if (!hasText(siteId)) {
|
|
131
|
+
throw new Error('SiteId is required');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!Array.isArray(guidelineIds) || guidelineIds.length === 0) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Fetch all guidelines for the site and filter
|
|
139
|
+
// Note: For large datasets, consider implementing batch get
|
|
140
|
+
const result = await this.allBySiteId(siteId);
|
|
141
|
+
const allGuidelines = result.data || [];
|
|
142
|
+
const guidelineIdSet = new Set(guidelineIds);
|
|
143
|
+
|
|
144
|
+
return allGuidelines.filter((guideline) => {
|
|
145
|
+
const id = guideline.getGuidelineId?.() ?? guideline.guidelineId;
|
|
146
|
+
return guidelineIdSet.has(id);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Removes all sentiment guidelines for a specific site.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} siteId - The site ID.
|
|
154
|
+
* @returns {Promise<void>}
|
|
155
|
+
*/
|
|
156
|
+
async removeForSiteId(siteId) {
|
|
157
|
+
if (!hasText(siteId)) {
|
|
158
|
+
throw new Error('SiteId is required');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const result = await this.allBySiteId(siteId);
|
|
162
|
+
const guidelinesToRemove = result.data || [];
|
|
163
|
+
|
|
164
|
+
if (guidelinesToRemove.length > 0) {
|
|
165
|
+
const keysToRemove = guidelinesToRemove.map((guideline) => ({
|
|
166
|
+
siteId,
|
|
167
|
+
guidelineId: guideline.getGuidelineId?.() ?? guideline.guidelineId,
|
|
168
|
+
}));
|
|
169
|
+
await this.removeByIndexKeys(keysToRemove);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export default SentimentGuidelineCollection;
|
|
@@ -0,0 +1,90 @@
|
|
|
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 BaseModel from '../base/base.model.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* SentimentGuideline - A class representing a sentiment analysis guideline.
|
|
17
|
+
* Guidelines define how to analyze topics (e.g., "Focus on product quality").
|
|
18
|
+
*
|
|
19
|
+
* @class SentimentGuideline
|
|
20
|
+
* @extends BaseModel
|
|
21
|
+
*/
|
|
22
|
+
class SentimentGuideline extends BaseModel {
|
|
23
|
+
static ENTITY_NAME = 'SentimentGuideline';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks if this guideline is currently enabled.
|
|
27
|
+
* @returns {boolean} True if the guideline is enabled.
|
|
28
|
+
*/
|
|
29
|
+
isEnabled() {
|
|
30
|
+
return this.getEnabled?.() ?? this.enabled ?? true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Checks if this guideline is enabled for a specific audit type.
|
|
35
|
+
* @param {string} auditType - The audit type to check.
|
|
36
|
+
* @returns {boolean} True if the audit is enabled for this guideline.
|
|
37
|
+
*/
|
|
38
|
+
isAuditEnabled(auditType) {
|
|
39
|
+
const audits = this.getAudits?.() ?? this.audits ?? [];
|
|
40
|
+
return audits.includes(auditType);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Adds an audit type to the audits array if not already present.
|
|
45
|
+
* @param {string} auditType - The audit type to add.
|
|
46
|
+
* @returns {this} The current instance for chaining.
|
|
47
|
+
*/
|
|
48
|
+
enableAudit(auditType) {
|
|
49
|
+
const audits = this.getAudits?.() ?? this.audits ?? [];
|
|
50
|
+
if (!audits.includes(auditType)) {
|
|
51
|
+
const updatedAudits = [...audits, auditType];
|
|
52
|
+
if (this.setAudits) {
|
|
53
|
+
this.setAudits(updatedAudits);
|
|
54
|
+
} else {
|
|
55
|
+
this.audits = updatedAudits;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Removes an audit type from the audits array.
|
|
63
|
+
* @param {string} auditType - The audit type to remove.
|
|
64
|
+
* @returns {this} The current instance for chaining.
|
|
65
|
+
*/
|
|
66
|
+
disableAudit(auditType) {
|
|
67
|
+
const audits = this.getAudits?.() ?? this.audits ?? [];
|
|
68
|
+
const filtered = audits.filter((a) => a !== auditType);
|
|
69
|
+
if (this.setAudits) {
|
|
70
|
+
this.setAudits(filtered);
|
|
71
|
+
} else {
|
|
72
|
+
this.audits = filtered;
|
|
73
|
+
}
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Generates the composite keys for remove/update operations.
|
|
79
|
+
* Required for entities with composite primary keys.
|
|
80
|
+
* @returns {Object} - The composite keys (siteId + guidelineId).
|
|
81
|
+
*/
|
|
82
|
+
generateCompositeKeys() {
|
|
83
|
+
return {
|
|
84
|
+
siteId: this.getSiteId(),
|
|
85
|
+
guidelineId: this.getGuidelineId(),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default SentimentGuideline;
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
/* c8 ignore start */
|
|
14
|
+
|
|
15
|
+
import { hasText } from '@adobe/spacecat-shared-utils';
|
|
16
|
+
|
|
17
|
+
import SchemaBuilder from '../base/schema.builder.js';
|
|
18
|
+
import SentimentGuideline from './sentiment-guideline.model.js';
|
|
19
|
+
import SentimentGuidelineCollection from './sentiment-guideline.collection.js';
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
Schema Doc: https://electrodb.dev/en/modeling/schema/
|
|
23
|
+
Attribute Doc: https://electrodb.dev/en/modeling/attributes/
|
|
24
|
+
Indexes Doc: https://electrodb.dev/en/modeling/indexes/
|
|
25
|
+
|
|
26
|
+
Data Access Patterns:
|
|
27
|
+
1. Get all guidelines for a site: allBySiteId(siteId)
|
|
28
|
+
2. Get a specific guideline: findById(siteId, guidelineId)
|
|
29
|
+
3. Get enabled guidelines: allBySiteIdEnabled(siteId) - uses FilterExpression
|
|
30
|
+
4. Get guidelines by audit type: allBySiteIdAndAuditType(siteId, auditType) - uses FilterExpression
|
|
31
|
+
5. Get multiple guidelines by IDs: findByIds(siteId, guidelineIds)
|
|
32
|
+
|
|
33
|
+
Primary Key (Composite):
|
|
34
|
+
- Partition Key (PK): siteId
|
|
35
|
+
- Sort Key (SK): guidelineId
|
|
36
|
+
- Provides natural uniqueness for siteId + guidelineId combinations
|
|
37
|
+
|
|
38
|
+
No GSI needed - all queries are site-scoped with FilterExpression
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
const schema = new SchemaBuilder(SentimentGuideline, SentimentGuidelineCollection)
|
|
42
|
+
.withPrimaryPartitionKeys(['siteId'])
|
|
43
|
+
.withPrimarySortKeys(['guidelineId'])
|
|
44
|
+
.addReference('belongs_to', 'Site')
|
|
45
|
+
.addAttribute('guidelineId', {
|
|
46
|
+
type: 'string',
|
|
47
|
+
required: true,
|
|
48
|
+
readOnly: true,
|
|
49
|
+
default: () => crypto.randomUUID(),
|
|
50
|
+
})
|
|
51
|
+
.addAttribute('name', {
|
|
52
|
+
type: 'string',
|
|
53
|
+
required: true,
|
|
54
|
+
validate: (value) => hasText(value),
|
|
55
|
+
})
|
|
56
|
+
.addAttribute('instruction', {
|
|
57
|
+
type: 'string',
|
|
58
|
+
required: true,
|
|
59
|
+
validate: (value) => hasText(value),
|
|
60
|
+
})
|
|
61
|
+
.addAttribute('audits', {
|
|
62
|
+
type: 'set',
|
|
63
|
+
items: 'string',
|
|
64
|
+
required: true,
|
|
65
|
+
default: [],
|
|
66
|
+
})
|
|
67
|
+
.addAttribute('enabled', {
|
|
68
|
+
type: 'boolean',
|
|
69
|
+
required: true,
|
|
70
|
+
default: true,
|
|
71
|
+
})
|
|
72
|
+
.addAttribute('createdBy', {
|
|
73
|
+
type: 'string',
|
|
74
|
+
required: true,
|
|
75
|
+
readOnly: true,
|
|
76
|
+
default: 'system',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export default schema.build();
|
|
@@ -0,0 +1,47 @@
|
|
|
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 type { BaseCollection, BaseModel, Site } from '../index';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* SentimentTopic entity representing a topic for sentiment analysis.
|
|
17
|
+
* Composite primary key: siteId (PK) + topicId (SK)
|
|
18
|
+
*/
|
|
19
|
+
export interface SentimentTopic extends BaseModel {
|
|
20
|
+
getTopicId(): string;
|
|
21
|
+
getName(): string;
|
|
22
|
+
getDescription(): string | undefined;
|
|
23
|
+
getSubPrompts(): string[];
|
|
24
|
+
getEnabled(): boolean;
|
|
25
|
+
getCreatedAt(): string;
|
|
26
|
+
getCreatedBy(): string;
|
|
27
|
+
getUpdatedAt(): string;
|
|
28
|
+
getUpdatedBy(): string;
|
|
29
|
+
getSite(): Promise<Site>;
|
|
30
|
+
getSiteId(): string;
|
|
31
|
+
|
|
32
|
+
setName(name: string): SentimentTopic;
|
|
33
|
+
setDescription(description: string): SentimentTopic;
|
|
34
|
+
setSubPrompts(subPrompts: string[]): SentimentTopic;
|
|
35
|
+
setEnabled(enabled: boolean): SentimentTopic;
|
|
36
|
+
setUpdatedBy(updatedBy: string): SentimentTopic;
|
|
37
|
+
|
|
38
|
+
addSubPrompt(prompt: string): SentimentTopic;
|
|
39
|
+
removeSubPrompt(prompt: string): SentimentTopic;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface SentimentTopicCollection extends BaseCollection<SentimentTopic> {
|
|
43
|
+
findById(siteId: string, topicId: string): Promise<SentimentTopic | null>;
|
|
44
|
+
allBySiteId(siteId: string, options?: { limit?: number; cursor?: string }): Promise<{ data: SentimentTopic[]; cursor: string | null }>;
|
|
45
|
+
allBySiteIdEnabled(siteId: string, options?: { limit?: number; cursor?: string }): Promise<{ data: SentimentTopic[]; cursor: string | null }>;
|
|
46
|
+
removeForSiteId(siteId: string): Promise<void>;
|
|
47
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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 SentimentTopic from './sentiment-topic.model.js';
|
|
14
|
+
import SentimentTopicCollection from './sentiment-topic.collection.js';
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
SentimentTopic,
|
|
18
|
+
SentimentTopicCollection,
|
|
19
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
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 { hasText } from '@adobe/spacecat-shared-utils';
|
|
14
|
+
|
|
15
|
+
import BaseCollection from '../base/base.collection.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* SentimentTopicCollection - A collection class for managing SentimentTopic entities.
|
|
19
|
+
* Extends BaseCollection to provide specific methods for sentiment topics.
|
|
20
|
+
*
|
|
21
|
+
* @class SentimentTopicCollection
|
|
22
|
+
* @extends BaseCollection
|
|
23
|
+
*/
|
|
24
|
+
class SentimentTopicCollection extends BaseCollection {
|
|
25
|
+
static COLLECTION_NAME = 'SentimentTopicCollection';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Finds a sentiment topic by its composite primary key (siteId + topicId).
|
|
29
|
+
*
|
|
30
|
+
* @param {string} siteId - The site ID (partition key).
|
|
31
|
+
* @param {string} topicId - The topic ID (sort key).
|
|
32
|
+
* @returns {Promise<SentimentTopic|null>} The found SentimentTopic or null.
|
|
33
|
+
*/
|
|
34
|
+
async findById(siteId, topicId) {
|
|
35
|
+
if (!hasText(siteId) || !hasText(topicId)) {
|
|
36
|
+
throw new Error('Both siteId and topicId are required');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return this.findByIndexKeys({ siteId, topicId });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Gets all sentiment topics for a site with pagination.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} siteId - The site ID.
|
|
46
|
+
* @param {object} [options={}] - Query options (limit, cursor).
|
|
47
|
+
* @returns {Promise<{data: SentimentTopic[], cursor: string|null}>} Paginated results.
|
|
48
|
+
*/
|
|
49
|
+
async allBySiteId(siteId, options = {}) {
|
|
50
|
+
if (!hasText(siteId)) {
|
|
51
|
+
throw new Error('SiteId is required');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const result = await this.allByIndexKeys(
|
|
55
|
+
{ siteId },
|
|
56
|
+
{ ...options, returnCursor: true },
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
data: result.data || [],
|
|
61
|
+
cursor: result.cursor,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Gets all enabled sentiment topics for a site.
|
|
67
|
+
*
|
|
68
|
+
* @param {string} siteId - The site ID.
|
|
69
|
+
* @param {object} [options={}] - Query options (limit, cursor).
|
|
70
|
+
* @returns {Promise<{data: SentimentTopic[], cursor: string|null}>} Paginated results.
|
|
71
|
+
*/
|
|
72
|
+
async allBySiteIdEnabled(siteId, options = {}) {
|
|
73
|
+
if (!hasText(siteId)) {
|
|
74
|
+
throw new Error('SiteId is required');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const result = await this.allByIndexKeys(
|
|
78
|
+
{ siteId },
|
|
79
|
+
{
|
|
80
|
+
...options,
|
|
81
|
+
returnCursor: true,
|
|
82
|
+
where: (attr, op) => op.eq(attr.enabled, true),
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
data: result.data || [],
|
|
88
|
+
cursor: result.cursor,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Removes all sentiment topics for a specific site.
|
|
94
|
+
*
|
|
95
|
+
* @param {string} siteId - The site ID.
|
|
96
|
+
* @returns {Promise<void>}
|
|
97
|
+
*/
|
|
98
|
+
async removeForSiteId(siteId) {
|
|
99
|
+
if (!hasText(siteId)) {
|
|
100
|
+
throw new Error('SiteId is required');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const result = await this.allBySiteId(siteId);
|
|
104
|
+
const topicsToRemove = result.data || [];
|
|
105
|
+
|
|
106
|
+
if (topicsToRemove.length > 0) {
|
|
107
|
+
const keysToRemove = topicsToRemove.map((topic) => ({
|
|
108
|
+
siteId,
|
|
109
|
+
topicId: topic.getTopicId?.() ?? topic.topicId,
|
|
110
|
+
}));
|
|
111
|
+
await this.removeByIndexKeys(keysToRemove);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export default SentimentTopicCollection;
|
|
@@ -0,0 +1,70 @@
|
|
|
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 BaseModel from '../base/base.model.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* SentimentTopic - A class representing a sentiment analysis topic.
|
|
17
|
+
* Topics define what to analyze (e.g., "BMW XM Latest", "2026 Corvette Stingray").
|
|
18
|
+
*
|
|
19
|
+
* @class SentimentTopic
|
|
20
|
+
* @extends BaseModel
|
|
21
|
+
*/
|
|
22
|
+
class SentimentTopic extends BaseModel {
|
|
23
|
+
static ENTITY_NAME = 'SentimentTopic';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Adds a sub-prompt to the topic.
|
|
27
|
+
* @param {string} prompt - The prompt to add.
|
|
28
|
+
* @returns {this} The current instance for chaining.
|
|
29
|
+
*/
|
|
30
|
+
addSubPrompt(prompt) {
|
|
31
|
+
const subPrompts = this.getSubPrompts?.() ?? this.subPrompts ?? [];
|
|
32
|
+
const updated = [...subPrompts, prompt];
|
|
33
|
+
if (this.setSubPrompts) {
|
|
34
|
+
this.setSubPrompts(updated);
|
|
35
|
+
} else {
|
|
36
|
+
this.subPrompts = updated;
|
|
37
|
+
}
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Removes a sub-prompt from the topic.
|
|
43
|
+
* @param {string} prompt - The prompt to remove.
|
|
44
|
+
* @returns {this} The current instance for chaining.
|
|
45
|
+
*/
|
|
46
|
+
removeSubPrompt(prompt) {
|
|
47
|
+
const subPrompts = this.getSubPrompts?.() ?? this.subPrompts ?? [];
|
|
48
|
+
const filtered = subPrompts.filter((p) => p !== prompt);
|
|
49
|
+
if (this.setSubPrompts) {
|
|
50
|
+
this.setSubPrompts(filtered);
|
|
51
|
+
} else {
|
|
52
|
+
this.subPrompts = filtered;
|
|
53
|
+
}
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Generates the composite keys for remove/update operations.
|
|
59
|
+
* Required for entities with composite primary keys.
|
|
60
|
+
* @returns {Object} - The composite keys (siteId + topicId).
|
|
61
|
+
*/
|
|
62
|
+
generateCompositeKeys() {
|
|
63
|
+
return {
|
|
64
|
+
siteId: this.getSiteId(),
|
|
65
|
+
topicId: this.getTopicId(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default SentimentTopic;
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
/* c8 ignore start */
|
|
14
|
+
|
|
15
|
+
import { hasText } from '@adobe/spacecat-shared-utils';
|
|
16
|
+
|
|
17
|
+
import SchemaBuilder from '../base/schema.builder.js';
|
|
18
|
+
import SentimentTopic from './sentiment-topic.model.js';
|
|
19
|
+
import SentimentTopicCollection from './sentiment-topic.collection.js';
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
Schema Doc: https://electrodb.dev/en/modeling/schema/
|
|
23
|
+
Attribute Doc: https://electrodb.dev/en/modeling/attributes/
|
|
24
|
+
Indexes Doc: https://electrodb.dev/en/modeling/indexes/
|
|
25
|
+
|
|
26
|
+
Data Access Patterns:
|
|
27
|
+
1. Get all topics for a site: allBySiteId(siteId)
|
|
28
|
+
2. Get a specific topic: findById(siteId, topicId)
|
|
29
|
+
3. Get enabled topics: allBySiteIdEnabled(siteId) - uses FilterExpression
|
|
30
|
+
|
|
31
|
+
Primary Key (Composite):
|
|
32
|
+
- Partition Key (PK): siteId
|
|
33
|
+
- Sort Key (SK): topicId
|
|
34
|
+
- Provides natural uniqueness for siteId + topicId combinations
|
|
35
|
+
|
|
36
|
+
No GSI needed - all queries are site-scoped with FilterExpression
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
const schema = new SchemaBuilder(SentimentTopic, SentimentTopicCollection)
|
|
40
|
+
.withPrimaryPartitionKeys(['siteId'])
|
|
41
|
+
.withPrimarySortKeys(['topicId'])
|
|
42
|
+
.addReference('belongs_to', 'Site')
|
|
43
|
+
.addAttribute('topicId', {
|
|
44
|
+
type: 'string',
|
|
45
|
+
required: true,
|
|
46
|
+
readOnly: true,
|
|
47
|
+
default: () => crypto.randomUUID(),
|
|
48
|
+
})
|
|
49
|
+
.addAttribute('name', {
|
|
50
|
+
type: 'string',
|
|
51
|
+
required: true,
|
|
52
|
+
validate: (value) => hasText(value),
|
|
53
|
+
})
|
|
54
|
+
.addAttribute('description', {
|
|
55
|
+
type: 'string',
|
|
56
|
+
required: false,
|
|
57
|
+
})
|
|
58
|
+
.addAttribute('subPrompts', {
|
|
59
|
+
type: 'list',
|
|
60
|
+
items: { type: 'string' },
|
|
61
|
+
required: true,
|
|
62
|
+
default: [],
|
|
63
|
+
})
|
|
64
|
+
.addAttribute('enabled', {
|
|
65
|
+
type: 'boolean',
|
|
66
|
+
required: true,
|
|
67
|
+
default: true,
|
|
68
|
+
})
|
|
69
|
+
.addAttribute('createdBy', {
|
|
70
|
+
type: 'string',
|
|
71
|
+
required: true,
|
|
72
|
+
readOnly: true,
|
|
73
|
+
default: 'system',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export default schema.build();
|