@adobe/spacecat-shared-data-access 1.44.4 → 1.45.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,17 @@
1
+ # [@adobe/spacecat-shared-data-access-v1.45.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.44.5...@adobe/spacecat-shared-data-access-v1.45.0) (2024-09-24)
2
+
3
+
4
+ ### Features
5
+
6
+ * Add an additional scope to all imports on all domains ([#383](https://github.com/adobe/spacecat-shared/issues/383)) ([92ad32c](https://github.com/adobe/spacecat-shared/commit/92ad32c892b151082b1ac6b67b29f6307dbeba15))
7
+
8
+ # [@adobe/spacecat-shared-data-access-v1.44.5](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.44.4...@adobe/spacecat-shared-data-access-v1.44.5) (2024-09-23)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * [Importer] Specify the list of supported features ([#362](https://github.com/adobe/spacecat-shared/issues/362)) ([c1cf7e1](https://github.com/adobe/spacecat-shared/commit/c1cf7e19b7a4bfe78a03a1996bb6d6c8667dd823))
14
+
1
15
  # [@adobe/spacecat-shared-data-access-v1.44.4](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.44.3...@adobe/spacecat-shared-data-access-v1.44.4) (2024-09-21)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-data-access",
3
- "version": "1.44.4",
3
+ "version": "1.45.0",
4
4
  "description": "Shared modules of the Spacecat Services - Data Access",
5
5
  "type": "module",
6
6
  "engines": {
@@ -13,18 +13,18 @@
13
13
  import { createImportJob } from '../models/importer/import-job.js';
14
14
 
15
15
  /**
16
- * Data Transfer Object for ImportJob
16
+ * The ImportJobDto is a helper that can convert an ImportJob object to a DynamoDB item and
17
+ * vice versa.
17
18
  */
18
-
19
19
  export const ImportJobDto = {
20
20
 
21
21
  /**
22
- * Converts an ImportJob object into a DynamoDB item.
23
- * @param importJob
24
- * @returns {{duration: *, baseURL: *, failedCount: *, apiKey: *,
25
- * options: *, successCount: *, importQueueId: *, startTime: *, id: *,
26
- * endTime: *, status: *}}
27
- */
22
+ * Converts an ImportJob object into a DynamoDB item.
23
+ * @param importJob
24
+ * @returns {{duration: *, baseURL: *, failedCount: *, apiKey: *,
25
+ * options: *, successCount: *, importQueueId: *, startTime: *, id: *,
26
+ * endTime: *, status: *}}
27
+ */
28
28
  toDynamoItem: (importJob) => ({
29
29
  id: importJob.getId(),
30
30
  baseURL: importJob.getBaseURL(),
@@ -37,14 +37,17 @@ export const ImportJobDto = {
37
37
  urlCount: importJob.getUrlCount(),
38
38
  successCount: importJob.getSuccessCount(),
39
39
  failedCount: importJob.getFailedCount(),
40
+ redirectCount: importJob.getRedirectCount(),
40
41
  importQueueId: importJob.getImportQueueId(),
41
42
  initiatedBy: importJob.getInitiatedBy(),
42
43
  GSI1PK: 'ALL_IMPORT_JOBS',
43
44
  }),
44
45
 
45
46
  /**
46
- * Converts a DynamoDB item into an ImportJob object.
47
- */
47
+ * Converts a DynamoDB item into an ImportJob object.
48
+ * @param {object} dynamoItem - The DynamoDB item to convert.
49
+ * @returns {ImportJob} - The ImportJob object.
50
+ */
48
51
  fromDynamoItem: (dynamoItem) => {
49
52
  const importJobData = {
50
53
  id: dynamoItem.id,
@@ -58,10 +61,12 @@ export const ImportJobDto = {
58
61
  urlCount: dynamoItem.urlCount,
59
62
  successCount: dynamoItem.successCount,
60
63
  failedCount: dynamoItem.failedCount,
64
+ redirectCount: dynamoItem.redirectCount,
61
65
  importQueueId: dynamoItem.importQueueId,
62
66
  initiatedBy: dynamoItem.initiatedBy,
63
67
  };
64
68
 
65
69
  return createImportJob(importJobData);
66
70
  },
71
+
67
72
  };
@@ -13,14 +13,15 @@
13
13
  import { createImportUrl } from '../models/importer/import-url.js';
14
14
 
15
15
  /**
16
- * Data Transfer Object for ImportUrl
16
+ * The ImportUrlDto is a helper that can convert an ImportUrl object to a DynamoDB item and
17
+ * vice versa.
17
18
  */
18
-
19
19
  export const ImportUrlDto = {
20
20
 
21
21
  /**
22
- * Converts an importUrl object to a DynamoDB item
23
- */
22
+ * Converts an ImportUrl object to a DynamoDB item.
23
+ * @returns {object} The new DynamoDB item.
24
+ */
24
25
  toDynamoItem: (importUrl) => ({
25
26
  id: importUrl.getId(),
26
27
  jobId: importUrl.getJobId(),
@@ -32,8 +33,10 @@ export const ImportUrlDto = {
32
33
  }),
33
34
 
34
35
  /**
35
- * Converts a DynamoDB item into an ImportUrl object
36
- */
36
+ * Converts a DynamoDB item into an ImportUrl object.
37
+ * @param {object} dynamoItem - The DynamoDB item to convert.
38
+ * @returns {ImportUrl} - The ImportUrl object.
39
+ */
37
40
  fromDynamoItem: (dynamoItem) => {
38
41
  const importUrlData = {
39
42
  id: dynamoItem.id,
package/src/index.d.ts CHANGED
@@ -10,6 +10,22 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
+ // see packages/spacecat-shared-data-access/src/models/importer/import-constants.js
14
+ export declare const ImportJobStatus: {
15
+ readonly RUNNING: 'RUNNING';
16
+ readonly COMPLETE: 'COMPLETE';
17
+ readonly FAILED: 'FAILED';
18
+ };
19
+
20
+ // packages/spacecat-shared-data-access/src/models/importer/import-constants.js
21
+ export declare const ImportUrlStatus: {
22
+ readonly PENDING: 'PENDING';
23
+ readonly REDIRECT: 'REDIRECT';
24
+ readonly RUNNING: 'RUNNING';
25
+ readonly COMPLETE: 'COMPLETE';
26
+ readonly FAILED: 'FAILED';
27
+ };
28
+
13
29
  // TODO: introduce AuditType interface or Scores interface
14
30
 
15
31
  /**
@@ -463,7 +479,7 @@ export interface ImportJob {
463
479
  /**
464
480
  * Retrieves the status of the import job.
465
481
  */
466
- getStatus: () => string;
482
+ getStatus: () => typeof ImportJobStatus;
467
483
 
468
484
  /**
469
485
  * Retrieves the baseURL of the import job.
@@ -505,6 +521,11 @@ export interface ImportJob {
505
521
  */
506
522
  getFailedCount: () => number;
507
523
 
524
+ /**
525
+ * Retrieves the redirect count of the import job.
526
+ */
527
+ getRedirectCount: () => number;
528
+
508
529
  /**
509
530
  * Retrieves the importQueueId of the import job.
510
531
  */
@@ -514,30 +535,43 @@ export interface ImportJob {
514
535
  * Retrieves the initiatedBy metadata (name, imsOrgId, imsUserId, userAgent) of the import job.
515
536
  */
516
537
  getInitiatedBy: () => object;
517
-
518
538
  }
519
539
 
520
540
  export interface ImportUrl {
521
541
  /**
522
542
  * Retrieves the ID of the import URL.
523
543
  */
524
- getId: () => string;
544
+ getId: () => string;
525
545
 
526
546
  /**
527
547
  * Retrieves the status of the import URL.
528
548
  */
529
- getStatus: () => string;
549
+ getStatus: () => typeof ImportUrlStatus;
530
550
 
531
551
  /**
532
552
  * Retrieves the URL of the import URL.
533
553
  */
534
- getUrl: () => string;
554
+ getUrl: () => string;
535
555
 
536
556
  /**
537
557
  * Retrieves the job ID of the import URL.
538
558
  */
539
- getJobId: () => string;
559
+ getJobId: () => string;
540
560
 
561
+ /**
562
+ * The reason that the import of a URL failed.
563
+ */
564
+ getReason: () => string;
565
+
566
+ /**
567
+ * The absolute path to the resource that is being imported for the given URL.
568
+ */
569
+ getFile: () => string;
570
+
571
+ /**
572
+ * Retrieves the resulting path and filename of the imported file.
573
+ */
574
+ getPath: () => string;
541
575
  }
542
576
 
543
577
  /**
@@ -853,13 +887,3 @@ export function createDataAccess(
853
887
  config: DataAccessConfig,
854
888
  logger: object,
855
889
  ): DataAccess;
856
-
857
- export interface ImportJobStatus {
858
- RUNNING: string,
859
- COMPLETE: string,
860
- FAILED: string,
861
- }
862
-
863
- export interface ImportUrlStatus extends ImportJobStatus {
864
- PENDING: string,
865
- }
package/src/index.js CHANGED
@@ -11,8 +11,8 @@
11
11
  */
12
12
 
13
13
  import { createDataAccess } from './service/index.js';
14
- import { ImportJobStatus } from './models/importer/import-job.js';
15
- import { ImportUrlStatus } from './models/importer/import-url.js';
14
+
15
+ export { ImportJobStatus, ImportUrlStatus, ImportOptions } from './models/importer/import-constants.js';
16
16
 
17
17
  const TABLE_NAME_AUDITS = 'spacecat-services-audits-dev';
18
18
  const TABLE_NAME_KEY_EVENTS = 'spacecat-services-key-events';
@@ -113,8 +113,3 @@ export default function dataAccessWrapper(fn) {
113
113
  return fn(request, context);
114
114
  };
115
115
  }
116
-
117
- export {
118
- ImportJobStatus,
119
- ImportUrlStatus,
120
- };
@@ -26,6 +26,7 @@ const scopeNames = [
26
26
  'imports.read',
27
27
  'imports.write',
28
28
  'imports.read_all',
29
+ 'imports.write_all_domains',
29
30
  ];
30
31
 
31
32
  const ApiKey = (data) => {
@@ -0,0 +1,39 @@
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
+ /**
14
+ * Supported Import Options.
15
+ */
16
+ export const ImportOptions = {
17
+ ENABLE_JAVASCRIPT: 'enableJavascript',
18
+ PAGE_LOAD_TIMEOUT: 'pageLoadTimeout',
19
+ };
20
+
21
+ /**
22
+ * Import Job Status types.
23
+ * Any changes to this object needs to be reflected in the index.d.ts file as well.
24
+ */
25
+ export const ImportJobStatus = {
26
+ RUNNING: 'RUNNING',
27
+ COMPLETE: 'COMPLETE',
28
+ FAILED: 'FAILED',
29
+ };
30
+
31
+ /**
32
+ * ImportURL Status types.
33
+ * Any changes to this object needs to be reflected in the index.d.ts file as well.
34
+ */
35
+ export const ImportUrlStatus = {
36
+ PENDING: 'PENDING',
37
+ REDIRECT: 'REDIRECT',
38
+ ...ImportJobStatus,
39
+ };
@@ -14,22 +14,18 @@ import {
14
14
  hasText, isIsoDate, isValidUrl, isObject, isString, isNumber, isInteger,
15
15
  } from '@adobe/spacecat-shared-utils';
16
16
  import { Base } from '../base.js';
17
-
18
- export const ImportJobStatus = {
19
- RUNNING: 'RUNNING',
20
- COMPLETE: 'COMPLETE',
21
- FAILED: 'FAILED',
22
- };
17
+ import { ImportJobStatus, ImportOptions } from './import-constants.js';
23
18
 
24
19
  /**
25
20
  * Creates a new ImportJob object.
26
21
  *
27
- * @param {Object} importJobData - The data for the ImportJob object.
22
+ * @param {Object} data - The data for the ImportJob object.
28
23
  * @returns {ImportJob} The new ImportJob object.
29
24
  */
30
25
  const ImportJob = (data) => {
31
26
  const self = Base(data);
32
27
 
28
+ // generate get methods for all properties of our base object
33
29
  self.getBaseURL = () => self.state.baseURL;
34
30
  self.getHashedApiKey = () => self.state.hashedApiKey;
35
31
  self.getOptions = () => self.state.options;
@@ -40,131 +36,147 @@ const ImportJob = (data) => {
40
36
  self.getUrlCount = () => self.state.urlCount;
41
37
  self.getSuccessCount = () => self.state.successCount;
42
38
  self.getFailedCount = () => self.state.failedCount;
39
+ self.getRedirectCount = () => self.state.redirectCount;
43
40
  self.getImportQueueId = () => self.state.importQueueId;
44
41
  self.getInitiatedBy = () => self.state.initiatedBy;
45
42
 
46
43
  /**
47
- * Updates the end time of the ImportJob.
48
- * @param {string} endTime - The new end time.
49
- * @returns {ImportJob} The updated ImportJob object.
50
- */
51
- self.updateEndTime = (endTime) => {
52
- if (!isIsoDate(endTime)) {
53
- throw new Error(`Invalid end time during update: ${endTime}`);
44
+ * Updates the state of the ImportJob.
45
+ * @param key - The key to update.
46
+ * @param value - The new value.
47
+ * @param validator - An optional validation function to use before updating the value.
48
+ * @returns {ImportJob} The updated ImportJob object.
49
+ */
50
+ const updateState = (key, value, validator) => {
51
+ if (validator && typeof validator === 'function') {
52
+ validator(value);
54
53
  }
55
54
 
56
- self.state.endTime = endTime;
55
+ self.state[key] = value;
57
56
  self.touch();
58
57
 
59
58
  return self;
60
59
  };
61
60
 
62
61
  /**
63
- * Updates the duration of the ImportJob.
64
- * @param {number} duration - The new duration.
65
- * @returns {ImportJob} The updated ImportJob object.
66
- */
67
- self.updateDuration = (duration) => {
68
- if (!isNumber(duration)) {
69
- throw new Error(`Invalid duration during update: ${duration}`);
62
+ * Updates the end time of the ImportJob.
63
+ * @param {string} endTime - The new end time in JavaScript ISO date string in Zulu (UTC)
64
+ * timezone. eg. 2024-05-29T14:36:00.000Z.
65
+ */
66
+ self.updateEndTime = (endTime) => updateState('endTime', endTime, (value) => {
67
+ if (!isIsoDate(value)) {
68
+ throw new Error(`Invalid end time during update: ${endTime}`);
70
69
  }
71
-
72
- self.state.duration = duration;
73
- self.touch();
74
-
75
- return self;
76
- };
70
+ });
77
71
 
78
72
  /**
79
- * Updates the status of the ImportJob.
80
- * @param {string} status - The new status.
81
- * @returns {ImportJob} The updated ImportJob object.
82
- */
83
- self.updateStatus = (status) => {
84
- if (!Object.values(ImportJobStatus).includes(status)) {
85
- throw new Error(`Invalid Import Job status during update: ${status}`);
73
+ * Updates the duration of the ImportJob.
74
+ * @param {number} duration - The new duration.
75
+ */
76
+ self.updateDuration = (duration) => updateState('duration', duration, (value) => {
77
+ if (!isNumber(value)) {
78
+ throw new Error(`Invalid duration during update: ${value}`);
86
79
  }
80
+ });
87
81
 
88
- self.state.status = status;
89
- self.touch();
90
-
91
- return self;
92
- };
82
+ /**
83
+ * Updates the status of the ImportJob.
84
+ * @param {string} status - The new status.
85
+ */
86
+ self.updateStatus = (status) => updateState('status', status, (value) => {
87
+ if (!Object.values(ImportJobStatus).includes(value)) {
88
+ throw new Error(`Invalid Import Job status during update: ${value}`);
89
+ }
90
+ });
93
91
 
94
92
  /**
95
93
  * Updates the Url count of the ImportJob
96
94
  * @param {number} urlCount - The new url count.
97
- * @returns {ImportJob} The updated ImportJob object.
98
95
  */
99
- self.updateUrlCount = (urlCount) => {
100
- if (!isInteger(urlCount)) {
101
- throw new Error(`Invalid url count during update: ${urlCount}`);
96
+ self.updateUrlCount = (urlCount) => updateState('urlCount', urlCount, (value) => {
97
+ if (!isInteger(value)) {
98
+ throw new Error(`Invalid url count during update: ${value}`);
102
99
  }
103
-
104
- self.state.urlCount = urlCount;
105
- self.touch();
106
-
107
- return self;
108
- };
100
+ });
109
101
 
110
102
  /**
111
- * Updates the success count of the ImportJob.
112
- * @param {number} successCount - The new success count.
113
- * @returns {ImportJob} The updated ImportJob object.
114
- */
115
- self.updateSuccessCount = (successCount) => {
116
- if (!isInteger(successCount)) {
117
- throw new Error(`Invalid success count during update: ${successCount}`);
103
+ * Updates the success count of the ImportJob.
104
+ * @param {number} successCount - The new success count.
105
+ * @returns {ImportJob} The updated ImportJob object.
106
+ */
107
+ self.updateSuccessCount = (successCount) => updateState('successCount', successCount, (value) => {
108
+ if (!isInteger(value) || value < 0) {
109
+ throw new Error(`Invalid success count during update: ${value}`);
118
110
  }
119
-
120
- self.state.successCount = successCount;
121
- self.touch();
122
-
123
- return self;
124
- };
111
+ });
125
112
 
126
113
  /**
127
- * Updates the failed count of the ImportJob.
128
- * @param {number} failedCount - The new failed count.
129
- * @returns {ImportJob} The updated ImportJob object.
130
- */
131
- self.updateFailedCount = (failedCount) => {
132
- if (!isInteger(failedCount)) {
133
- throw new Error(`Invalid failed count during update: ${failedCount}`);
114
+ * Updates the failed count of the ImportJob.
115
+ * @param {number} failedCount - The new failed count.
116
+ * @returns {ImportJob} The updated ImportJob object.
117
+ */
118
+ self.updateFailedCount = (failedCount) => updateState('failedCount', failedCount, (value) => {
119
+ if (!isInteger(value) || value < 0) {
120
+ throw new Error(`Invalid failed count during update: ${value}`);
134
121
  }
135
-
136
- self.state.failedCount = failedCount;
137
- self.touch();
138
-
139
- return self;
140
- };
122
+ });
141
123
 
142
124
  /**
143
- * Updates the import queue id of the ImportJob.
144
- * @param {string} importQueueId - The new import queue id.
145
- * @returns {ImportJob} The updated ImportJob object.
146
- */
147
- self.updateImportQueueId = (importQueueId) => {
148
- if (!hasText(importQueueId)) {
149
- throw new Error(`Invalid import queue id during update: ${importQueueId}`);
125
+ * Updates the redirect count of the ImportJob.
126
+ * @param {number} redirectCount - The new redirect count.
127
+ * @returns {ImportJob} The updated ImportJob object.
128
+ */
129
+ self.updateRedirectCount = (redirectCount) => updateState('redirectCount', redirectCount, (value) => {
130
+ if (!isInteger(value) || value < 0) {
131
+ throw new Error(`Invalid redirect count during update: ${value}`);
150
132
  }
133
+ });
151
134
 
152
- self.state.importQueueId = importQueueId;
153
- self.touch();
135
+ /**
136
+ * Updates the import queue id of the ImportJob.
137
+ * @param {string} importQueueId - The new import queue id.
138
+ * @returns {ImportJob} The updated ImportJob object.
139
+ */
140
+ self.updateImportQueueId = (importQueueId) => updateState(
141
+ 'importQueueId',
142
+ importQueueId,
143
+ (value) => {
144
+ if (!hasText(importQueueId)) {
145
+ throw new Error(`Invalid import queue id during update: ${value}`);
146
+ }
147
+ },
148
+ );
154
149
 
155
- return self;
156
- };
157
150
  return Object.freeze(self);
158
151
  };
159
152
 
160
153
  /**
161
154
  * Creates a new ImportJob object.
162
- * @param {Object} importJobData - The data for the ImportJob object.
155
+ * @param {Object} data - The data for the ImportJob object.
163
156
  * @returns {ImportJob} The new ImportJob object.
164
157
  */
165
158
  export const createImportJob = (data) => {
159
+ // Define a list of data type validators for each import option
160
+ const ImportOptionTypeValidator = {
161
+ [ImportOptions.ENABLE_JAVASCRIPT]: (value) => {
162
+ if (value !== true && value !== false) {
163
+ throw new Error(`Invalid value for ${ImportOptions.ENABLE_JAVASCRIPT}: ${value}`);
164
+ }
165
+ },
166
+ [ImportOptions.PAGE_LOAD_TIMEOUT]: (value) => {
167
+ if (!isInteger(value) || value < 0) {
168
+ throw new Error(`Invalid value for ${ImportOptions.PAGE_LOAD_TIMEOUT}: ${value}`);
169
+ }
170
+ },
171
+ };
172
+
166
173
  const newState = { ...data };
167
174
 
175
+ // set default values for the start time if one is not provided
176
+ if (!hasText(newState.startTime)) {
177
+ newState.startTime = new Date().toISOString();
178
+ }
179
+
168
180
  if (!isValidUrl(newState.baseURL)) {
169
181
  throw new Error(`Invalid base URL: ${newState.baseURL}`);
170
182
  }
@@ -177,16 +189,30 @@ export const createImportJob = (data) => {
177
189
  throw new Error('"StartTime" should be a valid ISO string');
178
190
  }
179
191
 
180
- if (!hasText(newState.startTime)) {
181
- newState.startTime = new Date().toISOString();
182
- }
183
-
184
192
  if (!Object.values(ImportJobStatus).includes(newState.status)) {
185
193
  throw new Error(`Invalid Import Job status ${newState.status}`);
186
194
  }
187
195
 
188
- if (!isObject(newState.options)) {
189
- throw new Error(`Invalid options: ${newState.options}`);
196
+ if (newState.options) {
197
+ if (!isObject(newState.options)) {
198
+ throw new Error(`Invalid options: ${newState.options}`);
199
+ }
200
+
201
+ const invalidOptions = Object.keys(newState.options).filter(
202
+ (key) => !Object.values(ImportOptions)
203
+ .some((value) => value.toLowerCase() === key.toLowerCase()),
204
+ );
205
+
206
+ if (invalidOptions.length > 0) {
207
+ throw new Error(`Invalid options: ${invalidOptions}`);
208
+ }
209
+
210
+ // validate each option for it's expected data type
211
+ Object.keys(newState.options).forEach((key) => {
212
+ if (ImportOptionTypeValidator[key]) {
213
+ ImportOptionTypeValidator[key](data.options[key]);
214
+ }
215
+ });
190
216
  }
191
217
 
192
218
  return ImportJob(newState);
@@ -12,18 +12,12 @@
12
12
 
13
13
  import { hasText, isValidUrl } from '@adobe/spacecat-shared-utils';
14
14
  import { Base } from '../base.js';
15
- import { ImportJobStatus } from './import-job.js';
16
-
17
- export const ImportUrlStatus = {
18
- PENDING: 'PENDING',
19
- REDIRECT: 'REDIRECT',
20
- ...ImportJobStatus,
21
- };
15
+ import { ImportUrlStatus } from './import-constants.js';
22
16
 
23
17
  /**
24
18
  * Creates a new ImportUrl object
25
19
  *
26
- * @param {Object} importUrlData
20
+ * @param {Object} data
27
21
  * @returns {ImportUrl}
28
22
  */
29
23
  const ImportUrl = (data) => {
@@ -39,58 +33,53 @@ const ImportUrl = (data) => {
39
33
  self.getFile = () => self.state.file;
40
34
 
41
35
  /**
42
- * Updates the status of the ImportUrl
36
+ * Updates the state of the ImportJob.
37
+ * @param key - The key to update.
38
+ * @param value - The new value.
39
+ * @param {Function} validator - An optional validation function to use before updating the value.
40
+ * The validator can return false to indicate that the value isn't worth throwing an exception,
41
+ * but continue to use the previous value.
43
42
  */
44
- self.setStatus = (status) => {
45
- if (!Object.values(ImportUrlStatus).includes(status)) {
46
- throw new Error(`Invalid Import URL status during update: ${status}`);
43
+ const updateState = (key, value, validator) => {
44
+ if (validator && typeof validator === 'function') {
45
+ // a validator can return true or false to indicate if the value is valid
46
+ // however if a validator throws an error, it is considered critical and invalid.
47
+ if (!validator(value)) {
48
+ return self;
49
+ }
47
50
  }
48
51
 
49
- self.state.status = status;
52
+ self.state[key] = value;
50
53
  self.touch();
51
54
 
52
55
  return self;
53
56
  };
54
57
 
55
58
  /**
56
- * Updates the reason that the import of this URL was not successful
59
+ * Updates the status of the ImportUrl
57
60
  */
58
- self.setReason = (reason) => {
59
- if (!hasText(reason)) {
60
- return self; // no-op
61
+ self.setStatus = (status) => updateState('status', status, (value) => {
62
+ if (!ImportUrlStatus[value]) {
63
+ throw new Error(`Invalid Import URL status during update: ${value}`);
61
64
  }
65
+ return true;
66
+ });
62
67
 
63
- self.state.reason = reason;
64
- self.touch();
65
- return self;
66
- };
68
+ /**
69
+ * Updates the reason that the import of this URL was not successful.
70
+ */
71
+ self.setReason = (reason) => updateState('reason', reason, hasText);
67
72
 
68
73
  /**
69
74
  * Updates the path of the ImportUrl
70
75
  */
71
- self.setPath = (path) => {
72
- if (!hasText(path)) {
73
- return self; // no-op
74
- }
75
-
76
- self.state.path = path;
77
- self.touch();
78
- return self;
79
- };
76
+ self.setPath = (path) => updateState('path', path, hasText);
80
77
 
81
78
  /**
82
79
  * Updates the file of the ImportUrl. This is the path and file name of the file which
83
80
  * was imported.
84
81
  */
85
- self.setFile = (file) => {
86
- if (!hasText(file)) {
87
- return self; // no-op
88
- }
89
-
90
- self.state.file = file;
91
- self.touch();
92
- return self;
93
- };
82
+ self.setFile = (file) => updateState('file', file, hasText);
94
83
 
95
84
  return Object.freeze(self);
96
85
  };
@@ -40,7 +40,7 @@ export const getImportJobsByDateRange = async (dynamoClient, config, log, startD
40
40
  };
41
41
 
42
42
  /**
43
- * Get Import Job by ID
43
+ * Get Import Job by ID.
44
44
  * @param {DynamoClient} dynamoClient
45
45
  * @param {Object} config
46
46
  * @param {Logger} log
@@ -52,6 +52,7 @@ export const getImportJobByID = async (dynamoClient, config, log, id) => {
52
52
  config.tableNameImportJobs,
53
53
  { id },
54
54
  );
55
+
55
56
  return item ? ImportJobDto.fromDynamoItem(item) : null;
56
57
  };
57
58
 
@@ -64,7 +64,7 @@ export const updateImportUrl = async (dynamoClient, config, log, importUrl) => {
64
64
  );
65
65
 
66
66
  if (!isObject(existingImportUrl)) {
67
- throw new Error(`Import Url with ID:${importUrl.getId()} does not exist`);
67
+ throw new Error(`Import Url with ID: ${importUrl.getId()} does not exist`);
68
68
  }
69
69
 
70
70
  await dynamoClient.putItem(config.tableNameImportUrls, ImportUrlDto.toDynamoItem(importUrl));
@@ -98,12 +98,12 @@ export const getImportUrlsByJobIdAndStatus = async (dynamoClient, config, log, j
98
98
  };
99
99
 
100
100
  /**
101
- * Get Import Urls by Job ID
101
+ * Get Import Urls by Job ID, if no urls exist an empty array is returned.
102
102
  * @param {DynamoClient} dynamoClient
103
103
  * @param {Object} config
104
104
  * @param {Logger} log
105
105
  * @param {string} jobId
106
- * @returns {Promise<ImportUrl[]>}
106
+ * @returns {Promise<ImportUrlDto[]> | []}
107
107
  */
108
108
  export const getImportUrlsByJobId = async (dynamoClient, config, log, jobId) => {
109
109
  const items = await dynamoClient.query({
@@ -114,5 +114,6 @@ export const getImportUrlsByJobId = async (dynamoClient, config, log, jobId) =>
114
114
  ':jobId': jobId,
115
115
  },
116
116
  });
117
- return items.map((item) => ImportUrlDto.fromDynamoItem(item));
117
+
118
+ return items ? items.map((item) => ImportUrlDto.fromDynamoItem(item)) : [];
118
119
  };