@adobe/spacecat-shared-data-access 1.8.0 → 1.9.1
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 +14 -0
- package/package.json +3 -1
- package/src/dto/organization.js +52 -0
- package/src/dto/site.js +6 -3
- package/src/index.d.ts +74 -5
- package/src/index.js +11 -0
- package/src/models/audit.js +3 -0
- package/src/models/organization.js +100 -0
- package/src/models/site/config.js +64 -0
- package/src/models/site.js +14 -10
- package/src/service/index.js +7 -1
- package/src/service/organizations/accessPatterns.js +131 -0
- package/src/service/organizations/index.js +45 -0
- package/src/service/sites/accessPatterns.js +18 -0
- package/src/service/sites/index.js +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-data-access-v1.9.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.9.0...@adobe/spacecat-shared-data-access-v1.9.1) (2024-01-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* initialize scores as an empty object (SITES-18417) ([#87](https://github.com/adobe/spacecat-shared/issues/87)) ([eb38e32](https://github.com/adobe/spacecat-shared/commit/eb38e3298228edb1d996fa633bff4af4dcbf69db))
|
|
7
|
+
|
|
8
|
+
# [@adobe/spacecat-shared-data-access-v1.9.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.8.0...@adobe/spacecat-shared-data-access-v1.9.0) (2024-01-18)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* data model for organizations ([944be83](https://github.com/adobe/spacecat-shared/commit/944be83c9f19e3acee9bb17352b3058fe6578a69))
|
|
14
|
+
|
|
1
15
|
# [@adobe/spacecat-shared-data-access-v1.8.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.7.0...@adobe/spacecat-shared-data-access-v1.8.0) (2024-01-18)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/spacecat-shared-data-access",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "Shared modules of the Spacecat Services - Data Access",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
"@adobe/spacecat-shared-utils": "1.2.0",
|
|
34
34
|
"@aws-sdk/client-dynamodb": "3.490.0",
|
|
35
35
|
"@aws-sdk/lib-dynamodb": "3.490.0",
|
|
36
|
+
"@types/joi": "17.2.3",
|
|
37
|
+
"joi": "17.11.0",
|
|
36
38
|
"uuid": "9.0.1"
|
|
37
39
|
},
|
|
38
40
|
"devDependencies": {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 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 { Config } from '../models/site/config.js';
|
|
14
|
+
import { createOrganization } from '../models/organization.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Data transfer object for Organization.
|
|
18
|
+
*/
|
|
19
|
+
export const OrganizationDto = {
|
|
20
|
+
/**
|
|
21
|
+
* Converts an Organization object into a DynamoDB item.
|
|
22
|
+
* @param {Readonly<Organization>} organization - Organization object.
|
|
23
|
+
* @returns {{createdAt, baseURL, GSI1PK: string, id, imsOrgId, updatedAt}}
|
|
24
|
+
*/
|
|
25
|
+
toDynamoItem: (organization) => ({
|
|
26
|
+
id: organization.getId(),
|
|
27
|
+
name: organization.getName(),
|
|
28
|
+
imsOrgId: organization.getImsOrgId(),
|
|
29
|
+
createdAt: organization.getCreatedAt(),
|
|
30
|
+
updatedAt: organization.getUpdatedAt(),
|
|
31
|
+
GSI1PK: 'ALL_ORGANIZATIONS',
|
|
32
|
+
config: Config.toDynamoItem(organization.getConfig()),
|
|
33
|
+
}),
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Converts a DynamoDB item into a Organization object.
|
|
37
|
+
* @param {object } dynamoItem - DynamoDB item.
|
|
38
|
+
* @returns {Readonly<Organization>} Organization object.
|
|
39
|
+
*/
|
|
40
|
+
fromDynamoItem: (dynamoItem) => {
|
|
41
|
+
const organizationData = {
|
|
42
|
+
id: dynamoItem.id,
|
|
43
|
+
name: dynamoItem.name,
|
|
44
|
+
imsOrgId: dynamoItem.imsOrgId,
|
|
45
|
+
createdAt: dynamoItem.createdAt,
|
|
46
|
+
updatedAt: dynamoItem.updatedAt,
|
|
47
|
+
config: dynamoItem.config,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return createOrganization(organizationData);
|
|
51
|
+
},
|
|
52
|
+
};
|
package/src/dto/site.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { createSite } from '../models/site.js';
|
|
14
14
|
import AuditConfig from '../models/site/audit-config.js';
|
|
15
|
+
import { Config } from '../models/site/config.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Data transfer object for Site.
|
|
@@ -20,20 +21,21 @@ export const SiteDto = {
|
|
|
20
21
|
/**
|
|
21
22
|
* Converts a Site object into a DynamoDB item.
|
|
22
23
|
* @param {Readonly<Site>} site - Site object.
|
|
23
|
-
* @returns {{createdAt, baseURL, GSI1PK: string, id,
|
|
24
|
+
* @returns {{createdAt, baseURL, GSI1PK: string, id, organizationId, updatedAt}}
|
|
24
25
|
*/
|
|
25
26
|
toDynamoItem: (site) => ({
|
|
26
27
|
id: site.getId(),
|
|
27
28
|
baseURL: site.getBaseURL(),
|
|
28
29
|
deliveryType: site.getDeliveryType(),
|
|
29
30
|
gitHubURL: site.getGitHubURL() || '',
|
|
30
|
-
|
|
31
|
+
organizationId: site.getOrganizationId() || '',
|
|
31
32
|
isLive: site.isLive(),
|
|
32
33
|
isLiveToggledAt: site.getIsLiveToggledAt(),
|
|
33
34
|
createdAt: site.getCreatedAt(),
|
|
34
35
|
updatedAt: site.getUpdatedAt(),
|
|
35
36
|
GSI1PK: 'ALL_SITES',
|
|
36
37
|
auditConfig: AuditConfig.toDynamoItem(site.getAuditConfig()),
|
|
38
|
+
config: Config.toDynamoItem(site.getConfig()),
|
|
37
39
|
}),
|
|
38
40
|
|
|
39
41
|
/**
|
|
@@ -47,12 +49,13 @@ export const SiteDto = {
|
|
|
47
49
|
baseURL: dynamoItem.baseURL,
|
|
48
50
|
deliveryType: dynamoItem.deliveryType,
|
|
49
51
|
gitHubURL: dynamoItem.gitHubURL,
|
|
50
|
-
|
|
52
|
+
organizationId: dynamoItem.organizationId,
|
|
51
53
|
isLive: dynamoItem.isLive,
|
|
52
54
|
isLiveToggledAt: dynamoItem.isLiveToggledAt,
|
|
53
55
|
createdAt: dynamoItem.createdAt,
|
|
54
56
|
updatedAt: dynamoItem.updatedAt,
|
|
55
57
|
auditConfig: dynamoItem.auditConfig,
|
|
58
|
+
config: dynamoItem.config,
|
|
56
59
|
};
|
|
57
60
|
|
|
58
61
|
return createSite(siteData);
|
package/src/index.d.ts
CHANGED
|
@@ -96,6 +96,10 @@ export interface AuditConfigType {
|
|
|
96
96
|
disabled: () => boolean;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
export interface Config {
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
99
103
|
/**
|
|
100
104
|
* AuditConfig defines the structure for the overall audit configuration of a site.
|
|
101
105
|
*/
|
|
@@ -150,10 +154,10 @@ export interface Site {
|
|
|
150
154
|
getGitHubURL: () => string;
|
|
151
155
|
|
|
152
156
|
/**
|
|
153
|
-
* Retrieves the
|
|
154
|
-
* @returns {string} The
|
|
157
|
+
* Retrieves the Organization ID associated with the site.
|
|
158
|
+
* @returns {string} The Org ID.
|
|
155
159
|
*/
|
|
156
|
-
|
|
160
|
+
getOrganizationId: () => string;
|
|
157
161
|
|
|
158
162
|
/**
|
|
159
163
|
* Retrieves the creation timestamp of the site.
|
|
@@ -173,6 +177,12 @@ export interface Site {
|
|
|
173
177
|
*/
|
|
174
178
|
getAuditConfig: () => AuditConfig;
|
|
175
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Retrieves the current configuration for the site.
|
|
182
|
+
* @returns {Config} The current configuration.
|
|
183
|
+
*/
|
|
184
|
+
getConfig: () => Config;
|
|
185
|
+
|
|
176
186
|
/**
|
|
177
187
|
* Retrieves the audits associated with the site.
|
|
178
188
|
* @returns {Audit[]} The list of audits.
|
|
@@ -213,10 +223,48 @@ export interface Site {
|
|
|
213
223
|
|
|
214
224
|
/**
|
|
215
225
|
* Updates the IMS Org ID of the site.
|
|
216
|
-
* @param {string}
|
|
226
|
+
* @param {string} organizationId The new Org ID.
|
|
217
227
|
* @returns {Site} The updated site instance.
|
|
218
228
|
*/
|
|
219
|
-
|
|
229
|
+
updateOrganizationId: (organizationId: string) => Site;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export interface Organization {
|
|
233
|
+
/**
|
|
234
|
+
* Retrieves the ID of the site.
|
|
235
|
+
* @returns {string} The site ID.
|
|
236
|
+
*/
|
|
237
|
+
getId: () => string;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Retrieves the base URL of the site.
|
|
241
|
+
* @returns {string} The base URL.
|
|
242
|
+
*/
|
|
243
|
+
getName: () => string;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Retrieves the IMS Organization ID associated with the site.
|
|
247
|
+
* @returns {string} The IMS Org ID.
|
|
248
|
+
*/
|
|
249
|
+
getImsOrgId: () => string;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Retrieves the creation timestamp of the site.
|
|
253
|
+
* @returns {string} The creation timestamp.
|
|
254
|
+
*/
|
|
255
|
+
getCreatedAt: () => string;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Retrieves the last update timestamp of the site.
|
|
259
|
+
* @returns {string} The last update timestamp.
|
|
260
|
+
*/
|
|
261
|
+
getUpdatedAt: () => string;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Retrieves the current audit configuration for the site.
|
|
265
|
+
* @returns {Config} The current audit configuration.
|
|
266
|
+
*/
|
|
267
|
+
getConfig: () => Config;
|
|
220
268
|
}
|
|
221
269
|
|
|
222
270
|
export interface DataAccess {
|
|
@@ -257,6 +305,9 @@ export interface DataAccess {
|
|
|
257
305
|
sortAuditsAscending?: boolean,
|
|
258
306
|
deliveryType?: string,
|
|
259
307
|
) => Promise<Site[]>;
|
|
308
|
+
getSitesByOrganizationID: (
|
|
309
|
+
organizationId: string,
|
|
310
|
+
) => Promise<Organization[]>
|
|
260
311
|
getSiteByBaseURL: (
|
|
261
312
|
baseUrl: string,
|
|
262
313
|
) => Promise<Site | null>;
|
|
@@ -285,17 +336,35 @@ export interface DataAccess {
|
|
|
285
336
|
removeSite: (
|
|
286
337
|
siteId: string,
|
|
287
338
|
) => Promise<void>;
|
|
339
|
+
getOrganizations:
|
|
340
|
+
() => Promise<Organization[]>;
|
|
341
|
+
getOrganizationByID: (
|
|
342
|
+
organizationID: string,
|
|
343
|
+
) => Promise<Organization | null>;
|
|
344
|
+
addOrganization: (
|
|
345
|
+
organizationData: object,
|
|
346
|
+
) => Promise<Organization>;
|
|
347
|
+
updateOrganization: (
|
|
348
|
+
organization: Organization,
|
|
349
|
+
) => Promise<Organization>;
|
|
350
|
+
removeRemoveOrganization: (
|
|
351
|
+
organizationId: string,
|
|
352
|
+
) => Promise<void>;
|
|
288
353
|
}
|
|
289
354
|
|
|
290
355
|
interface DataAccessConfig {
|
|
291
356
|
tableNameAudits: string;
|
|
292
357
|
tableNameLatestAudits: string;
|
|
358
|
+
tableNameOrganizations: string,
|
|
293
359
|
tableNameSites: string;
|
|
294
360
|
indexNameAllSites: string;
|
|
361
|
+
indexNameAllSitesOrganizations: string,
|
|
295
362
|
indexNameAllSitesByDeliveryType: string;
|
|
296
363
|
indexNameAllLatestAuditScores: string;
|
|
364
|
+
indexNameAllOrganizations: string,
|
|
297
365
|
pkAllSites: string;
|
|
298
366
|
pkAllLatestAudits: string;
|
|
367
|
+
pkAllOrganizations: string;
|
|
299
368
|
}
|
|
300
369
|
|
|
301
370
|
export function createDataAccess(
|
package/src/index.js
CHANGED
|
@@ -15,12 +15,16 @@ import { createDataAccess } from './service/index.js';
|
|
|
15
15
|
const TABLE_NAME_AUDITS = 'spacecat-services-audits-dev';
|
|
16
16
|
const TABLE_NAME_LATEST_AUDITS = 'spacecat-services-latest-audits-dev';
|
|
17
17
|
const TABLE_NAME_SITES = 'spacecat-services-sites-dev';
|
|
18
|
+
const TABLE_NAME_ORGANIZATIONS = 'spacecat-services-organizations-dev';
|
|
18
19
|
|
|
19
20
|
const INDEX_NAME_ALL_SITES = 'spacecat-services-all-sites-dev';
|
|
21
|
+
const INDEX_NAME_ALL_ORGANIZATIONS = 'spacecat-services-all-organizations-dev';
|
|
20
22
|
const INDEX_NAME_ALL_SITES_BY_DELIVERY_TYPE = 'spacecat-services-all-sites-by-delivery-type-dev';
|
|
21
23
|
const INDEX_NAME_ALL_LATEST_AUDIT_SCORES = 'spacecat-services-all-latest-audit-scores-dev';
|
|
24
|
+
const INDEX_NAME_ALL_SITES_ORGANIZATIONS = 'spacecat-services-all-sites-organizations-dev';
|
|
22
25
|
|
|
23
26
|
const PK_ALL_SITES = 'ALL_SITES';
|
|
27
|
+
const PK_ALL_ORGANIZATIONS = 'ALL_ORGANIZATIONS';
|
|
24
28
|
const PK_ALL_LATEST_AUDITS = 'ALL_LATEST_AUDITS';
|
|
25
29
|
|
|
26
30
|
export default function dataAccessWrapper(fn) {
|
|
@@ -32,19 +36,26 @@ export default function dataAccessWrapper(fn) {
|
|
|
32
36
|
DYNAMO_TABLE_NAME_AUDITS = TABLE_NAME_AUDITS,
|
|
33
37
|
DYNAMO_TABLE_NAME_LATEST_AUDITS = TABLE_NAME_LATEST_AUDITS,
|
|
34
38
|
DYNAMO_TABLE_NAME_SITES = TABLE_NAME_SITES,
|
|
39
|
+
DYNAMO_TABLE_NAME_ORGANIZATIONS = TABLE_NAME_ORGANIZATIONS,
|
|
35
40
|
DYNAMO_INDEX_NAME_ALL_SITES = INDEX_NAME_ALL_SITES,
|
|
36
41
|
DYNAMO_INDEX_NAME_ALL_SITES_BY_DELIVERY_TYPE = INDEX_NAME_ALL_SITES_BY_DELIVERY_TYPE,
|
|
37
42
|
DYNAMO_INDEX_NAME_ALL_LATEST_AUDIT_SCORES = INDEX_NAME_ALL_LATEST_AUDIT_SCORES,
|
|
43
|
+
DYNAMO_INDEX_NAME_ALL_ORGANIZATIONS = INDEX_NAME_ALL_ORGANIZATIONS,
|
|
44
|
+
DYNAMO_INDEX_NAME_ALL_SITES_ORGANIZATIONS = INDEX_NAME_ALL_SITES_ORGANIZATIONS,
|
|
38
45
|
} = context.env;
|
|
39
46
|
|
|
40
47
|
context.dataAccess = createDataAccess({
|
|
41
48
|
tableNameAudits: DYNAMO_TABLE_NAME_AUDITS,
|
|
42
49
|
tableNameLatestAudits: DYNAMO_TABLE_NAME_LATEST_AUDITS,
|
|
50
|
+
tableNameOrganizations: DYNAMO_TABLE_NAME_ORGANIZATIONS,
|
|
43
51
|
tableNameSites: DYNAMO_TABLE_NAME_SITES,
|
|
44
52
|
indexNameAllSites: DYNAMO_INDEX_NAME_ALL_SITES,
|
|
53
|
+
indexNameOrganizations: DYNAMO_INDEX_NAME_ALL_ORGANIZATIONS,
|
|
45
54
|
indexNameAllSitesByDeliveryType: DYNAMO_INDEX_NAME_ALL_SITES_BY_DELIVERY_TYPE,
|
|
46
55
|
indexNameAllLatestAuditScores: DYNAMO_INDEX_NAME_ALL_LATEST_AUDIT_SCORES,
|
|
56
|
+
indexNameAllSitesOrganizations: DYNAMO_INDEX_NAME_ALL_SITES_ORGANIZATIONS,
|
|
47
57
|
pkAllSites: PK_ALL_SITES,
|
|
58
|
+
pkAllOrganizations: PK_ALL_ORGANIZATIONS,
|
|
48
59
|
pkAllLatestAudits: PK_ALL_LATEST_AUDITS,
|
|
49
60
|
}, log);
|
|
50
61
|
}
|
package/src/models/audit.js
CHANGED
|
@@ -107,6 +107,9 @@ export const createAudit = (data) => {
|
|
|
107
107
|
throw new Error('Audit result must be an object');
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
if (!newState.scores) {
|
|
111
|
+
newState.scores = {};
|
|
112
|
+
}
|
|
110
113
|
validateScores(data.auditResult, data.auditType);
|
|
111
114
|
|
|
112
115
|
if (data.previousAuditResult && !isObject(data.previousAuditResult)) {
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 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, isObject } from '@adobe/spacecat-shared-utils';
|
|
14
|
+
|
|
15
|
+
import { Base } from './base.js';
|
|
16
|
+
import { Config, DEFAULT_CONFIG } from './site/config.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new Organization.
|
|
20
|
+
*
|
|
21
|
+
* @param {object} data - organization data
|
|
22
|
+
* @returns {Readonly<Organization>} organization - new organization
|
|
23
|
+
*/
|
|
24
|
+
const Organization = (data = {}) => {
|
|
25
|
+
const self = Base(data);
|
|
26
|
+
|
|
27
|
+
self.getConfig = () => self.state.config;
|
|
28
|
+
self.getName = () => self.state.name;
|
|
29
|
+
self.getImsOrgId = () => self.state.imsOrgId;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Updates the IMS Org ID belonging to the organization.
|
|
33
|
+
* @param {string} imsOrgId - The IMS Org ID.
|
|
34
|
+
* @return {Base} The updated organization.
|
|
35
|
+
*/
|
|
36
|
+
self.updateImsOrgId = (imsOrgId) => {
|
|
37
|
+
if (!hasText(imsOrgId)) {
|
|
38
|
+
throw new Error('IMS Org ID must be provided');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
self.state.imsOrgId = imsOrgId;
|
|
42
|
+
self.touch();
|
|
43
|
+
|
|
44
|
+
return self;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Updates the organization name.
|
|
49
|
+
* @param {string} name - The name of the organization.
|
|
50
|
+
* @return {Base} The updated organization.
|
|
51
|
+
*/
|
|
52
|
+
self.updateName = (name) => {
|
|
53
|
+
if (!hasText(name)) {
|
|
54
|
+
throw new Error('Org name must be provided');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
self.state.name = name;
|
|
58
|
+
self.touch();
|
|
59
|
+
|
|
60
|
+
return self;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Updates the organization config.
|
|
65
|
+
* @param {string} config - The Org config.
|
|
66
|
+
* @return {Base} The updated organization.
|
|
67
|
+
*/
|
|
68
|
+
self.updateConfig = (config) => {
|
|
69
|
+
if (!isObject(config)) {
|
|
70
|
+
throw new Error('Config must be provided');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
self.state.config = Config.toDynamoItem(config);
|
|
74
|
+
self.touch();
|
|
75
|
+
|
|
76
|
+
return self;
|
|
77
|
+
};
|
|
78
|
+
return Object.freeze(self);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Creates a new Organization.
|
|
83
|
+
*
|
|
84
|
+
* @param {object} data - organization data
|
|
85
|
+
* @returns {Readonly<Organization>} organization - new organization
|
|
86
|
+
*/
|
|
87
|
+
export const createOrganization = (data) => {
|
|
88
|
+
const newState = { ...data };
|
|
89
|
+
|
|
90
|
+
if (!isObject(newState.config)) {
|
|
91
|
+
newState.config = { ...DEFAULT_CONFIG };
|
|
92
|
+
}
|
|
93
|
+
if (!hasText(newState.name)) {
|
|
94
|
+
throw new Error('Org name must be provided');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
newState.config = Config(newState.config);
|
|
98
|
+
|
|
99
|
+
return Organization(newState);
|
|
100
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 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 Joi from 'joi';
|
|
14
|
+
|
|
15
|
+
export const configSchema = Joi.object({
|
|
16
|
+
slack: Joi.object({
|
|
17
|
+
workspace: Joi.string(),
|
|
18
|
+
channel: Joi.string(),
|
|
19
|
+
}),
|
|
20
|
+
alerts: Joi.array().items(Joi.object({
|
|
21
|
+
type: Joi.string(),
|
|
22
|
+
byOrg: Joi.boolean(),
|
|
23
|
+
mentions: Joi.array().items(Joi.object({ slack: Joi.array().items(Joi.string()) })),
|
|
24
|
+
})),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const DEFAULT_CONFIG = {
|
|
28
|
+
slack: {
|
|
29
|
+
},
|
|
30
|
+
alerts: [],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Function to validate incoming configuration
|
|
34
|
+
function validateConfiguration(config) {
|
|
35
|
+
const { error, value } = configSchema.validate(config);
|
|
36
|
+
|
|
37
|
+
if (error) {
|
|
38
|
+
throw new Error(`Configuration validation error: ${error.message}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return value; // Validated and sanitized configuration
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const Config = (data = {}) => {
|
|
45
|
+
const validConfig = validateConfiguration(data);
|
|
46
|
+
const state = {
|
|
47
|
+
slack: {
|
|
48
|
+
...(validConfig?.slack?.channel ? { channel: validConfig?.slack?.channel } : {}),
|
|
49
|
+
...(validConfig?.slack?.workspace ? { workspace: validConfig?.slack?.workspace } : {}),
|
|
50
|
+
},
|
|
51
|
+
alerts: validConfig.alerts || [],
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const self = {
|
|
55
|
+
alerts: state.alerts,
|
|
56
|
+
slack: state.slack,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return Object.freeze(self);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
Config.fromDynamoItem = (dynamoItem) => Config(dynamoItem);
|
|
63
|
+
|
|
64
|
+
Config.toDynamoItem = (config) => validateConfiguration(config);
|
package/src/models/site.js
CHANGED
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { isObject, isValidUrl } from '@adobe/spacecat-shared-utils';
|
|
14
14
|
|
|
15
15
|
import { Base } from './base.js';
|
|
16
16
|
import AuditConfig from './site/audit-config.js';
|
|
17
|
+
import { Config, DEFAULT_CONFIG } from './site/config.js';
|
|
17
18
|
|
|
18
19
|
export const DELIVERY_TYPES = {
|
|
19
20
|
AEM_CS: 'aem_cs',
|
|
@@ -35,9 +36,10 @@ const Site = (data = {}) => {
|
|
|
35
36
|
self.getAuditConfig = () => self.state.auditConfig;
|
|
36
37
|
self.getAudits = () => self.state.audits;
|
|
37
38
|
self.getBaseURL = () => self.state.baseURL;
|
|
39
|
+
self.getConfig = () => self.state.config;
|
|
38
40
|
self.getDeliveryType = () => self.state.deliveryType;
|
|
39
41
|
self.getGitHubURL = () => self.state.gitHubURL;
|
|
40
|
-
self.
|
|
42
|
+
self.getOrganizationId = () => self.state.organizationId;
|
|
41
43
|
self.isLive = () => self.state.isLive;
|
|
42
44
|
self.getIsLiveToggledAt = () => self.state.isLiveToggledAt;
|
|
43
45
|
|
|
@@ -118,16 +120,12 @@ const Site = (data = {}) => {
|
|
|
118
120
|
};
|
|
119
121
|
|
|
120
122
|
/**
|
|
121
|
-
* Updates the
|
|
122
|
-
* @param {string}
|
|
123
|
+
* Updates the organizationId the site belongs to.
|
|
124
|
+
* @param {string} organizationId - The Org ID.
|
|
123
125
|
* @return {Base} The updated site.
|
|
124
126
|
*/
|
|
125
|
-
self.
|
|
126
|
-
|
|
127
|
-
throw new Error('IMS Org ID must be provided');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
self.state.imsOrgId = imsOrgId;
|
|
127
|
+
self.updateOrganizationId = (organizationId) => {
|
|
128
|
+
self.state.organizationId = organizationId;
|
|
131
129
|
self.touch();
|
|
132
130
|
|
|
133
131
|
return self;
|
|
@@ -186,5 +184,11 @@ export const createSite = (data) => {
|
|
|
186
184
|
|
|
187
185
|
newState.auditConfig = AuditConfig(newState.auditConfig);
|
|
188
186
|
|
|
187
|
+
if (!isObject(newState.config)) {
|
|
188
|
+
newState.config = { ...DEFAULT_CONFIG };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
newState.config = Config(newState.config);
|
|
192
|
+
|
|
189
193
|
return Site(newState);
|
|
190
194
|
};
|
package/src/service/index.js
CHANGED
|
@@ -13,11 +13,15 @@
|
|
|
13
13
|
import { createClient } from '@adobe/spacecat-shared-dynamo';
|
|
14
14
|
import { auditFunctions } from './audits/index.js';
|
|
15
15
|
import { siteFunctions } from './sites/index.js';
|
|
16
|
+
import { organizationFunctions } from './organizations/index.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Creates a data access object.
|
|
19
20
|
*
|
|
20
|
-
* @param {
|
|
21
|
+
* @param {{pkAllSites: string, pkAllLatestAudits: string, indexNameAllLatestAuditScores: string,
|
|
22
|
+
* tableNameAudits: string,tableNameLatestAudits: string, indexNameAllSitesOrganizations: string,
|
|
23
|
+
* tableNameSites: string, tableNameOrganizations: string, indexNameAllSites: string,
|
|
24
|
+
* indexNameAllOrganizations: string, pkAllOrganizations: string}} config configuration
|
|
21
25
|
* @param {Logger} log logger
|
|
22
26
|
* @returns {object} data access object
|
|
23
27
|
*/
|
|
@@ -26,9 +30,11 @@ export const createDataAccess = (config, log = console) => {
|
|
|
26
30
|
|
|
27
31
|
const auditFuncs = auditFunctions(dynamoClient, config, log);
|
|
28
32
|
const siteFuncs = siteFunctions(dynamoClient, config, log);
|
|
33
|
+
const organizationFuncs = organizationFunctions(dynamoClient, config, log);
|
|
29
34
|
|
|
30
35
|
return {
|
|
31
36
|
...auditFuncs,
|
|
32
37
|
...siteFuncs,
|
|
38
|
+
...organizationFuncs,
|
|
33
39
|
};
|
|
34
40
|
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 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 { isObject } from '@adobe/spacecat-shared-utils';
|
|
14
|
+
|
|
15
|
+
import { createOrganization } from '../../models/organization.js';
|
|
16
|
+
import { OrganizationDto } from '../../dto/organization.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Retrieves all organizations.
|
|
20
|
+
*
|
|
21
|
+
* @param {DynamoDbClient} dynamoClient - The DynamoDB client.
|
|
22
|
+
* @param {DataAccessConfig} config - The data access config.
|
|
23
|
+
* @returns {Promise<Readonly<Organization>[]>} A promise that resolves to an array of all
|
|
24
|
+
* organizations.
|
|
25
|
+
*/
|
|
26
|
+
export const getOrganizations = async (dynamoClient, config) => {
|
|
27
|
+
const dynamoItems = await dynamoClient.query({
|
|
28
|
+
TableName: config.tableNameOrganizations,
|
|
29
|
+
IndexName: config.indexNameAllOrganizations,
|
|
30
|
+
KeyConditionExpression: 'GSI1PK = :gsi1pk',
|
|
31
|
+
ExpressionAttributeValues: {
|
|
32
|
+
':gsi1pk': config.pkAllOrganizations,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return dynamoItems.map((dynamoItem) => OrganizationDto.fromDynamoItem(dynamoItem));
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const getOrganizationByID = async (
|
|
40
|
+
dynamoClient,
|
|
41
|
+
config,
|
|
42
|
+
organizationId,
|
|
43
|
+
) => {
|
|
44
|
+
const dynamoItem = await dynamoClient.getItem(
|
|
45
|
+
config.tableNameOrganizations,
|
|
46
|
+
{ id: organizationId },
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return isObject(dynamoItem) ? OrganizationDto.fromDynamoItem(dynamoItem) : null;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Adds an organization.
|
|
54
|
+
*
|
|
55
|
+
* @param {DynamoDbClient} dynamoClient - The DynamoDB client.
|
|
56
|
+
* @param {DataAccessConfig} config - The data access config.
|
|
57
|
+
* @param {Logger} log - The logger.
|
|
58
|
+
* @param {object} organizationData - The organization data.
|
|
59
|
+
* @returns {Promise<Readonly<Organization>>}
|
|
60
|
+
*/
|
|
61
|
+
export const addOrganization = async (
|
|
62
|
+
dynamoClient,
|
|
63
|
+
config,
|
|
64
|
+
log,
|
|
65
|
+
organizationData,
|
|
66
|
+
) => {
|
|
67
|
+
const organization = createOrganization(organizationData);
|
|
68
|
+
|
|
69
|
+
await dynamoClient.putItem(
|
|
70
|
+
config.tableNameOrganizations,
|
|
71
|
+
OrganizationDto.toDynamoItem(organization),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return organization;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Updates an organization.
|
|
79
|
+
*
|
|
80
|
+
* @param {DynamoDbClient} dynamoClient - The DynamoDB client.
|
|
81
|
+
* @param {DataAccessConfig} config - The data access config.
|
|
82
|
+
* @param {Logger} log - The logger.
|
|
83
|
+
* @param {Organization} organization - The organization.
|
|
84
|
+
* @returns {Promise<Readonly<Organization>>} - The updated organization.
|
|
85
|
+
*/
|
|
86
|
+
export const updateOrganization = async (
|
|
87
|
+
dynamoClient,
|
|
88
|
+
config,
|
|
89
|
+
log,
|
|
90
|
+
organization,
|
|
91
|
+
) => {
|
|
92
|
+
const existingOrganization = await getOrganizationByID(
|
|
93
|
+
dynamoClient,
|
|
94
|
+
config,
|
|
95
|
+
organization.getId(),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (!isObject(existingOrganization)) {
|
|
99
|
+
throw new Error('Organization not found');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await dynamoClient.putItem(
|
|
103
|
+
config.tableNameOrganizations,
|
|
104
|
+
OrganizationDto.toDynamoItem(organization),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
return organization;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Removes an organization.
|
|
112
|
+
*
|
|
113
|
+
* @param {DynamoDbClient} dynamoClient - The DynamoDB client.
|
|
114
|
+
* @param {DataAccessConfig} config - The data access config.
|
|
115
|
+
* @param {Logger} log - The logger.
|
|
116
|
+
* @param {string} organizationId - The ID of the organization to remove.
|
|
117
|
+
* @returns {Promise<void>}
|
|
118
|
+
*/
|
|
119
|
+
export const removeOrganization = async (
|
|
120
|
+
dynamoClient,
|
|
121
|
+
config,
|
|
122
|
+
log,
|
|
123
|
+
organizationId,
|
|
124
|
+
) => {
|
|
125
|
+
try {
|
|
126
|
+
await dynamoClient.removeItem(config.tableNameOrganizations, { id: organizationId });
|
|
127
|
+
} catch (error) {
|
|
128
|
+
log.error(`Error removing organization: ${error.message}`);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 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
|
+
import {
|
|
13
|
+
getOrganizations,
|
|
14
|
+
getOrganizationByID,
|
|
15
|
+
addOrganization,
|
|
16
|
+
updateOrganization,
|
|
17
|
+
removeOrganization,
|
|
18
|
+
} from './accessPatterns.js';
|
|
19
|
+
|
|
20
|
+
export const organizationFunctions = (dynamoClient, config, log) => ({
|
|
21
|
+
getOrganizations: () => getOrganizations(dynamoClient, config),
|
|
22
|
+
getOrganizationByID: (organizationId) => getOrganizationByID(
|
|
23
|
+
dynamoClient,
|
|
24
|
+
config,
|
|
25
|
+
organizationId,
|
|
26
|
+
),
|
|
27
|
+
addOrganization: (organizationData) => addOrganization(
|
|
28
|
+
dynamoClient,
|
|
29
|
+
config,
|
|
30
|
+
log,
|
|
31
|
+
organizationData,
|
|
32
|
+
),
|
|
33
|
+
updateOrganization: (organizationData) => updateOrganization(
|
|
34
|
+
dynamoClient,
|
|
35
|
+
config,
|
|
36
|
+
log,
|
|
37
|
+
organizationData,
|
|
38
|
+
),
|
|
39
|
+
removeOrganization: (organizationId) => removeOrganization(
|
|
40
|
+
dynamoClient,
|
|
41
|
+
config,
|
|
42
|
+
log,
|
|
43
|
+
organizationId,
|
|
44
|
+
),
|
|
45
|
+
});
|
|
@@ -247,6 +247,24 @@ export const getSiteByBaseURLWithLatestAudit = async (
|
|
|
247
247
|
auditType,
|
|
248
248
|
) => getSiteByBaseURLWithAuditInfo(dynamoClient, config, log, baseUrl, auditType, true);
|
|
249
249
|
|
|
250
|
+
export const getSitesByOrganizationID = async (
|
|
251
|
+
dynamoClient,
|
|
252
|
+
config,
|
|
253
|
+
organizationId,
|
|
254
|
+
) => {
|
|
255
|
+
const dynamoItems = await dynamoClient.query({
|
|
256
|
+
TableName: config.tableNameSites,
|
|
257
|
+
IndexName: config.indexNameAllSitesOrganizations,
|
|
258
|
+
KeyConditionExpression: 'organizationId = :organizationId',
|
|
259
|
+
ExpressionAttributeValues: {
|
|
260
|
+
':organizationId': organizationId,
|
|
261
|
+
},
|
|
262
|
+
Limit: 1,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
return dynamoItems.map((dynamoItem) => SiteDto.fromDynamoItem(dynamoItem));
|
|
266
|
+
};
|
|
267
|
+
|
|
250
268
|
/**
|
|
251
269
|
* Retrieves a site by its ID.
|
|
252
270
|
*
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
getSiteByBaseURLWithAudits,
|
|
18
18
|
getSiteByBaseURLWithLatestAudit,
|
|
19
19
|
getSiteByID,
|
|
20
|
-
getSites, getSitesByDeliveryType,
|
|
20
|
+
getSites, getSitesByDeliveryType, getSitesByOrganizationID,
|
|
21
21
|
getSitesToAudit,
|
|
22
22
|
getSitesWithLatestAudit, removeSite,
|
|
23
23
|
updateSite,
|
|
@@ -33,6 +33,11 @@ export const siteFunctions = (dynamoClient, config, log) => ({
|
|
|
33
33
|
config,
|
|
34
34
|
deliveryType,
|
|
35
35
|
),
|
|
36
|
+
getSitesByOrganizationID: (organizationId) => getSitesByOrganizationID(
|
|
37
|
+
dynamoClient,
|
|
38
|
+
config,
|
|
39
|
+
organizationId,
|
|
40
|
+
),
|
|
36
41
|
getSitesToAudit: () => getSitesToAudit(
|
|
37
42
|
dynamoClient,
|
|
38
43
|
config,
|