@adobe/spacecat-shared-data-access 1.0.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.
@@ -0,0 +1,265 @@
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 { isObject } from '@adobe/spacecat-shared-utils';
14
+
15
+ import {
16
+ getAuditsForSite,
17
+ getLatestAuditForSite,
18
+ getLatestAudits, removeAuditsForSite,
19
+ } from '../audits/accessPatterns.js';
20
+
21
+ import { createSite } from '../../models/site.js';
22
+ import { SiteDto } from '../../dto/site.js';
23
+
24
+ const INDEX_NAME_ALL_SITES = 'all_sites';
25
+ const PK_ALL_SITES = 'ALL_SITES';
26
+ const TABLE_NAME_SITES = 'sites';
27
+
28
+ /**
29
+ * Retrieves all sites.
30
+ *
31
+ * @param {DynamoDbClient} dynamoClient - The DynamoDB client.
32
+ * @returns {Promise<Readonly<Site>[]>} A promise that resolves to an array of all sites.
33
+ */
34
+ export const getSites = async (dynamoClient) => {
35
+ const dynamoItems = await dynamoClient.query({
36
+ TableName: TABLE_NAME_SITES,
37
+ IndexName: INDEX_NAME_ALL_SITES, // GSI name
38
+ KeyConditionExpression: 'GSI1PK = :gsi1pk',
39
+ ExpressionAttributeValues: {
40
+ ':gsi1pk': PK_ALL_SITES,
41
+ },
42
+ });
43
+
44
+ return dynamoItems.map((dynamoItem) => SiteDto.fromDynamoItem(dynamoItem));
45
+ };
46
+
47
+ /**
48
+ * Retrieves a list of base URLs for all sites.
49
+ *
50
+ * @param {DynamoDbClient} dynamoClient - The DynamoDB client.
51
+ * @returns {Promise<Array<string>>} A promise that resolves to an array of base URLs for all sites.
52
+ */
53
+ export const getSitesToAudit = async (dynamoClient) => {
54
+ const sites = await getSites(dynamoClient);
55
+
56
+ return sites.map((site) => site.getBaseURL());
57
+ };
58
+
59
+ /**
60
+ * Retrieves sites with their latest audit of a specified type.
61
+ *
62
+ * @param {DynamoDbClient} dynamoClient - The DynamoDB client.
63
+ * @param {Logger} log - The logger.
64
+ * @param {string} auditType - The type of the latest audits to retrieve for each site.
65
+ * @param {boolean} [sortAuditsAscending] - Optional. Determines if the audits
66
+ * should be sorted ascending or descending by scores.
67
+ * @returns {Promise<Readonly<Site>[]>} A promise that resolves to an array of site objects,
68
+ * each with its latest audit of the specified type.
69
+ */
70
+ export const getSitesWithLatestAudit = async (
71
+ dynamoClient,
72
+ log,
73
+ auditType,
74
+ sortAuditsAscending = true,
75
+ ) => {
76
+ const [sites, latestAudits] = await Promise.all([
77
+ getSites(dynamoClient),
78
+ getLatestAudits(dynamoClient, log, auditType, sortAuditsAscending),
79
+ ]);
80
+
81
+ const sitesMap = new Map(sites.map((site) => [site.getId(), site]));
82
+
83
+ return latestAudits.reduce((result, audit) => {
84
+ const site = sitesMap.get(audit.getSiteId());
85
+ if (site) {
86
+ site.setAudits([audit]);
87
+ result.push(site);
88
+ }
89
+ return result;
90
+ }, []);
91
+ };
92
+
93
+ /**
94
+ * Retrieves a site by its base URL.
95
+ *
96
+ * @param {DynamoDbClient} dynamoClient - The DynamoDB client.
97
+ * @param {Logger} log - The logger.
98
+ * @param {string} baseURL - The base URL of the site to retrieve.
99
+ * @returns {Promise<Readonly<Site>|null>} A promise that resolves to the site object if found,
100
+ * otherwise null.
101
+ */
102
+ export const getSiteByBaseURL = async (
103
+ dynamoClient,
104
+ log,
105
+ baseURL,
106
+ ) => {
107
+ const dynamoItems = await dynamoClient.query({
108
+ TableName: TABLE_NAME_SITES,
109
+ IndexName: INDEX_NAME_ALL_SITES,
110
+ KeyConditionExpression: 'GSI1PK = :gsi1pk AND baseURL = :baseURL',
111
+ ExpressionAttributeValues: {
112
+ ':gsi1pk': PK_ALL_SITES,
113
+ ':baseURL': baseURL,
114
+ },
115
+ Limit: 1,
116
+ });
117
+
118
+ if (dynamoItems.length === 0) {
119
+ return null;
120
+ }
121
+
122
+ return SiteDto.fromDynamoItem(dynamoItems[0]);
123
+ };
124
+
125
+ /**
126
+ * Retrieves a site by its base URL, along with associated audit information.
127
+ *
128
+ * @param {DynamoDbClient} dynamoClient - The DynamoDB client.
129
+ * @param {Logger} log - The logger.
130
+ * @param {string} baseUrl - The base URL of the site to retrieve.
131
+ * @param {string} auditType - The type of audits to retrieve for the site.
132
+ * @param {boolean} [latestOnly=false] - Determines if only the latest audit should be retrieved.
133
+ * @returns {Promise<Readonly<Site>|null>} A promise that resolves to the site object with audit
134
+ * data if found, otherwise null.
135
+ */
136
+ export const getSiteByBaseURLWithAuditInfo = async (
137
+ dynamoClient,
138
+ log,
139
+ baseUrl,
140
+ auditType,
141
+ latestOnly = false,
142
+ ) => {
143
+ const site = await getSiteByBaseURL(dynamoClient, log, baseUrl);
144
+
145
+ if (!isObject(site)) {
146
+ return null;
147
+ }
148
+
149
+ const audits = latestOnly
150
+ ? [await getLatestAuditForSite(
151
+ dynamoClient,
152
+ log,
153
+ site.getId(),
154
+ auditType,
155
+ )].filter((audit) => audit != null)
156
+ : await getAuditsForSite(
157
+ dynamoClient,
158
+ log,
159
+ site.getId(),
160
+ auditType,
161
+ );
162
+
163
+ site.setAudits(audits);
164
+
165
+ return site;
166
+ };
167
+
168
+ /**
169
+ * Retrieves a site by its base URL, including all its audits.
170
+ *
171
+ * @param {DynamoDbClient} dynamoClient - The DynamoDB client.
172
+ * @param {Logger} log - The logger.
173
+ * @param {string} baseUrl - The base URL of the site to retrieve.
174
+ * @param {string} auditType - The type of audits to retrieve for the site.
175
+ * @returns {Promise<Readonly<Site>|null>} A promise that resolves to the site object
176
+ * with all its audits.
177
+ */
178
+ export const getSiteByBaseURLWithAudits = async (
179
+ dynamoClient,
180
+ log,
181
+ baseUrl,
182
+ auditType,
183
+ ) => getSiteByBaseURLWithAuditInfo(dynamoClient, log, baseUrl, auditType, false);
184
+
185
+ /**
186
+ * Retrieves a site by its base URL, including only its latest audit.
187
+ *
188
+ * @param {DynamoDbClient} dynamoClient - The DynamoDB client.
189
+ * @param {Logger} log - The logger.
190
+ * @param {string} baseUrl - The base URL of the site to retrieve.
191
+ * @param {string} auditType - The type of the latest audit to retrieve for the site.
192
+ * @returns {Promise<Readonly<Site>|null>} A promise that resolves to the site object
193
+ * with its latest audit.
194
+ */
195
+ export const getSiteByBaseURLWithLatestAudit = async (
196
+ dynamoClient,
197
+ log,
198
+ baseUrl,
199
+ auditType,
200
+ ) => getSiteByBaseURLWithAuditInfo(dynamoClient, log, baseUrl, auditType, true);
201
+
202
+ /**
203
+ * Adds a site.
204
+ *
205
+ * @param {DynamoDbClient} dynamoClient - The DynamoDB client.
206
+ * @param {Logger} log - The logger.
207
+ * @param {object} siteData - The site data.
208
+ * @returns {Promise<Readonly<Site>>}
209
+ */
210
+ export const addSite = async (dynamoClient, log, siteData) => {
211
+ const site = createSite(siteData);
212
+ const existingSite = await getSiteByBaseURL(
213
+ dynamoClient,
214
+ log,
215
+ site.getBaseURL(),
216
+ );
217
+
218
+ if (isObject(existingSite)) {
219
+ throw new Error('Site already exists');
220
+ }
221
+
222
+ await dynamoClient.putItem(TABLE_NAME_SITES, SiteDto.toDynamoItem(site));
223
+
224
+ return site;
225
+ };
226
+
227
+ /**
228
+ * Updates a site.
229
+ *
230
+ * @param {DynamoDbClient} dynamoClient - The DynamoDB client.
231
+ * @param {Logger} log - The logger.
232
+ * @param {Site} site - The site.
233
+ * @returns {Promise<Readonly<Site>>} - The updated site.
234
+ */
235
+ export const updateSite = async (dynamoClient, log, site) => {
236
+ const existingSite = await getSiteByBaseURL(dynamoClient, log, site.getBaseURL());
237
+
238
+ if (!isObject(existingSite)) {
239
+ throw new Error('Site not found');
240
+ }
241
+
242
+ await dynamoClient.putItem(TABLE_NAME_SITES, SiteDto.toDynamoItem(site));
243
+
244
+ return site;
245
+ };
246
+
247
+ /**
248
+ * Removes a site and its related audits.
249
+ *
250
+ * @param {DynamoDbClient} dynamoClient - The DynamoDB client.
251
+ * @param {Logger} log - The logger.
252
+ * @param {string} siteId - The ID of the site to remove.
253
+ * @returns {Promise<void>}
254
+ */
255
+ export const removeSite = async (dynamoClient, log, siteId) => {
256
+ try {
257
+ // TODO: Add transaction support
258
+ await removeAuditsForSite(dynamoClient, log, siteId);
259
+
260
+ await dynamoClient.removeItem(TABLE_NAME_SITES, { id: siteId });
261
+ } catch (error) {
262
+ log.error(`Error removing site: ${error.message}`);
263
+ throw error;
264
+ }
265
+ };
@@ -0,0 +1,65 @@
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 {
14
+ addSite,
15
+ getSiteByBaseURL,
16
+ getSiteByBaseURLWithAuditInfo,
17
+ getSiteByBaseURLWithAudits,
18
+ getSiteByBaseURLWithLatestAudit,
19
+ getSites,
20
+ getSitesToAudit,
21
+ getSitesWithLatestAudit, removeSite,
22
+ updateSite,
23
+ } from './accessPatterns.js';
24
+
25
+ export const siteFunctions = (dynamoClient, log) => ({
26
+ getSites: () => getSites(
27
+ dynamoClient,
28
+ ),
29
+ getSitesToAudit: () => getSitesToAudit(
30
+ dynamoClient,
31
+ ),
32
+ getSitesWithLatestAudit: (auditType, sortAuditsAscending) => getSitesWithLatestAudit(
33
+ dynamoClient,
34
+ log,
35
+ auditType,
36
+ sortAuditsAscending,
37
+ ),
38
+ getSiteByBaseURL: (baseUrl) => getSiteByBaseURL(
39
+ dynamoClient,
40
+ log,
41
+ baseUrl,
42
+ ),
43
+ getSiteByBaseURLWithAuditInfo: (baseUrl, auditType, latestOnly) => getSiteByBaseURLWithAuditInfo(
44
+ dynamoClient,
45
+ log,
46
+ baseUrl,
47
+ auditType,
48
+ latestOnly,
49
+ ),
50
+ getSiteByBaseURLWithAudits: (baseUrl, auditType) => getSiteByBaseURLWithAudits(
51
+ dynamoClient,
52
+ log,
53
+ baseUrl,
54
+ auditType,
55
+ ),
56
+ getSiteByBaseURLWithLatestAudit: (baseUrl, auditType) => getSiteByBaseURLWithLatestAudit(
57
+ dynamoClient,
58
+ log,
59
+ baseUrl,
60
+ auditType,
61
+ ),
62
+ addSite: (siteData) => addSite(dynamoClient, log, siteData),
63
+ updateSite: (site) => updateSite(dynamoClient, log, site),
64
+ removeSite: (siteId) => removeSite(dynamoClient, log, siteId),
65
+ });