@adobe/spacecat-shared-data-access 1.35.2 → 1.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ # [@adobe/spacecat-shared-data-access-v1.37.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.36.0...@adobe/spacecat-shared-data-access-v1.37.0) (2024-07-22)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **deps:** update external fixes ([#295](https://github.com/adobe/spacecat-shared/issues/295)) ([4e8d50d](https://github.com/adobe/spacecat-shared/commit/4e8d50d8d88b2d68b483fda2ead31542d15ee952))
7
+
8
+
9
+ ### Features
10
+
11
+ * new config data model ([2525af4](https://github.com/adobe/spacecat-shared/commit/2525af48d1d4dad7e1b6e1fb3fbf3cda79d1252d))
12
+ * update the config model for sites and orgs ([ed556bd](https://github.com/adobe/spacecat-shared/commit/ed556bda995d8cb6b79cb44efbee7d0b779dc0aa))
13
+ * update to trigger release ([b1610ab](https://github.com/adobe/spacecat-shared/commit/b1610ab8160a75348803b470deaa8c28d2baf6f1))
14
+
15
+ # [@adobe/spacecat-shared-data-access-v1.36.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.35.2...@adobe/spacecat-shared-data-access-v1.36.0) (2024-07-16)
16
+
17
+
18
+ ### Features
19
+
20
+ * Get all import jobs by date range ([#292](https://github.com/adobe/spacecat-shared/issues/292)) ([21accd3](https://github.com/adobe/spacecat-shared/commit/21accd34a543af3ab39aacaa0545f84c7bb50f3b))
21
+
1
22
  # [@adobe/spacecat-shared-data-access-v1.35.2](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.35.1...@adobe/spacecat-shared-data-access-v1.35.2) (2024-07-13)
2
23
 
3
24
 
package/migration.sh ADDED
@@ -0,0 +1,135 @@
1
+ #!/bin/bash
2
+
3
+ # Define AWS CLI command with local DynamoDB endpoint
4
+ AWS_CMD="aws dynamodb --endpoint-url http://localhost:8000"
5
+ REGION="us-west-2"
6
+
7
+ # Define table names
8
+ SITE_TABLE="spacecat-services-sites"
9
+ ORGANIZATION_TABLE="spacecat-services-organizations"
10
+
11
+ # Fetch all sites
12
+ SITES=$($AWS_CMD scan --table-name $SITE_TABLE)
13
+ ORGANIZATIONS=$($AWS_CMD scan --table-name $ORGANIZATION_TABLE)
14
+
15
+ # Migrate each site
16
+ echo "$SITES" | jq -c '.Items[]' | while read -r site; do
17
+ SITE_ID=$(echo $site | jq -r '.id.S')
18
+ BASE_URL=$(echo $site | jq -r '.baseURL.S')
19
+ DELIVERY_TYPE=$(echo $site | jq -r '.deliveryType.S')
20
+ GITHUB_URL=$(echo $site | jq -r '.gitHubURL.S')
21
+ ORG_ID=$(echo $site | jq -r '.organizationId.S')
22
+ IS_LIVE=$(echo $site | jq -r '.isLive.BOOL // false')
23
+ IS_LIVE_TOGGLED_AT=$(echo $site | jq -r '.isLiveToggledAt.S // empty')
24
+ GSI1PK=$(echo $site | jq -r '.GSI1PK.S')
25
+ CREATED_AT=$(echo $site | jq -r '.createdAt.S')
26
+ UPDATED_AT=$(echo $site | jq -r '.updatedAt.S')
27
+ SLACK=$(echo $site | jq -r '.config.M.slack // {"M": {}}')
28
+ IMPORTS=$(echo $site | jq -r '.config.M.imports // {"L": []}')
29
+ HLX_CONFIG=$(echo $site | jq -r '.hlxConfig // {"M": {}}')
30
+
31
+ # Check for 404 and broken-backlinks mentions
32
+ ALERTS=$(echo $site | jq -c '.config.M.alerts.L')
33
+ MENTIONS_404_SLACK='{"L":[]}'
34
+ MENTIONS_BROKEN_BACKLINKS_SLACK='{"L":[]}'
35
+ for alert in $(echo "$ALERTS" | jq -c '.[]'); do
36
+ ALERT_TYPE=$(echo $alert | jq -r '.M.type.S // empty')
37
+ if [ "$ALERT_TYPE" == "404" ]; then
38
+ MENTIONS_404_SLACK=$(echo $alert | jq -r '.M.mentions.L[0].M.slack // {"L":[]}')
39
+ elif [ "$ALERT_TYPE" == "broken-backlinks" ]; then
40
+ MENTIONS_BROKEN_BACKLINKS_SLACK=$(echo $alert | jq -r '.M.mentions.L[0].M.slack // {"L":[]}')
41
+ fi
42
+ done
43
+
44
+ # Get excluded URLs
45
+ EXCLUDED_URLS=$(echo $site | jq -c '.auditConfig.M.auditTypeConfigs.M["broken-backlinks"].M.excludedURLs // {"L" :[]} ')
46
+ MANUAL_OVERWRITES=$(echo $site | jq -c '.auditConfig.M.auditTypeConfigs.M["broken-backlinks"].M.manualOverwrites // {"L" :[]} ')
47
+ FIXED_URLS=$(echo $site | jq -c '.auditConfig.M.auditTypeConfigs.M["broken-backlinks"].M.fixedURLs // {"L" :[]} ')
48
+ MIGRATED_SITE=$(cat <<EOF
49
+ {
50
+ "id": {"S": "$SITE_ID"},
51
+ "baseURL": {"S": "$BASE_URL"},
52
+ "deliveryType": {"S": "$DELIVERY_TYPE"},
53
+ "gitHubURL": {"S": "$GITHUB_URL"},
54
+ "organizationId": {"S": "$ORG_ID"},
55
+ "isLive": {"BOOL": $IS_LIVE},
56
+ "isLiveToggledAt": {"S": "$IS_LIVE_TOGGLED_AT"},
57
+ "GSI1PK": {"S": "$GSI1PK"},
58
+ "createdAt": {"S": "$CREATED_AT"},
59
+ "updatedAt": {"S": "$UPDATED_AT"},
60
+ "hlxConfig": $HLX_CONFIG,
61
+ "config": {
62
+ "M": {
63
+ "slack": $SLACK,
64
+ "imports": $IMPORTS,
65
+ "handlers": {
66
+ "M": {
67
+ "404": {"M": {"mentions": {"M": {"slack": $MENTIONS_404_SLACK}}}},
68
+ "broken-backlinks": {"M": {"mentions": {"M": {"slack": $MENTIONS_BROKEN_BACKLINKS_SLACK}}, "excludedURLs": $EXCLUDED_URLS, "manualOverwrites": $MANUAL_OVERWRITES, "fixedURLs": $FIXED_URLS}}
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ EOF
75
+ )
76
+
77
+ # Insert migrated site data into the site table
78
+ $AWS_CMD put-item --table-name $SITE_TABLE --item "$MIGRATED_SITE"
79
+ done
80
+
81
+ # Migrate each organization
82
+ echo "$ORGANIZATIONS" | jq -c '.Items[]' | while read -r org; do
83
+ ORG_ID=$(echo $org | jq -r '.id.S')
84
+ IMS_ORG_ID=$(echo $org | jq -r '.imsOrgId.S')
85
+ NAME=$(echo $org | jq -r '.name.S')
86
+ GSI1PK=$(echo $org | jq -r '.GSI1PK.S')
87
+ CREATED_AT=$(echo $org | jq -r '.createdAt.S')
88
+ UPDATED_AT=$(echo $org | jq -r '.updatedAt.S')
89
+ SLACK=$(echo $org | jq -r '.config.M.slack // {"M": {}}')
90
+ IMPORTS=$(echo $org | jq -r '.config.M.imports // {"L": []}')
91
+
92
+ # Check for 404 and broken-backlinks mentions
93
+ ALERTS=$(echo $org | jq -c '.config.M.alerts.L')
94
+ MENTIONS_404_SLACK='{"L":[]}'
95
+ MENTIONS_BROKEN_BACKLINKS_SLACK='{"L":[]}'
96
+ for alert in $(echo "$ALERTS" | jq -c '.[]'); do
97
+ ALERT_TYPE=$(echo $alert | jq -r '.M.type.S // empty')
98
+ if [ "$ALERT_TYPE" == "404" ]; then
99
+ MENTIONS_404_SLACK=$(echo $alert | jq -r '.M.mentions.L[0].M.slack // {"L":[]}')
100
+ elif [ "$ALERT_TYPE" == "broken-backlinks" ]; then
101
+ MENTIONS_BROKEN_BACKLINKS_SLACK=$(echo $alert | jq -r '.M.mentions.L[0].M.slack // {"L":[]}')
102
+ fi
103
+ done
104
+
105
+
106
+
107
+ MIGRATED_ORG=$(cat <<EOF
108
+ {
109
+ "id": {"S": "$ORG_ID"},
110
+ "imsOrgId": {"S": "$IMS_ORG_ID"},
111
+ "name": {"S": "$NAME"},
112
+ "GSI1PK": {"S": "$GSI1PK"},
113
+ "createdAt": {"S": "$CREATED_AT"},
114
+ "updatedAt": {"S": "$UPDATED_AT"},
115
+ "config": {
116
+ "M": {
117
+ "slack": $SLACK,
118
+ "imports": $IMPORTS,
119
+ "handlers": {
120
+ "M": {
121
+ "404": {"M": {"mentions": {"M": {"slack": $MENTIONS_404_SLACK}}}},
122
+ "broken-backlinks": {"M": {"mentions": {"M": {"slack": $MENTIONS_BROKEN_BACKLINKS_SLACK}}}}
123
+ }
124
+ }
125
+ }
126
+ }
127
+ }
128
+ EOF
129
+ )
130
+
131
+ # Insert migrated organization data into the organization table
132
+ $AWS_CMD put-item --table-name $ORGANIZATION_TABLE --item "$MIGRATED_ORG"
133
+ done
134
+
135
+ echo "Migration completed successfully."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-data-access",
3
- "version": "1.35.2",
3
+ "version": "1.37.0",
4
4
  "description": "Shared modules of the Spacecat Services - Data Access",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -31,8 +31,8 @@
31
31
  "dependencies": {
32
32
  "@adobe/spacecat-shared-dynamo": "1.2.5",
33
33
  "@adobe/spacecat-shared-utils": "1.2.0",
34
- "@aws-sdk/client-dynamodb": "3.614.0",
35
- "@aws-sdk/lib-dynamodb": "3.614.0",
34
+ "@aws-sdk/client-dynamodb": "3.616.0",
35
+ "@aws-sdk/lib-dynamodb": "3.616.0",
36
36
  "@types/joi": "17.2.3",
37
37
  "joi": "17.13.3",
38
38
  "uuid": "10.0.0"
@@ -26,6 +26,7 @@ export const ConfigurationDto = {
26
26
  version: dynamoItem.version,
27
27
  queues: dynamoItem.queues,
28
28
  jobs: dynamoItem.jobs,
29
+ handlers: dynamoItem.handlers,
29
30
  };
30
31
 
31
32
  return createConfiguration(configurationData);
@@ -41,5 +42,6 @@ export const ConfigurationDto = {
41
42
  version: configuration.getVersion(),
42
43
  queues: configuration.getQueues(),
43
44
  jobs: configuration.getJobs(),
45
+ handlers: configuration.getHandlers(),
44
46
  }),
45
47
  };
package/src/dto/site.js CHANGED
@@ -11,7 +11,6 @@
11
11
  */
12
12
 
13
13
  import { createSite } from '../models/site.js';
14
- import AuditConfig from '../models/site/audit-config.js';
15
14
  import { Config } from '../models/site/config.js';
16
15
 
17
16
  /**
@@ -35,7 +34,6 @@ export const SiteDto = {
35
34
  createdAt: site.getCreatedAt(),
36
35
  updatedAt: site.getUpdatedAt(),
37
36
  GSI1PK: 'ALL_SITES',
38
- auditConfig: AuditConfig.toDynamoItem(site.getAuditConfig()),
39
37
  config: Config.toDynamoItem(site.getConfig()),
40
38
  }),
41
39
 
@@ -56,7 +54,6 @@ export const SiteDto = {
56
54
  isLiveToggledAt: dynamoItem.isLiveToggledAt,
57
55
  createdAt: dynamoItem.createdAt,
58
56
  updatedAt: dynamoItem.updatedAt,
59
- auditConfig: dynamoItem.auditConfig,
60
57
  config: dynamoItem.config,
61
58
  };
62
59
 
package/src/index.d.ts CHANGED
@@ -84,18 +84,6 @@ export interface Audit {
84
84
  getScores: () => object;
85
85
  }
86
86
 
87
- /**
88
- * AuditConfigType defines the structure for specific audit type configurations.
89
- */
90
- export interface AuditConfigType {
91
- /**
92
- * Returns true if the audit type is disabled for the site. If an audit type is disabled, no
93
- * audits of that type will be scheduled for the site.
94
- * @returns {boolean} True if the audit type is disabled for the site.
95
- */
96
- disabled: () => boolean;
97
- }
98
-
99
87
  export interface Config {
100
88
 
101
89
  }
@@ -104,31 +92,6 @@ export interface FulfillableItems {
104
92
  items: string[];
105
93
  }
106
94
 
107
- /**
108
- * AuditConfig defines the structure for the overall audit configuration of a site.
109
- */
110
- export interface AuditConfig {
111
- /**
112
- * Returns true if audits are disabled for the site. If audits are disabled, no audits will be
113
- * scheduled for the site. Overrides any audit type specific configurations.
114
- * @returns {boolean} True if audits are disabled for the site.
115
- */
116
- auditsDisabled: () => boolean;
117
-
118
- /**
119
- * Returns the audit config for a specific audit type. The audit type is the key.
120
- * @param {string} auditType The audit type to get the config for.
121
- * @returns {AuditConfigType} The audit config for the audit type.
122
- */
123
- getAuditTypeConfig: (auditType: string) => AuditConfigType;
124
-
125
- /**
126
- * Returns the audit configs for all audit types. The keys are the audit types.
127
- * @returns {object} The audit configs for all audit types.
128
- */
129
- getAuditTypeConfigs: () => object;
130
- }
131
-
132
95
  /**
133
96
  * Represents a key event.
134
97
  */
@@ -222,12 +185,6 @@ export interface Site {
222
185
  */
223
186
  getUpdatedAt: () => string;
224
187
 
225
- /**
226
- * Retrieves the current audit configuration for the site.
227
- * @returns {AuditConfig} The current audit configuration.
228
- */
229
- getAuditConfig: () => AuditConfig;
230
-
231
188
  /**
232
189
  * Retrieves the current configuration for the site.
233
190
  * @returns {Config} The current configuration.
@@ -435,6 +392,60 @@ export interface Configuration {
435
392
  */
436
393
  getJobs: () => Array<object>;
437
394
 
395
+ /**
396
+ * Retrieves the handlers configuration.
397
+ * @returns {object} The handlers configuration.
398
+ */
399
+ getHandlers: () => object;
400
+
401
+ /**
402
+ * Retrieves the handler configuration for handler type.
403
+ * @returns {object} The handler type configuration.
404
+ */
405
+ getHandler: (type) => object;
406
+
407
+ /**
408
+ * Return true if a handler type is enabled for an organization.
409
+ * @param type handler type
410
+ * @param org organization
411
+ */
412
+ isHandlerEnabledForOrg: (type: string, org: Organization) => boolean;
413
+
414
+ /**
415
+ * Return true if a handler type is enabled for a site.
416
+ * @param type handler type
417
+ * @param site site
418
+ */
419
+ isHandlerEnabledForSite: (type: string, site: Site) => boolean;
420
+
421
+ /**
422
+ * Enables a handler type for an site.
423
+ * @param type handler type
424
+ * @param site site
425
+ */
426
+ enableHandlerForSite: (type: string, site: Site) => void;
427
+
428
+ /**
429
+ * Enables a handler type for an organization.
430
+ * @param type handler type
431
+ * @param org organization
432
+ */
433
+ enableHandlerForOrg: (type: string, org: Organization) => void;
434
+
435
+ /**
436
+ * Disables a handler type for an site.
437
+ * @param type handler type
438
+ * @param site site
439
+ */
440
+ disableHandlerForSite: (type: string, site: Site) => void;
441
+
442
+ /**
443
+ * Disables a handler type for an organization.
444
+ * @param type handler type
445
+ * @param org organization
446
+ */
447
+ disableHandlerForOrg: (type:string, org: Organization) => void;
448
+
438
449
  }
439
450
 
440
451
  export interface ImportJob {
@@ -612,6 +623,10 @@ export interface DataAccess {
612
623
  removeOrganization: (
613
624
  organizationId: string,
614
625
  ) => Promise<void>;
626
+ getImportJobsByDateRange: (
627
+ startDate: string,
628
+ endDate: string,
629
+ ) => Promise<ImportJob[]>;
615
630
  getImportJobByID: (
616
631
  id: string,
617
632
  ) => Promise<ImportJob | null>;
@@ -681,6 +696,7 @@ interface DataAccessConfig {
681
696
  indexNameAllOrganizations: string,
682
697
  indexNameAllOrganizationsByImsOrgId: string,
683
698
  indexNameAllImportJobsByStatus: string,
699
+ indexNameAllImportJobsByDateRange: string,
684
700
  indexNameAllImportUrlsByJobIdAndStatus: string,
685
701
  pkAllSites: string;
686
702
  pkAllLatestAudits: string;
package/src/index.js CHANGED
@@ -33,6 +33,7 @@ const INDEX_NAME_ALL_SITES_BY_DELIVERY_TYPE = 'spacecat-services-all-sites-by-de
33
33
  const INDEX_NAME_ALL_LATEST_AUDIT_SCORES = 'spacecat-services-all-latest-audit-scores-dev';
34
34
  const INDEX_NAME_ALL_SITES_ORGANIZATIONS = 'spacecat-services-all-sites-organizations-dev';
35
35
  const INDEX_NAME_ALL_IMPORT_JOBS_BY_STATUS = 'spacecat-services-all-import-jobs-by-status-dev';
36
+ const INDEX_NAME_ALL_IMPORT_JOBS_BY_DATE_RANGE = 'spacecat-services-all-import-jobs-by-date-range-dev';
36
37
  const INDEX_NAME_ALL_IMPORT_URLS_BY_JOB_ID_AND_STATUS = 'spacecat-services-all-import-urls-by-job-id-and-status-dev';
37
38
 
38
39
  const PK_ALL_SITES = 'ALL_SITES';
@@ -66,6 +67,7 @@ export default function dataAccessWrapper(fn) {
66
67
  DYNAMO_INDEX_NAME_ALL_ORGANIZATIONS_BY_IMS_ORG_ID = INDEX_NAME_ALL_ORGANIZATIONS_BY_IMS_ORG_ID,
67
68
  DYNAMO_INDEX_NAME_ALL_SITES_ORGANIZATIONS = INDEX_NAME_ALL_SITES_ORGANIZATIONS,
68
69
  DYNAMO_INDEX_NAME_ALL_IMPORT_JOBS_BY_STATUS = INDEX_NAME_ALL_IMPORT_JOBS_BY_STATUS,
70
+ DYNAMO_INDEX_NAME_ALL_IMPORT_JOBS_BY_DATE_RANGE = INDEX_NAME_ALL_IMPORT_JOBS_BY_DATE_RANGE,
69
71
  DYNAMO_INDEX_NAME_ALL_IMPORT_URLS_BY_JOB_ID_AND_STATUS =
70
72
  INDEX_NAME_ALL_IMPORT_URLS_BY_JOB_ID_AND_STATUS,
71
73
  } = context.env;
@@ -89,6 +91,7 @@ export default function dataAccessWrapper(fn) {
89
91
  indexNameAllLatestAuditScores: DYNAMO_INDEX_NAME_ALL_LATEST_AUDIT_SCORES,
90
92
  indexNameAllSitesOrganizations: DYNAMO_INDEX_NAME_ALL_SITES_ORGANIZATIONS,
91
93
  indexNameAllImportJobsByStatus: DYNAMO_INDEX_NAME_ALL_IMPORT_JOBS_BY_STATUS,
94
+ indexNameAllImportJobsByDateRange: DYNAMO_INDEX_NAME_ALL_IMPORT_JOBS_BY_DATE_RANGE,
92
95
  indexNameImportUrlsByJobIdAndStatus: DYNAMO_INDEX_NAME_ALL_IMPORT_URLS_BY_JOB_ID_AND_STATUS,
93
96
  pkAllSites: PK_ALL_SITES,
94
97
  pkAllOrganizations: PK_ALL_ORGANIZATIONS,
@@ -13,18 +13,151 @@
13
13
  import Joi from 'joi';
14
14
 
15
15
  const Configuration = (data = {}) => {
16
- const self = { ...data };
17
- self.getJobs = () => self.jobs;
18
- self.getVersion = () => self.version;
19
- self.getQueues = () => self.queues;
16
+ const state = { ...data };
17
+ const self = { state };
18
+ self.getJobs = () => self.state.jobs;
19
+ self.getVersion = () => self.state.version;
20
+ self.getQueues = () => self.state.queues;
21
+ self.getHandlers = () => self.state.handlers;
22
+ self.getHandler = (type) => self.state.handlers[type];
23
+ self.addHandler = (type, handlerData) => {
24
+ state.handlers = state.handlers || {};
25
+ state.handlers[type] = { ...handlerData };
26
+ };
27
+ self.isHandlerEnabledForSite = (type, site) => {
28
+ const handler = state.handlers[type];
29
+ if (!handler) return false;
30
+
31
+ const siteId = site.getId();
32
+ const orgId = site.getOrganizationId();
33
+
34
+ if (handler.enabled) {
35
+ return handler.enabled.sites.includes(siteId) || handler.enabled.orgs.includes(orgId);
36
+ }
37
+
38
+ if (handler.disabled) {
39
+ return !((handler.disabled.sites && handler.disabled.sites.includes(siteId))
40
+ || (handler.disabled.orgs && handler.disabled.orgs.includes(orgId)));
41
+ }
42
+
43
+ return handler.enabledByDefault;
44
+ };
45
+
46
+ self.isHandlerEnabledForOrg = (type, org) => {
47
+ const handler = state.handlers[type];
48
+ if (!handler) return false;
49
+
50
+ const orgId = org.getId();
51
+
52
+ if (handler.enabled) {
53
+ return handler.enabled.orgs.includes(orgId);
54
+ }
55
+
56
+ if (handler.disabled) {
57
+ return !handler.disabled.orgs.includes(orgId);
58
+ }
59
+
60
+ return handler.enabledByDefault;
61
+ };
62
+
63
+ const updateHandlerOrgs = (type, orgId, enabled) => {
64
+ const handler = state.handlers[type];
65
+ if (!handler) return;
66
+
67
+ if (enabled) {
68
+ if (handler.enabledByDefault) {
69
+ handler.disabled.orgs = handler.disabled.orgs?.filter((id) => id !== orgId) || [];
70
+ } else {
71
+ handler.enabled = handler.enabled || { orgs: [] };
72
+ handler.enabled.orgs = [...(handler.enabled?.orgs || []), orgId];
73
+ }
74
+ } else if (handler.enabledByDefault) {
75
+ handler.disabled = handler.disabled || { orgs: [] };
76
+ handler.disabled.orgs = [...(handler.disabled?.orgs || []), orgId];
77
+ } else {
78
+ handler.enabled.orgs = handler.enabled.orgs?.filter((id) => id !== orgId) || [];
79
+ }
80
+ };
81
+
82
+ const updateHandlerSites = (type, siteId, enabled) => {
83
+ const handler = state.handlers[type];
84
+ if (!handler) return;
85
+
86
+ if (enabled) {
87
+ if (handler.enabledByDefault) {
88
+ handler.disabled.sites = handler.disabled.sites?.filter((id) => id !== siteId) || [];
89
+ } else {
90
+ handler.enabled = handler.enabled || { sites: [] };
91
+ handler.enabled.sites = [...(handler.enabled.sites || []), siteId];
92
+ }
93
+ } else if (handler.enabledByDefault) {
94
+ handler.disabled = handler.disabled || { sites: [] };
95
+ handler.disabled.sites = [...(handler.disabled.sites || []), siteId];
96
+ } else {
97
+ handler.enabled.sites = handler.enabled.sites?.filter((id) => id !== siteId) || [];
98
+ }
99
+ };
100
+
101
+ self.enableHandlerForSite = (type, site) => {
102
+ const siteId = site.getId();
103
+ if (self.isHandlerEnabledForSite(type, site)) return;
104
+
105
+ updateHandlerSites(type, siteId, true);
106
+ };
107
+
108
+ self.enableHandlerForOrg = (type, org) => {
109
+ const orgId = org.getId();
110
+ if (self.isHandlerEnabledForOrg(type, org)) return;
111
+
112
+ updateHandlerOrgs(type, orgId, true);
113
+ };
114
+
115
+ self.disableHandlerForSite = (type, site) => {
116
+ const siteId = site.getId();
117
+ if (!self.isHandlerEnabledForSite(type, site)) return;
118
+
119
+ updateHandlerSites(type, siteId, false);
120
+ };
121
+
122
+ self.disableHandlerForOrg = (type, org) => {
123
+ const orgId = org.getId();
124
+ if (!self.isHandlerEnabledForOrg(type, org)) return;
125
+
126
+ updateHandlerOrgs(type, orgId, false);
127
+ };
20
128
 
21
129
  return Object.freeze(self);
22
130
  };
23
131
 
132
+ /**
133
+ *
134
+ * @param configuration
135
+ * @returns {any}
136
+ */
137
+
24
138
  export const checkConfiguration = (configuration) => {
25
139
  const schema = Joi.object({
26
140
  version: Joi.string().required(),
27
141
  queues: Joi.object().required(),
142
+ handlers: Joi.object().pattern(Joi.string(), Joi.object(
143
+ {
144
+ enabled: Joi.object({
145
+ sites: Joi.array().items(Joi.string()),
146
+ orgs: Joi.array().items(Joi.string()),
147
+ }),
148
+ disabled: Joi.object({
149
+ sites: Joi.array().items(Joi.string()),
150
+ orgs: Joi.array().items(Joi.string()),
151
+ }),
152
+ enabledByDefault: Joi.boolean().required(),
153
+ dependencies: Joi.array().items(Joi.object(
154
+ {
155
+ handler: Joi.string(),
156
+ actions: Joi.array().items(Joi.string()),
157
+ },
158
+ )),
159
+ },
160
+ )),
28
161
  jobs: Joi.array().required(),
29
162
  }).unknown(true);
30
163
  const { error, value } = schema.validate(configuration);
@@ -26,24 +26,11 @@ export const DEFAULT_ORGANIZATION_ID = 'default';
26
26
  const Organization = (data = {}) => {
27
27
  const self = Base(data);
28
28
 
29
- self.getAuditConfig = () => self.state.config.audits;
30
29
  self.getConfig = () => self.state.config;
31
30
  self.getName = () => self.state.name;
32
31
  self.getImsOrgId = () => self.state.imsOrgId;
33
32
  self.getFulfillableItems = () => self.state.fulfillableItems;
34
33
 
35
- self.setAllAuditsDisabled = (disabled) => {
36
- self.state.config.audits.updateAuditsDisabled(disabled);
37
- self.touch();
38
- return self;
39
- };
40
-
41
- self.updateAuditTypeConfig = (type, config) => {
42
- self.state.config.audits.updateAuditTypeConfig(type, config);
43
- self.touch();
44
- return self;
45
- };
46
-
47
34
  /**
48
35
  * Updates the IMS Org ID belonging to the organization.
49
36
  * @param {string} imsOrgId - The IMS Org ID.
@@ -11,7 +11,6 @@
11
11
  */
12
12
 
13
13
  import Joi from 'joi';
14
- import AuditConfig from './audit-config.js';
15
14
 
16
15
  export const configSchema = Joi.object({
17
16
  slack: Joi.object({
@@ -19,35 +18,25 @@ export const configSchema = Joi.object({
19
18
  channel: Joi.string(),
20
19
  invitedUserCount: Joi.number().integer().min(0),
21
20
  }),
22
- alerts: Joi.array().items(Joi.object({
23
- type: Joi.string().required(),
24
- byOrg: Joi.boolean(),
25
- mentions: Joi.array().items(Joi.object({ slack: Joi.array().items(Joi.string()) })),
26
- }).unknown(true)),
27
- audits: Joi.object({
28
- auditsDisabled: Joi.boolean().optional(),
29
- auditTypeConfigs: Joi.object().pattern(
30
- Joi.string(),
31
- Joi.object({
32
- disabled: Joi.boolean().optional(),
33
- excludedURLs: Joi.array().items(Joi.string()).optional(),
34
- manualOverwrites: Joi.array().items(Joi.object({
35
- brokenTargetURL: Joi.string().optional(),
36
- targetURL: Joi.string().optional(),
37
- })).optional(),
38
- fixedURLs: Joi.array().items(Joi.object({
39
- brokenTargetURL: Joi.string().optional(),
40
- targetURL: Joi.string().optional(),
41
- })).optional(),
42
- }).unknown(true),
43
- ).unknown(true),
44
- }).unknown(true),
21
+ imports: Joi.array().items(Joi.object({ type: Joi.string() }).unknown(true)),
22
+ handlers: Joi.object().pattern(Joi.string(), Joi.object({
23
+ mentions: Joi.object().pattern(Joi.string(), Joi.array().items(Joi.string())),
24
+ excludedURLs: Joi.array().items(Joi.string()),
25
+ manualOverwrites: Joi.array().items(Joi.object({
26
+ brokenTargetURL: Joi.string().optional(),
27
+ targetURL: Joi.string().optional(),
28
+ })).optional(),
29
+ fixedURLs: Joi.array().items(Joi.object({
30
+ brokenTargetURL: Joi.string().optional(),
31
+ targetURL: Joi.string().optional(),
32
+ })).optional(),
33
+ }).unknown(true)).unknown(true),
45
34
  }).unknown(true);
46
35
 
47
36
  export const DEFAULT_CONFIG = {
48
37
  slack: {},
49
- alerts: [],
50
- audits: {},
38
+ handlers: {
39
+ },
51
40
  };
52
41
 
53
42
  // Function to validate incoming configuration
@@ -63,11 +52,50 @@ function validateConfiguration(config) {
63
52
 
64
53
  export const Config = (data = {}) => {
65
54
  const validConfig = validateConfiguration(data);
66
- validConfig.audits = AuditConfig(validConfig.audits);
67
55
 
68
56
  const state = { ...validConfig };
57
+ const self = { state };
58
+ self.getSlackConfig = () => state.slack;
59
+ self.getSlackMentions = (type) => state?.handlers[type]?.mentions?.slack;
60
+ self.getHandlerConfig = (type) => state?.handlers[type];
61
+ self.getHandlers = () => state.handlers;
62
+ self.getImports = () => state.imports;
63
+ self.getExcludedURLs = (type) => state?.handlers[type]?.excludedURLs;
64
+ self.getManualOverrides = (type) => state?.handlers[type]?.manualOverwrites;
65
+ self.getFixedURLs = (type) => state?.handlers[type]?.fixedURLs;
69
66
 
70
- const self = { ...state };
67
+ self.updateSlackConfig = (channel, workspace, invitedUserCount) => {
68
+ state.slack = {
69
+ channel,
70
+ workspace,
71
+ invitedUserCount,
72
+ };
73
+ };
74
+
75
+ self.updateSlackMentions = (type, mentions) => {
76
+ state.handlers = state.handlers || {};
77
+ state.handlers[type] = state.handlers[type] || {};
78
+ state.handlers[type].mentions = state.handlers[type].mentions || {};
79
+ state.handlers[type].mentions.slack = mentions;
80
+ };
81
+
82
+ self.updateExcludeURLs = (type, excludedURLs) => {
83
+ state.handlers = state.handlers || {};
84
+ state.handlers[type] = state.handlers[type] || {};
85
+ state.handlers[type].excludedURLs = excludedURLs;
86
+ };
87
+
88
+ self.updateManualOverrides = (type, manualOverwrites) => {
89
+ state.handlers = state.handlers || {};
90
+ state.handlers[type] = state.handlers[type] || {};
91
+ state.handlers[type].manualOverwrites = manualOverwrites;
92
+ };
93
+
94
+ self.updateFixedURLs = (type, fixedURLs) => {
95
+ state.handlers = state.handlers || {};
96
+ state.handlers[type] = state.handlers[type] || {};
97
+ state.handlers[type].fixedURLs = fixedURLs;
98
+ };
71
99
 
72
100
  return Object.freeze(self);
73
101
  };
@@ -75,6 +103,7 @@ export const Config = (data = {}) => {
75
103
  Config.fromDynamoItem = (dynamoItem) => Config(dynamoItem);
76
104
 
77
105
  Config.toDynamoItem = (config) => ({
78
- ...config,
79
- audits: AuditConfig.toDynamoItem(config.audits),
106
+ slack: config.getSlackConfig(),
107
+ handlers: config.getHandlers(),
108
+ imports: config.getImports(),
80
109
  });
@@ -13,7 +13,6 @@
13
13
  import { hasText, isObject, isValidUrl } from '@adobe/spacecat-shared-utils';
14
14
 
15
15
  import { Base } from './base.js';
16
- import AuditConfig from './site/audit-config.js';
17
16
  import { Config, DEFAULT_CONFIG } from './site/config.js';
18
17
  import { DEFAULT_ORGANIZATION_ID } from './organization.js';
19
18
 
@@ -34,7 +33,6 @@ export const DEFAULT_DELIVERY_TYPE = DELIVERY_TYPES.AEM_EDGE;
34
33
  const Site = (data = {}) => {
35
34
  const self = Base(data);
36
35
 
37
- self.getAuditConfig = () => self.state.auditConfig;
38
36
  self.getAudits = () => self.state.audits;
39
37
  self.getBaseURL = () => self.state.baseURL;
40
38
  self.getConfig = () => self.state.config;
@@ -82,18 +80,6 @@ const Site = (data = {}) => {
82
80
  return self;
83
81
  }; */
84
82
 
85
- self.setAllAuditsDisabled = (disabled) => {
86
- self.state.auditConfig.updateAuditsDisabled(disabled);
87
- self.touch();
88
- return self;
89
- };
90
-
91
- self.updateAuditTypeConfig = (type, config) => {
92
- self.state.auditConfig.updateAuditTypeConfig(type, config);
93
- self.touch();
94
- return self;
95
- };
96
-
97
83
  /**
98
84
  * Updates the site config.
99
85
  * @param {string} config - The Site config.
@@ -222,15 +208,6 @@ export const createSite = (data) => {
222
208
  newState.audits = [];
223
209
  }
224
210
 
225
- if (!isObject(newState.auditConfig)) {
226
- newState.auditConfig = {
227
- auditsDisabled: false,
228
- auditTypeConfigs: {},
229
- };
230
- }
231
-
232
- newState.auditConfig = AuditConfig(newState.auditConfig);
233
-
234
211
  if (!isObject(newState.config)) {
235
212
  newState.config = { ...DEFAULT_CONFIG };
236
213
  }
@@ -110,8 +110,7 @@ export const updateConfiguration = async (
110
110
  const newConfigurationData = { ...configurationData };
111
111
  const latestConfiguration = await getConfiguration(dynamoClient, config);
112
112
 
113
- newConfigurationData.version = incrementVersion(latestConfiguration?.version);
114
- newConfigurationData.PK = config.pkAllConfigurations;
113
+ newConfigurationData.version = incrementVersion(latestConfiguration?.getVersion());
115
114
 
116
115
  const newConfiguration = createConfiguration(newConfigurationData);
117
116
 
@@ -14,6 +14,31 @@ import { isObject } from '@adobe/spacecat-shared-utils';
14
14
  import { ImportJobDto } from '../../dto/import-job.js';
15
15
  import { createImportJob } from '../../models/importer/import-job.js';
16
16
 
17
+ /**
18
+ * Get all Import Jobs within a specific date range
19
+ * @param {DynamoClient} dynamoClient
20
+ * @param {Object} config
21
+ * @param {Logger} log
22
+ * @param {string} startDate
23
+ * @param {string} endDate
24
+ */
25
+ export const getImportJobsByDateRange = async (dynamoClient, config, log, startDate, endDate) => {
26
+ const items = await dynamoClient.query({
27
+ TableName: config.tableNameImportJobs,
28
+ IndexName: config.indexNameAllImportJobsByDateRange,
29
+ KeyConditionExpression: 'GSI1PK = :gsi1pk AND #startTime BETWEEN :startDate AND :endDate',
30
+ ExpressionAttributeNames: {
31
+ '#startTime': 'startTime',
32
+ },
33
+ ExpressionAttributeValues: {
34
+ ':gsi1pk': config.pkAllImportJobs,
35
+ ':startDate': startDate,
36
+ ':endDate': endDate,
37
+ },
38
+ });
39
+ return items.map((item) => ImportJobDto.fromDynamoItem(item));
40
+ };
41
+
17
42
  /**
18
43
  * Get Import Job by ID
19
44
  * @param {DynamoClient} dynamoClient
@@ -15,9 +15,17 @@ import {
15
15
  getImportJobByID,
16
16
  getImportJobsByStatus,
17
17
  updateImportJob,
18
+ getImportJobsByDateRange,
18
19
  } from './accessPatterns.js';
19
20
 
20
21
  export const importJobFunctions = (dynamoClient, config, log) => ({
22
+ getImportJobsByDateRange: (startDate, endDate) => getImportJobsByDateRange(
23
+ dynamoClient,
24
+ config,
25
+ log,
26
+ startDate,
27
+ endDate,
28
+ ),
21
29
  getImportJobByID: (id) => getImportJobByID(
22
30
  dynamoClient,
23
31
  config,
@@ -336,7 +336,6 @@ export const getSiteByID = async (
336
336
 
337
337
  return isObject(dynamoItem) ? SiteDto.fromDynamoItem(dynamoItem) : null;
338
338
  };
339
-
340
339
  /**
341
340
  * Adds a site.
342
341
  *
@@ -1,60 +0,0 @@
1
- /*
2
- * Copyright 2023 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
- const AuditConfigType = (data = {}) => {
14
- const state = {
15
- disabled: data.disabled || false,
16
- excludedURLs: data.excludedURLs || [],
17
- manualOverwrites: data.manualOverwrites || [],
18
- fixedURLs: data.fixedURLs || [],
19
- };
20
-
21
- const self = {
22
- disabled: () => state.disabled,
23
- getExcludedURLs: () => state.excludedURLs,
24
- updateExcludedURLs: (excludeURLs) => {
25
- state.excludedURLs = excludeURLs;
26
- },
27
- getManualOverwrites: () => state.manualOverwrites,
28
- updateManualOverwrites: (manualOverwrites) => {
29
- state.manualOverwrites = manualOverwrites;
30
- },
31
- getFixedURLs: () => state.fixedURLs,
32
- updateFixedURLs: (fixedURLs) => {
33
- state.fixedURLs = fixedURLs;
34
- },
35
- updateDisabled: (newValue) => {
36
- state.disabled = newValue;
37
- },
38
- };
39
-
40
- return Object.freeze(self);
41
- };
42
-
43
- AuditConfigType.fromDynamoItem = (dynamoItem) => {
44
- const auditConfigTypeData = {
45
- disabled: dynamoItem.disabled,
46
- excludedURLs: dynamoItem.excludedURLs,
47
- manualOverwrites: dynamoItem.manualOverwrites,
48
- fixedURLs: dynamoItem.fixedURLs,
49
- };
50
- return AuditConfigType(auditConfigTypeData);
51
- };
52
-
53
- AuditConfigType.toDynamoItem = (auditConfigType) => ({
54
- disabled: auditConfigType.disabled(),
55
- excludedURLs: auditConfigType.getExcludedURLs(),
56
- manualOverwrites: auditConfigType.getManualOverwrites(),
57
- fixedURLs: auditConfigType.getFixedURLs(),
58
- });
59
-
60
- export default AuditConfigType;
@@ -1,85 +0,0 @@
1
- /*
2
- * Copyright 2023 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 AuditConfigType from './audit-config-type.js';
14
- import {
15
- AUDIT_TYPE_BROKEN_BACKLINKS,
16
- AUDIT_TYPE_EXPERIMENTATION_ESS_DAILY,
17
- AUDIT_TYPE_EXPERIMENTATION_ESS_MONTHLY,
18
- AUDIT_TYPE_ORGANIC_KEYWORDS,
19
- AUDIT_TYPE_ORGANIC_TRAFFIC,
20
- } from '../audit.js';
21
-
22
- const AUDIT_TYPE_DISABLED_DEFAULTS = {
23
- [AUDIT_TYPE_BROKEN_BACKLINKS]: true,
24
- [AUDIT_TYPE_EXPERIMENTATION_ESS_DAILY]: true,
25
- [AUDIT_TYPE_EXPERIMENTATION_ESS_MONTHLY]: true,
26
- [AUDIT_TYPE_ORGANIC_KEYWORDS]: true,
27
- [AUDIT_TYPE_ORGANIC_TRAFFIC]: true,
28
- };
29
-
30
- function getAuditTypeConfigs(auditTypeConfigs, auditsDisabled) {
31
- if (!auditTypeConfigs || Object.keys(auditTypeConfigs).length === 0) {
32
- return {
33
- [AUDIT_TYPE_BROKEN_BACKLINKS]: AuditConfigType({ disabled: true }),
34
- [AUDIT_TYPE_EXPERIMENTATION_ESS_DAILY]: AuditConfigType({ disabled: true }),
35
- [AUDIT_TYPE_EXPERIMENTATION_ESS_MONTHLY]: AuditConfigType({ disabled: true }),
36
- [AUDIT_TYPE_ORGANIC_KEYWORDS]: AuditConfigType({ disabled: true }),
37
- [AUDIT_TYPE_ORGANIC_TRAFFIC]: AuditConfigType({ disabled: true }),
38
- };
39
- }
40
- return Object.entries(auditTypeConfigs || {}).reduce((acc, [key, value]) => {
41
- const disabled = value.disabled !== undefined
42
- ? value.disabled : (AUDIT_TYPE_DISABLED_DEFAULTS[key] || auditsDisabled || false);
43
- acc[key] = AuditConfigType(
44
- {
45
- ...value,
46
- disabled,
47
- },
48
- );
49
- return acc;
50
- }, {});
51
- }
52
-
53
- const AuditConfig = (data = {}) => {
54
- const state = {
55
- auditsDisabled: data.auditsDisabled || false,
56
- auditTypeConfigs: getAuditTypeConfigs(data.auditTypeConfigs, data.auditsDisabled),
57
- };
58
-
59
- const self = {
60
- auditsDisabled: () => state.auditsDisabled,
61
- getAuditTypeConfigs: () => state.auditTypeConfigs,
62
- getAuditTypeConfig: (type) => state.auditTypeConfigs[type],
63
- updateAuditsDisabled: (newValue) => {
64
- state.auditsDisabled = newValue;
65
- },
66
- updateAuditTypeConfig: (type, config) => {
67
- state.auditTypeConfigs[type] = AuditConfigType(config);
68
- },
69
- };
70
-
71
- return Object.freeze(self);
72
- };
73
-
74
- AuditConfig.fromDynamoItem = (dynamoItem) => AuditConfig(dynamoItem);
75
-
76
- AuditConfig.toDynamoItem = (auditConfig) => ({
77
- auditsDisabled: auditConfig.auditsDisabled(),
78
- auditTypeConfigs: Object.entries(auditConfig.getAuditTypeConfigs())
79
- .reduce((acc, [key, value]) => {
80
- acc[key] = AuditConfigType.toDynamoItem(value);
81
- return acc;
82
- }, {}),
83
- });
84
-
85
- export default AuditConfig;