@adobe/spacecat-shared-data-access 1.36.0 → 1.38.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 +21 -0
- package/migration.sh +135 -0
- package/package.json +3 -3
- package/src/dto/configuration.js +2 -0
- package/src/dto/experiment.js +66 -0
- package/src/dto/site.js +0 -3
- package/src/index.d.ts +130 -43
- package/src/index.js +3 -0
- package/src/models/configuration.js +137 -4
- package/src/models/experiment.js +67 -0
- package/src/models/organization.js +0 -13
- package/src/models/site/config.js +59 -30
- package/src/models/site.js +0 -23
- package/src/service/configurations/accessPatterns.js +1 -2
- package/src/service/experiments/accessPatterns.js +98 -0
- package/src/service/experiments/index.js +40 -0
- package/src/service/index.js +5 -1
- package/src/service/sites/accessPatterns.js +0 -1
- package/src/models/site/audit-config-type.js +0 -60
- package/src/models/site/audit-config.js +0 -85
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-data-access-v1.38.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.37.0...@adobe/spacecat-shared-data-access-v1.38.0) (2024-07-23)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* Experimentation entity ([#288](https://github.com/adobe/spacecat-shared/issues/288)) ([774e2c7](https://github.com/adobe/spacecat-shared/commit/774e2c7013c9e617c745c494e20e1cdd8cce71e7))
|
|
7
|
+
|
|
8
|
+
# [@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)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **deps:** update external fixes ([#295](https://github.com/adobe/spacecat-shared/issues/295)) ([4e8d50d](https://github.com/adobe/spacecat-shared/commit/4e8d50d8d88b2d68b483fda2ead31542d15ee952))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* new config data model ([2525af4](https://github.com/adobe/spacecat-shared/commit/2525af48d1d4dad7e1b6e1fb3fbf3cda79d1252d))
|
|
19
|
+
* update the config model for sites and orgs ([ed556bd](https://github.com/adobe/spacecat-shared/commit/ed556bda995d8cb6b79cb44efbee7d0b779dc0aa))
|
|
20
|
+
* update to trigger release ([b1610ab](https://github.com/adobe/spacecat-shared/commit/b1610ab8160a75348803b470deaa8c28d2baf6f1))
|
|
21
|
+
|
|
1
22
|
# [@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)
|
|
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.
|
|
3
|
+
"version": "1.38.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.
|
|
35
|
-
"@aws-sdk/lib-dynamodb": "3.
|
|
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"
|
package/src/dto/configuration.js
CHANGED
|
@@ -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
|
};
|
|
@@ -0,0 +1,66 @@
|
|
|
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 { createExperiment } from '../models/experiment.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Data transfer object for Experiment.
|
|
17
|
+
*/
|
|
18
|
+
export const ExperimentDto = {
|
|
19
|
+
/**
|
|
20
|
+
* Converts an Experiment object into a DynamoDB item.
|
|
21
|
+
* @param {Readonly<Experiment>} experiment - Experiment object.
|
|
22
|
+
* @returns {{siteId, id, name, url, status, type, startDate, endDate,
|
|
23
|
+
* variants, updatedAt, updatedBy, conversionEventName, conversionEventValue}}
|
|
24
|
+
*/
|
|
25
|
+
toDynamoItem: (experiment) => ({
|
|
26
|
+
siteId: experiment.getSiteId(),
|
|
27
|
+
experimentId: experiment.getExperimentId(),
|
|
28
|
+
name: experiment.getName(),
|
|
29
|
+
url: experiment.getUrl(),
|
|
30
|
+
status: experiment.getStatus(),
|
|
31
|
+
type: experiment.getType(),
|
|
32
|
+
startDate: experiment.getStartDate(),
|
|
33
|
+
endDate: experiment.getEndDate(),
|
|
34
|
+
variants: experiment.getVariants(),
|
|
35
|
+
updatedAt: experiment.getUpdatedAt(),
|
|
36
|
+
updatedBy: experiment.getUpdatedBy(),
|
|
37
|
+
conversionEventName: experiment.getConversionEventName(),
|
|
38
|
+
conversionEventValue: experiment.getConversionEventValue(),
|
|
39
|
+
SK: `${experiment.getExperimentId()}#${experiment.getUrl()}`,
|
|
40
|
+
}),
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Converts a DynamoDB item into Experiment object;
|
|
44
|
+
* @param {object } dynamoItem - DynamoDB item.
|
|
45
|
+
* @returns {Readonly<Experiment>} Experiment object.
|
|
46
|
+
*/
|
|
47
|
+
fromDynamoItem: (dynamoItem) => {
|
|
48
|
+
const experiment = {
|
|
49
|
+
siteId: dynamoItem.siteId,
|
|
50
|
+
experimentId: dynamoItem.experimentId,
|
|
51
|
+
name: dynamoItem.name,
|
|
52
|
+
url: dynamoItem.url,
|
|
53
|
+
status: dynamoItem.status,
|
|
54
|
+
type: dynamoItem.type,
|
|
55
|
+
startDate: dynamoItem.startDate,
|
|
56
|
+
endDate: dynamoItem.endDate,
|
|
57
|
+
variants: dynamoItem.variants,
|
|
58
|
+
updatedAt: dynamoItem.updatedAt,
|
|
59
|
+
updatedBy: dynamoItem.updatedBy,
|
|
60
|
+
conversionEventName: dynamoItem.conversionEventName,
|
|
61
|
+
conversionEventValue: dynamoItem.conversionEventValue,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return createExperiment(experiment);
|
|
65
|
+
},
|
|
66
|
+
};
|
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 {
|
|
@@ -523,6 +534,76 @@ export interface ImportUrl {
|
|
|
523
534
|
|
|
524
535
|
}
|
|
525
536
|
|
|
537
|
+
/**
|
|
538
|
+
* Represents an experiment entity.
|
|
539
|
+
*/
|
|
540
|
+
export interface Experiment {
|
|
541
|
+
/**
|
|
542
|
+
* Retrieves the ID of the experiment.
|
|
543
|
+
*/
|
|
544
|
+
getExperimentId: () => string;
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Retrieves the site ID of the experiment.
|
|
548
|
+
*/
|
|
549
|
+
getSiteId: () => string;
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Retrieves the Control URL of the experiment.
|
|
553
|
+
*/
|
|
554
|
+
getUrl: () => string;
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Retrieves the experiment name.
|
|
558
|
+
*/
|
|
559
|
+
getName: () => string;
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Retrieves the experiment type.
|
|
563
|
+
*/
|
|
564
|
+
getType: () => string;
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Retrieves the experiment status.
|
|
568
|
+
*/
|
|
569
|
+
getStatus: () => string;
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Retrieves the experiment variants.
|
|
573
|
+
*/
|
|
574
|
+
getVariants: () => Array<object>;
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Retrieves the experiment start date.
|
|
578
|
+
*/
|
|
579
|
+
getStartDate: () => string;
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Retrieves the experiment end date.
|
|
583
|
+
*/
|
|
584
|
+
getEndDate: () => string;
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Retrieves the conversion event name.
|
|
588
|
+
*/
|
|
589
|
+
getConversionEventName: () => string;
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Retrieves the conversion event value
|
|
593
|
+
*/
|
|
594
|
+
getConversionEventValue: () => string;
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Retrieves the last update timestamp of the experiment entity in persistent store.
|
|
598
|
+
*/
|
|
599
|
+
getUpdatedAt: () => string;
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Retrieves the updated by of the experiment entity in persistent store.
|
|
603
|
+
*/
|
|
604
|
+
getUpdatedBy: () => string;
|
|
605
|
+
}
|
|
606
|
+
|
|
526
607
|
export interface DataAccess {
|
|
527
608
|
getAuditForSite: (
|
|
528
609
|
sitedId: string,
|
|
@@ -664,6 +745,11 @@ export interface DataAccess {
|
|
|
664
745
|
createKeyEvent: (keyEventData: object) => Promise<KeyEvent>;
|
|
665
746
|
getKeyEventsForSite: (siteId: string) => Promise<KeyEvent[]>
|
|
666
747
|
removeKeyEvent: (keyEventId: string) => Promise<void>;
|
|
748
|
+
|
|
749
|
+
// experiment functions
|
|
750
|
+
getExperiments: (siteId: string, experimentId?: string) => Promise<Experiment[]>;
|
|
751
|
+
getExperiment: (siteId: string, experimentId: string, url: string) => Promise<Experiment | null>;
|
|
752
|
+
upsertExperiment: (experimentData: object) => Promise<Experiment>;
|
|
667
753
|
}
|
|
668
754
|
|
|
669
755
|
interface DataAccessConfig {
|
|
@@ -677,6 +763,7 @@ interface DataAccessConfig {
|
|
|
677
763
|
tableNameSiteTopPages: string;
|
|
678
764
|
tableNameImportJobs: string;
|
|
679
765
|
tableNameImportUrls: string;
|
|
766
|
+
tableNameExperiments: string;
|
|
680
767
|
indexNameAllKeyEventsBySiteId: string,
|
|
681
768
|
indexNameAllSites: string;
|
|
682
769
|
indexNameAllSitesOrganizations: string,
|
package/src/index.js
CHANGED
|
@@ -24,6 +24,7 @@ const TABLE_NAME_CONFIGURATIONS = 'spacecat-services-configurations-dev';
|
|
|
24
24
|
const TABLE_NAME_SITE_TOP_PAGES = 'spacecat-services-site-top-pages-dev';
|
|
25
25
|
const TABLE_NAME_IMPORT_JOBS = 'spacecat-services-import-jobs-dev';
|
|
26
26
|
const TABLE_NAME_IMPORT_URLS = 'spacecat-services-import-urls-dev';
|
|
27
|
+
const TABLE_NAME_EXPERIMENTS = 'spacecat-services-experiments-dev';
|
|
27
28
|
|
|
28
29
|
const INDEX_NAME_ALL_KEY_EVENTS_BY_SITE_ID = 'spacecat-services-key-events-by-site-id';
|
|
29
30
|
const INDEX_NAME_ALL_SITES = 'spacecat-services-all-sites-dev';
|
|
@@ -58,6 +59,7 @@ export default function dataAccessWrapper(fn) {
|
|
|
58
59
|
DYNAMO_TABLE_NAME_SITE_TOP_PAGES = TABLE_NAME_SITE_TOP_PAGES,
|
|
59
60
|
DYNAMO_TABLE_NAME_IMPORT_JOBS = TABLE_NAME_IMPORT_JOBS,
|
|
60
61
|
DYNAMO_TABLE_NAME_IMPORT_URLS = TABLE_NAME_IMPORT_URLS,
|
|
62
|
+
DYNAMO_TABLE_NAME_EXPERIMENTS = TABLE_NAME_EXPERIMENTS,
|
|
61
63
|
DYNAMO_INDEX_NAME_ALL_KEY_EVENTS_BY_SITE_ID = INDEX_NAME_ALL_KEY_EVENTS_BY_SITE_ID,
|
|
62
64
|
DYNAMO_INDEX_NAME_ALL_SITES = INDEX_NAME_ALL_SITES,
|
|
63
65
|
DYNAMO_INDEX_NAME_ALL_SITES_BY_DELIVERY_TYPE = INDEX_NAME_ALL_SITES_BY_DELIVERY_TYPE,
|
|
@@ -83,6 +85,7 @@ export default function dataAccessWrapper(fn) {
|
|
|
83
85
|
tableNameSiteTopPages: DYNAMO_TABLE_NAME_SITE_TOP_PAGES,
|
|
84
86
|
tableNameImportJobs: DYNAMO_TABLE_NAME_IMPORT_JOBS,
|
|
85
87
|
tableNameImportUrls: DYNAMO_TABLE_NAME_IMPORT_URLS,
|
|
88
|
+
tableNameExperiments: DYNAMO_TABLE_NAME_EXPERIMENTS,
|
|
86
89
|
indexNameAllKeyEventsBySiteId: DYNAMO_INDEX_NAME_ALL_KEY_EVENTS_BY_SITE_ID,
|
|
87
90
|
indexNameAllSites: DYNAMO_INDEX_NAME_ALL_SITES,
|
|
88
91
|
indexNameAllOrganizations: DYNAMO_INDEX_NAME_ALL_ORGANIZATIONS,
|
|
@@ -13,18 +13,151 @@
|
|
|
13
13
|
import Joi from 'joi';
|
|
14
14
|
|
|
15
15
|
const Configuration = (data = {}) => {
|
|
16
|
-
const
|
|
17
|
-
self
|
|
18
|
-
self.
|
|
19
|
-
self.
|
|
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);
|
|
@@ -0,0 +1,67 @@
|
|
|
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 } from '@adobe/spacecat-shared-utils';
|
|
14
|
+
|
|
15
|
+
import { Base } from './base.js';
|
|
16
|
+
|
|
17
|
+
export const DEFAULT_UPDATED_BY = 'spacecat';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a new Experiment.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} data - experiment data
|
|
23
|
+
* @returns {Readonly<SiteCandidate>} new experiment
|
|
24
|
+
*/
|
|
25
|
+
const Experiment = (data = {}) => {
|
|
26
|
+
const self = Base(data);
|
|
27
|
+
|
|
28
|
+
self.getSiteId = () => self.state.siteId;
|
|
29
|
+
self.getExperimentId = () => self.state.experimentId;
|
|
30
|
+
self.getName = () => self.state.name;
|
|
31
|
+
self.getUrl = () => self.state.url;
|
|
32
|
+
self.getStatus = () => self.state.status;
|
|
33
|
+
self.getType = () => self.state.type;
|
|
34
|
+
self.getStartDate = () => self.state.startDate;
|
|
35
|
+
self.getEndDate = () => self.state.endDate;
|
|
36
|
+
self.getVariants = () => self.state.variants;
|
|
37
|
+
self.getUpdatedAt = () => self.state.updatedAt;
|
|
38
|
+
self.getUpdatedBy = () => self.state.updatedBy;
|
|
39
|
+
self.getConversionEventName = () => self.state.conversionEventName;
|
|
40
|
+
self.getConversionEventValue = () => self.state.conversionEventValue;
|
|
41
|
+
|
|
42
|
+
return Object.freeze(self);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates a new Experiment.
|
|
47
|
+
*
|
|
48
|
+
* @param {object} data - experiment data
|
|
49
|
+
* @returns {Readonly<SiteCandidate>} new experiment
|
|
50
|
+
*/
|
|
51
|
+
export const createExperiment = (data) => {
|
|
52
|
+
const newState = { ...data };
|
|
53
|
+
|
|
54
|
+
if (!hasText(newState.siteId)) {
|
|
55
|
+
throw new Error('Site ID must be provided');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!hasText(newState.experimentId)) {
|
|
59
|
+
throw new Error('Experiment ID must be provided');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!hasText(newState.updatedBy)) {
|
|
63
|
+
newState.updatedBy = DEFAULT_UPDATED_BY;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return Experiment(newState);
|
|
67
|
+
};
|
|
@@ -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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Joi.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
79
|
-
|
|
106
|
+
slack: config.getSlackConfig(),
|
|
107
|
+
handlers: config.getHandlers(),
|
|
108
|
+
imports: config.getImports(),
|
|
80
109
|
});
|
package/src/models/site.js
CHANGED
|
@@ -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?.
|
|
114
|
-
newConfigurationData.PK = config.pkAllConfigurations;
|
|
113
|
+
newConfigurationData.version = incrementVersion(latestConfiguration?.getVersion());
|
|
115
114
|
|
|
116
115
|
const newConfiguration = createConfiguration(newConfigurationData);
|
|
117
116
|
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
import { createExperiment } from '../../models/experiment.js';
|
|
15
|
+
import { ExperimentDto } from '../../dto/experiment.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns the experiment if exists in experiments table using siteId, experimentId and url
|
|
19
|
+
* @param {DynamoDbClient} dynamoClient - The DynamoDB client.
|
|
20
|
+
* @param {DataAccessConfig} config - The data access config.
|
|
21
|
+
* @param {string} siteId - siteId of the experiment.
|
|
22
|
+
* @param {string} id - id of the experiment.
|
|
23
|
+
* @return the experiment if exists, null otherwise
|
|
24
|
+
*/
|
|
25
|
+
export const getExperiment = async (dynamoClient, config, siteId, experimentId, url) => {
|
|
26
|
+
const queryParams = {
|
|
27
|
+
TableName: config.tableNameExperiments,
|
|
28
|
+
KeyConditionExpression: 'siteId = :siteId AND SK = :SK',
|
|
29
|
+
ExpressionAttributeValues: {
|
|
30
|
+
':siteId': siteId,
|
|
31
|
+
':SK': `${experimentId}#${url}`,
|
|
32
|
+
},
|
|
33
|
+
ScanIndexForward: false,
|
|
34
|
+
};
|
|
35
|
+
const dynamoItems = await dynamoClient.query(queryParams);
|
|
36
|
+
if (dynamoItems != null && dynamoItems.length > 0 && isObject(dynamoItems[0])) {
|
|
37
|
+
return ExperimentDto.fromDynamoItem(dynamoItems[0]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return null;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Retrieves all experiments for a given siteId.
|
|
45
|
+
* @param {*} dynamoClient - The DynamoDB client.
|
|
46
|
+
* @param {*} config - The data access config.
|
|
47
|
+
* @param {*} log - the logger object
|
|
48
|
+
* @param {*} siteId - siteId of the experiment.
|
|
49
|
+
* @param {*} experimentId - experiment id.
|
|
50
|
+
* @returns {Promise<ReadonlyArray<SiteCandidate>>} A promise that resolves to
|
|
51
|
+
* an array of experiments
|
|
52
|
+
*/
|
|
53
|
+
export const getExperiments = async (
|
|
54
|
+
dynamoClient,
|
|
55
|
+
config,
|
|
56
|
+
log,
|
|
57
|
+
siteId,
|
|
58
|
+
experimentId,
|
|
59
|
+
) => {
|
|
60
|
+
const queryParams = {
|
|
61
|
+
TableName: config.tableNameExperiments,
|
|
62
|
+
KeyConditionExpression: 'siteId = :siteId',
|
|
63
|
+
ExpressionAttributeValues: { ':siteId': siteId },
|
|
64
|
+
ScanIndexForward: false,
|
|
65
|
+
};
|
|
66
|
+
if (experimentId) {
|
|
67
|
+
queryParams.KeyConditionExpression += ' AND begins_with(SK, :experimentId)';
|
|
68
|
+
queryParams.ExpressionAttributeValues[':experimentId'] = experimentId;
|
|
69
|
+
}
|
|
70
|
+
const dynamoItems = await dynamoClient.query(queryParams);
|
|
71
|
+
|
|
72
|
+
return dynamoItems.map((item) => ExperimentDto.fromDynamoItem(item));
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Upserts an experiment.
|
|
77
|
+
*
|
|
78
|
+
* @param {DynamoDbClient} dynamoClient - The DynamoDB client.
|
|
79
|
+
* @param {DataAccessConfig} config - The data access config.
|
|
80
|
+
* @param {object} log - the logger object
|
|
81
|
+
* @param {object} experimentData - The experiment data.
|
|
82
|
+
* @returns {Promise<Readonly<SiteCandidate>>} A promise that resolves to newly created/updated
|
|
83
|
+
* experiment
|
|
84
|
+
*/
|
|
85
|
+
export const upsertExperiment = async (
|
|
86
|
+
dynamoClient,
|
|
87
|
+
config,
|
|
88
|
+
log,
|
|
89
|
+
experimentData,
|
|
90
|
+
) => {
|
|
91
|
+
const experiment = createExperiment(experimentData);
|
|
92
|
+
await dynamoClient.putItem(
|
|
93
|
+
config.tableNameExperiments,
|
|
94
|
+
ExperimentDto.toDynamoItem(experiment),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return experiment;
|
|
98
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
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 {
|
|
14
|
+
getExperiments,
|
|
15
|
+
upsertExperiment,
|
|
16
|
+
getExperiment,
|
|
17
|
+
} from './accessPatterns.js';
|
|
18
|
+
|
|
19
|
+
export const experimentFunctions = (dynamoClient, config, log) => ({
|
|
20
|
+
getExperiments: (siteId, experimentId) => getExperiments(
|
|
21
|
+
dynamoClient,
|
|
22
|
+
config,
|
|
23
|
+
log,
|
|
24
|
+
siteId,
|
|
25
|
+
experimentId,
|
|
26
|
+
),
|
|
27
|
+
upsertExperiment: (experimentData) => upsertExperiment(
|
|
28
|
+
dynamoClient,
|
|
29
|
+
config,
|
|
30
|
+
log,
|
|
31
|
+
experimentData,
|
|
32
|
+
),
|
|
33
|
+
getExperiment: (siteId, experimentId, url) => getExperiment(
|
|
34
|
+
dynamoClient,
|
|
35
|
+
config,
|
|
36
|
+
siteId,
|
|
37
|
+
experimentId,
|
|
38
|
+
url,
|
|
39
|
+
),
|
|
40
|
+
});
|
package/src/service/index.js
CHANGED
|
@@ -20,13 +20,15 @@ import { configurationFunctions } from './configurations/index.js';
|
|
|
20
20
|
import { siteTopPagesFunctions } from './site-top-pages/index.js';
|
|
21
21
|
import { importJobFunctions } from './import-job/index.js';
|
|
22
22
|
import { importUrlFunctions } from './import-url/index.js';
|
|
23
|
+
import { experimentFunctions } from './experiments/index.js';
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Creates a data access object.
|
|
26
27
|
*
|
|
27
28
|
* @param {{pkAllSites: string, pkAllLatestAudits: string, indexNameAllLatestAuditScores: string,
|
|
28
29
|
* tableNameAudits: string,tableNameLatestAudits: string, indexNameAllSitesOrganizations: string,
|
|
29
|
-
* tableNameSites: string, tableNameOrganizations: string,
|
|
30
|
+
* tableNameSites: string, tableNameOrganizations: string, tableNameExperiments: string,
|
|
31
|
+
* indexNameAllSites: string,
|
|
30
32
|
* tableNameImportJobs: string, pkAllImportJobs: string, indexNameAllImportJobs: string,
|
|
31
33
|
* tableNameSiteTopPages: string, indexNameAllOrganizations: string,
|
|
32
34
|
* indexNameAllOrganizationsByImsOrgId: string, pkAllOrganizations: string}} config configuration
|
|
@@ -45,6 +47,7 @@ export const createDataAccess = (config, log = console) => {
|
|
|
45
47
|
const siteTopPagesFuncs = siteTopPagesFunctions(dynamoClient, config);
|
|
46
48
|
const importJobFuncs = importJobFunctions(dynamoClient, config, log);
|
|
47
49
|
const importUrlFuncs = importUrlFunctions(dynamoClient, config, log);
|
|
50
|
+
const experimentFuncs = experimentFunctions(dynamoClient, config, log);
|
|
48
51
|
|
|
49
52
|
return {
|
|
50
53
|
...auditFuncs,
|
|
@@ -56,5 +59,6 @@ export const createDataAccess = (config, log = console) => {
|
|
|
56
59
|
...siteTopPagesFuncs,
|
|
57
60
|
...importJobFuncs,
|
|
58
61
|
...importUrlFuncs,
|
|
62
|
+
...experimentFuncs,
|
|
59
63
|
};
|
|
60
64
|
};
|
|
@@ -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;
|