@adobe/spacecat-shared-data-access 1.61.0 → 1.61.2

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +1 -1
  3. package/src/service/index.js +4 -3
  4. package/src/v2/models/api-key/api-key.schema.js +2 -0
  5. package/src/v2/models/audit/audit.model.js +56 -55
  6. package/src/v2/models/audit/audit.schema.js +2 -2
  7. package/src/v2/models/audit/index.d.ts +6 -2
  8. package/src/v2/models/base/base.collection.js +17 -0
  9. package/src/v2/models/base/index.d.ts +2 -1
  10. package/src/v2/models/configuration/configuration.schema.js +2 -2
  11. package/src/v2/models/experiment/experiment.model.js +2 -2
  12. package/src/v2/models/experiment/experiment.schema.js +14 -5
  13. package/src/v2/models/import-job/import-job.collection.js +1 -1
  14. package/src/v2/models/import-job/import-job.model.js +29 -29
  15. package/src/v2/models/import-job/import-job.schema.js +7 -7
  16. package/src/v2/models/import-url/import-url.model.js +2 -2
  17. package/src/v2/models/import-url/import-url.schema.js +4 -4
  18. package/src/v2/models/key-event/key-event.model.js +11 -11
  19. package/src/v2/models/key-event/key-event.schema.js +1 -2
  20. package/src/v2/models/latest-audit/index.d.ts +2 -2
  21. package/src/v2/models/latest-audit/latest-audit.schema.js +2 -2
  22. package/src/v2/models/opportunity/opportunity.model.js +13 -13
  23. package/src/v2/models/opportunity/opportunity.schema.js +3 -3
  24. package/src/v2/models/site/index.d.ts +1 -1
  25. package/src/v2/models/site/site.collection.js +46 -0
  26. package/src/v2/models/site/site.model.js +8 -8
  27. package/src/v2/models/site/site.schema.js +3 -6
  28. package/src/v2/models/site-candidate/site-candidate.model.js +15 -15
  29. package/src/v2/models/site-candidate/site-candidate.schema.js +3 -3
  30. package/src/v2/models/site-top-page/site-top-page.model.js +2 -2
  31. package/src/v2/models/site-top-page/site-top-page.schema.js +2 -2
  32. package/src/v2/models/suggestion/suggestion.collection.js +3 -3
  33. package/src/v2/models/suggestion/suggestion.model.js +15 -15
  34. package/src/v2/models/suggestion/suggestion.schema.js +4 -4
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [@adobe/spacecat-shared-data-access-v1.61.2](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.61.1...@adobe/spacecat-shared-data-access-v1.61.2) (2024-12-27)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * various missing ([#506](https://github.com/adobe/spacecat-shared/issues/506)) ([8fc72f0](https://github.com/adobe/spacecat-shared/commit/8fc72f0545eb17fb9ccdf0caa35872734f518883))
7
+
8
+ # [@adobe/spacecat-shared-data-access-v1.61.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.61.0...@adobe/spacecat-shared-data-access-v1.61.1) (2024-12-24)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * schema and db init ([#505](https://github.com/adobe/spacecat-shared/issues/505)) ([9b67b9a](https://github.com/adobe/spacecat-shared/commit/9b67b9a9f40f643c648cc5043018bfbc1d2a9c12))
14
+
1
15
  # [@adobe/spacecat-shared-data-access-v1.61.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.60.3...@adobe/spacecat-shared-data-access-v1.61.0) (2024-12-23)
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.61.0",
3
+ "version": "1.61.2",
4
4
  "description": "Shared modules of the Spacecat Services - Data Access",
5
5
  "type": "module",
6
6
  "engines": {
@@ -31,8 +31,8 @@ import { importUrlFunctions } from './import-url/index.js';
31
31
  import { experimentFunctions } from './experiments/index.js';
32
32
  import { apiKeyFunctions } from './api-key/index.js';
33
33
 
34
- const createRawClient = () => {
35
- const dbClient = AWSXray.captureAWSv3Client(new DynamoDB());
34
+ const createRawClient = (client = undefined) => {
35
+ const dbClient = client || AWSXray.captureAWSv3Client(new DynamoDB());
36
36
  return DynamoDBDocument.from(dbClient, {
37
37
  marshallOptions: {
38
38
  convertEmptyValues: true,
@@ -70,6 +70,7 @@ const createElectroService = (client, config, log) => {
70
70
  * tableNameSiteTopPages: string, indexNameAllOrganizations: string,
71
71
  * indexNameAllOrganizationsByImsOrgId: string, pkAllOrganizations: string}} config configuration
72
72
  * @param {Logger} log log
73
+ * @param client custom dynamo client
73
74
  * @returns {object} data access object
74
75
  */
75
76
  export const createDataAccess = (config, log = console, client = undefined) => {
@@ -88,7 +89,7 @@ export const createDataAccess = (config, log = console, client = undefined) => {
88
89
  const apiKeyFuncs = apiKeyFunctions(dynamoClient, config, log);
89
90
 
90
91
  // electro-based data access objects
91
- const rawClient = createRawClient();
92
+ const rawClient = createRawClient(client);
92
93
  const electroService = createElectroService(rawClient, config, log);
93
94
  const entityRegistry = new EntityRegistry(electroService, log);
94
95
  const collections = entityRegistry.getCollections();
@@ -31,9 +31,11 @@ const schema = new SchemaBuilder(ApiKey, ApiKeyCollection)
31
31
  })
32
32
  .addAttribute('imsUserId', {
33
33
  type: 'string',
34
+ default: 'default',
34
35
  })
35
36
  .addAttribute('imsOrgId', {
36
37
  type: 'string',
38
+ default: 'default',
37
39
  })
38
40
  .addAttribute('name', {
39
41
  type: 'string',
@@ -15,71 +15,72 @@ import { isObject } from '@adobe/spacecat-shared-utils';
15
15
  import { ValidationError } from '../../errors/index.js';
16
16
  import BaseModel from '../base/base.model.js';
17
17
 
18
- const AUDIT_TYPES = {
19
- 404: '404',
20
- BROKEN_BACKLINKS: 'broken-backlinks',
21
- EXPERIMENTATION: 'experimentation',
22
- ORGANIC_KEYWORDS: 'organic-keywords',
23
- ORGANIC_TRAFFIC: 'organic-traffic',
24
- CWV: 'cwv',
25
- LHS_DESKTOP: 'lhs-desktop',
26
- LHS_MOBILE: 'lhs-mobile',
27
- EXPERIMENTATION_ESS_MONTHLY: 'experimentation-ess-monthly',
28
- EXPERIMENTATION_ESS_DAILY: 'experimentation-ess-daily',
29
- };
18
+ /**
19
+ * Audit - A class representing an Audit entity.
20
+ * Provides methods to access and manipulate Audit-specific data.
21
+ *
22
+ * @class Audit
23
+ * @extends BaseModel
24
+ */
25
+ class Audit extends BaseModel {
26
+ static AUDIT_TYPES = {
27
+ 404: '404',
28
+ BROKEN_BACKLINKS: 'broken-backlinks',
29
+ EXPERIMENTATION: 'experimentation',
30
+ ORGANIC_KEYWORDS: 'organic-keywords',
31
+ ORGANIC_TRAFFIC: 'organic-traffic',
32
+ CWV: 'cwv',
33
+ LHS_DESKTOP: 'lhs-desktop',
34
+ LHS_MOBILE: 'lhs-mobile',
35
+ EXPERIMENTATION_ESS_MONTHLY: 'experimentation-ess-monthly',
36
+ EXPERIMENTATION_ESS_DAILY: 'experimentation-ess-daily',
37
+ };
30
38
 
31
- const AUDIT_TYPE_PROPERTIES = {
32
- [AUDIT_TYPES.LHS_DESKTOP]: ['performance', 'seo', 'accessibility', 'best-practices'],
33
- [AUDIT_TYPES.LHS_MOBILE]: ['performance', 'seo', 'accessibility', 'best-practices'],
34
- };
39
+ static AUDIT_TYPE_PROPERTIES = {
40
+ [Audit.AUDIT_TYPES.LHS_DESKTOP]: ['performance', 'seo', 'accessibility', 'best-practices'],
41
+ [Audit.AUDIT_TYPES.LHS_MOBILE]: ['performance', 'seo', 'accessibility', 'best-practices'],
42
+ };
35
43
 
36
- export const AUDIT_CONFIG = {
37
- TYPES: AUDIT_TYPES,
38
- PROPERTIES: AUDIT_TYPE_PROPERTIES,
39
- };
44
+ static AUDIT_CONFIG = {
45
+ TYPES: Audit.AUDIT_TYPES,
46
+ PROPERTIES: Audit.AUDIT_TYPE_PROPERTIES,
47
+ };
40
48
 
41
- /**
42
- * Validates if the auditResult contains the required properties for the given audit type.
43
- * @param {object} auditResult - The audit result to validate.
44
- * @param {string} auditType - The type of the audit.
45
- * @returns {boolean} - True if valid, false otherwise.
46
- */
47
- export const validateAuditResult = (auditResult, auditType) => {
48
- if (!isObject(auditResult) && !Array.isArray(auditResult)) {
49
- throw new ValidationError('Audit result must be an object or array');
50
- }
49
+ /**
50
+ * Validates if the auditResult contains the required properties for the given audit type.
51
+ * @param {object} auditResult - The audit result to validate.
52
+ * @param {string} auditType - The type of the audit.
53
+ * @returns {boolean} - True if valid, false otherwise.
54
+ */
55
+ static validateAuditResult = (auditResult, auditType) => {
56
+ if (!isObject(auditResult) && !Array.isArray(auditResult)) {
57
+ throw new ValidationError('Audit result must be an object or array');
58
+ }
51
59
 
52
- if (isObject(auditResult.runtimeError)) {
53
- return true;
54
- }
60
+ if (isObject(auditResult.runtimeError)) {
61
+ return true;
62
+ }
55
63
 
56
- if ((auditType === AUDIT_CONFIG.TYPES.LHS_MOBILE || auditType === AUDIT_CONFIG.TYPES.LHS_DESKTOP)
57
- && !isObject(auditResult.scores)) {
58
- throw new ValidationError(`Missing scores property for audit type '${auditType}'`);
59
- }
64
+ if ((
65
+ auditType === Audit.AUDIT_CONFIG.TYPES.LHS_MOBILE
66
+ || auditType === Audit.AUDIT_CONFIG.TYPES.LHS_DESKTOP
67
+ )
68
+ && !isObject(auditResult.scores)) {
69
+ throw new ValidationError(`Missing scores property for audit type '${auditType}'`);
70
+ }
60
71
 
61
- const expectedProperties = AUDIT_CONFIG.PROPERTIES[auditType];
72
+ const expectedProperties = Audit.AUDIT_CONFIG.PROPERTIES[auditType];
62
73
 
63
- if (expectedProperties) {
64
- for (const prop of expectedProperties) {
65
- if (!(prop in auditResult.scores)) {
66
- throw new ValidationError(`Missing expected property '${prop}' for audit type '${auditType}'`);
74
+ if (expectedProperties) {
75
+ for (const prop of expectedProperties) {
76
+ if (!(prop in auditResult.scores)) {
77
+ throw new ValidationError(`Missing expected property '${prop}' for audit type '${auditType}'`);
78
+ }
67
79
  }
68
80
  }
69
- }
70
81
 
71
- return true;
72
- };
73
-
74
- /**
75
- * Audit - A class representing an Audit entity.
76
- * Provides methods to access and manipulate Audit-specific data.
77
- *
78
- * @class Audit
79
- * @extends BaseModel
80
- */
81
- class Audit extends BaseModel {
82
- // add your custom methods or overrides here
82
+ return true;
83
+ };
83
84
 
84
85
  getScores() {
85
86
  return this.getAuditResult()?.scores;
@@ -15,7 +15,7 @@
15
15
  import { isIsoDate, isNonEmptyObject } from '@adobe/spacecat-shared-utils';
16
16
 
17
17
  import SchemaBuilder from '../base/schema.builder.js';
18
- import Audit, { validateAuditResult } from './audit.model.js';
18
+ import Audit from './audit.model.js';
19
19
  import AuditCollection from './audit.collection.js';
20
20
 
21
21
  /*
@@ -37,7 +37,7 @@ const schema = new SchemaBuilder(Audit, AuditCollection)
37
37
  set: (value, attributes) => {
38
38
  // as the electroDb validate function does not provide access to the model instance
39
39
  // we need to call the validate function from the model on setting the value
40
- validateAuditResult(value, attributes.auditType);
40
+ Audit.validateAuditResult(value, attributes.auditType);
41
41
  return value;
42
42
  },
43
43
  })
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import type {
14
- BaseCollection, BaseModel, LatestAudit, Opportunity, Site,
14
+ BaseCollection, BaseModel, LatestAudit, Opportunity, QueryOptions, Site,
15
15
  } from '../index';
16
16
 
17
17
  export interface Audit extends BaseModel {
@@ -33,7 +33,11 @@ export interface Audit extends BaseModel {
33
33
 
34
34
  export interface AuditCollection extends BaseCollection<Audit> {
35
35
  allBySiteId(siteId: string): Promise<Audit[]>;
36
- allBySiteIdAndAuditType(siteId: string, auditType: string): Promise<Audit[]>;
36
+ allBySiteIdAndAuditType(
37
+ siteId: string,
38
+ auditType: string,
39
+ options?: QueryOptions
40
+ ): Promise<Audit[]>;
37
41
  allBySiteIdAndAuditTypeAndAuditedAt(
38
42
  siteId: string, auditType: string, auditedAt: string
39
43
  ): Promise<Audit[]>;
@@ -326,6 +326,23 @@ class BaseCollection {
326
326
  return this.#createInstance(record?.data);
327
327
  }
328
328
 
329
+ /**
330
+ * Checks if an entity exists by its ID.
331
+ * @param {string} id - The UUID of the entity to check.
332
+ * @return {Promise<boolean>} - A promise that resolves to true if the entity exists,
333
+ * otherwise false.
334
+ * @throws {ValidationError} - Throws an error if the ID is not provided.
335
+ */
336
+ async existsById(id) {
337
+ guardId(this.idName, id, this.entityName);
338
+
339
+ const record = await this.entity.get({ [this.idName]: id }).go({
340
+ attributes: [this.idName],
341
+ });
342
+
343
+ return isNonEmptyObject(record?.data);
344
+ }
345
+
329
346
  /**
330
347
  * Finds a single entity by index keys.
331
348
  * @param {Object} keys - The index keys to use for the query.
@@ -30,7 +30,7 @@ export interface BaseModel {
30
30
  export interface QueryOptions {
31
31
  index?: string;
32
32
  limit?: number;
33
- sort?: string;
33
+ order?: string;
34
34
  attributes?: string[];
35
35
  }
36
36
 
@@ -42,6 +42,7 @@ export interface BaseCollection<T extends BaseModel> {
42
42
  allByIndexKeys(keys: object, options?: QueryOptions): Promise<T[]>;
43
43
  create(item: object): Promise<T>;
44
44
  createMany(items: object[], parent?: T): Promise<MultiStatusCreateResult<T>>;
45
+ existsById(id: string): Promise<boolean>;
45
46
  findByAll(sortKeys?: object, options?: QueryOptions): Promise<T> | null;
46
47
  findById(id: string): Promise<T> | null;
47
48
  findByIndexKeys(indexKeys: object): Promise<T>;
@@ -78,9 +78,9 @@ const schema = new SchemaBuilder(Configuration, ConfigurationCollection)
78
78
  items: {
79
79
  type: 'map',
80
80
  properties: {
81
- group: { type: ['audits', 'imports', 'reports'] },
81
+ group: { type: ['audits', 'imports', 'reports', 'scrapes'] },
82
82
  type: { type: 'string', required: true },
83
- interval: { type: ['daily', 'weekly'] },
83
+ interval: { type: ['daily', 'weekly', 'every-saturday', 'every-sunday', 'fortnightly', 'monthly'] },
84
84
  },
85
85
  },
86
86
  })
@@ -12,8 +12,6 @@
12
12
 
13
13
  import BaseModel from '../base/base.model.js';
14
14
 
15
- export const DEFAULT_UPDATED_BY = 'spacecat';
16
-
17
15
  /**
18
16
  * Experiment - A class representing an Experiment entity.
19
17
  * Provides methods to access and manipulate Experiment-specific data.
@@ -22,6 +20,8 @@ export const DEFAULT_UPDATED_BY = 'spacecat';
22
20
  * @extends BaseModel
23
21
  */
24
22
  class Experiment extends BaseModel {
23
+ static DEFAULT_UPDATED_BY = 'spacecat';
24
+
25
25
  // add your custom methods or overrides here
26
26
  }
27
27
 
@@ -12,10 +12,12 @@
12
12
 
13
13
  /* c8 ignore start */
14
14
 
15
- import { isIsoDate, isNonEmptyObject, isValidUrl } from '@adobe/spacecat-shared-utils';
15
+ import {
16
+ isIsoDate, isNonEmptyObject, isString, isValidUrl,
17
+ } from '@adobe/spacecat-shared-utils';
16
18
 
17
19
  import SchemaBuilder from '../base/schema.builder.js';
18
- import Experiment, { DEFAULT_UPDATED_BY } from './experiment.model.js';
20
+ import Experiment from './experiment.model.js';
19
21
  import ExperimentCollection from './experiment.collection.js';
20
22
 
21
23
  /*
@@ -28,8 +30,12 @@ const schema = new SchemaBuilder(Experiment, ExperimentCollection)
28
30
  .addReference('belongs_to', 'Site', ['expId', 'url', 'updatedAt'])
29
31
  .addAttribute('conversionEventName', {
30
32
  type: 'string',
33
+ validate: (value) => !value || isString(value),
34
+ })
35
+ .addAttribute('conversionEventValue', {
36
+ type: 'string',
37
+ validate: (value) => !value || isString(value),
31
38
  })
32
- .addAttribute('conversionEventValue', { type: 'string' })
33
39
  .addAttribute('endDate', {
34
40
  type: 'string',
35
41
  validate: (value) => !value || isIsoDate(value),
@@ -47,7 +53,10 @@ const schema = new SchemaBuilder(Experiment, ExperimentCollection)
47
53
  type: ['ACTIVE', 'INACTIVE'],
48
54
  required: true,
49
55
  })
50
- .addAttribute('type', { type: 'string' })
56
+ .addAttribute('type', {
57
+ type: 'string',
58
+ validate: (value) => !value || isString(value),
59
+ })
51
60
  .addAttribute('url', {
52
61
  type: 'string',
53
62
  required: true,
@@ -56,7 +65,7 @@ const schema = new SchemaBuilder(Experiment, ExperimentCollection)
56
65
  .addAttribute('updatedBy', {
57
66
  type: 'string',
58
67
  required: true,
59
- default: DEFAULT_UPDATED_BY,
68
+ default: Experiment.DEFAULT_UPDATED_BY,
60
69
  })
61
70
  .addAttribute('variants', {
62
71
  type: 'list',
@@ -12,8 +12,8 @@
12
12
 
13
13
  import { isIsoDate } from '@adobe/spacecat-shared-utils';
14
14
 
15
- import BaseCollection from '../base/base.collection.js';
16
15
  import { ValidationError } from '../../errors/index.js';
16
+ import BaseCollection from '../base/base.collection.js';
17
17
 
18
18
  /**
19
19
  * ImportJobCollection - A collection class responsible for managing ImportJob entities.
@@ -12,35 +12,6 @@
12
12
 
13
13
  import BaseModel from '../base/base.model.js';
14
14
 
15
- /**
16
- * Supported Import Options.
17
- */
18
- export const ImportOptions = {
19
- ENABLE_JAVASCRIPT: 'enableJavascript',
20
- PAGE_LOAD_TIMEOUT: 'pageLoadTimeout',
21
- };
22
-
23
- /**
24
- * Import Job Status types.
25
- * Any changes to this object needs to be reflected in the index.d.ts file as well.
26
- */
27
- export const ImportJobStatus = {
28
- RUNNING: 'RUNNING',
29
- COMPLETE: 'COMPLETE',
30
- FAILED: 'FAILED',
31
- STOPPED: 'STOPPED',
32
- };
33
-
34
- /**
35
- * ImportURL Status types.
36
- * Any changes to this object needs to be reflected in the index.d.ts file as well.
37
- */
38
- export const ImportUrlStatus = {
39
- PENDING: 'PENDING',
40
- REDIRECT: 'REDIRECT',
41
- ...ImportJobStatus,
42
- };
43
-
44
15
  /**
45
16
  * ImportJob - A class representing an ImportJob entity.
46
17
  * Provides methods to access and manipulate ImportJob-specific data.
@@ -49,6 +20,35 @@ export const ImportUrlStatus = {
49
20
  * @extends BaseModel
50
21
  */
51
22
  class ImportJob extends BaseModel {
23
+ /**
24
+ * Import Job Status types.
25
+ * Any changes to this object needs to be reflected in the index.d.ts file as well.
26
+ */
27
+ static ImportJobStatus = {
28
+ RUNNING: 'RUNNING',
29
+ COMPLETE: 'COMPLETE',
30
+ FAILED: 'FAILED',
31
+ STOPPED: 'STOPPED',
32
+ };
33
+
34
+ /**
35
+ * ImportURL Status types.
36
+ * Any changes to this object needs to be reflected in the index.d.ts file as well.
37
+ */
38
+ static ImportUrlStatus = {
39
+ PENDING: 'PENDING',
40
+ REDIRECT: 'REDIRECT',
41
+ ...ImportJob.ImportJobStatus,
42
+ };
43
+
44
+ /**
45
+ * Supported Import Options.
46
+ */
47
+ static ImportOptions = {
48
+ ENABLE_JAVASCRIPT: 'enableJavascript',
49
+ PAGE_LOAD_TIMEOUT: 'pageLoadTimeout',
50
+ };
51
+
52
52
  // add your custom methods or overrides here
53
53
  }
54
54
 
@@ -21,18 +21,18 @@ import {
21
21
  } from '@adobe/spacecat-shared-utils';
22
22
 
23
23
  import SchemaBuilder from '../base/schema.builder.js';
24
- import ImportJob, { ImportJobStatus, ImportOptions } from './import-job.model.js';
24
+ import ImportJob from './import-job.model.js';
25
25
  import ImportJobCollection from './import-job.collection.js';
26
26
 
27
27
  const ImportOptionTypeValidator = {
28
- [ImportOptions.ENABLE_JAVASCRIPT]: (value) => {
28
+ [ImportJob.ImportOptions.ENABLE_JAVASCRIPT]: (value) => {
29
29
  if (value !== true && value !== false) {
30
- throw new Error(`Invalid value for ${ImportOptions.ENABLE_JAVASCRIPT}: ${value}`);
30
+ throw new Error(`Invalid value for ${ImportJob.ImportOptions.ENABLE_JAVASCRIPT}: ${value}`);
31
31
  }
32
32
  },
33
- [ImportOptions.PAGE_LOAD_TIMEOUT]: (value) => {
33
+ [ImportJob.ImportOptions.PAGE_LOAD_TIMEOUT]: (value) => {
34
34
  if (!isInteger(value) || value < 0) {
35
- throw new Error(`Invalid value for ${ImportOptions.PAGE_LOAD_TIMEOUT}: ${value}`);
35
+ throw new Error(`Invalid value for ${ImportJob.ImportOptions.PAGE_LOAD_TIMEOUT}: ${value}`);
36
36
  }
37
37
  },
38
38
  };
@@ -43,7 +43,7 @@ const validateOptions = (options) => {
43
43
  }
44
44
 
45
45
  const invalidOptions = Object.keys(options).filter(
46
- (key) => !Object.values(ImportOptions)
46
+ (key) => !Object.values(ImportJob.ImportOptions)
47
47
  .some((value) => value.toLowerCase() === key.toLowerCase()),
48
48
  );
49
49
 
@@ -122,7 +122,7 @@ const schema = new SchemaBuilder(ImportJob, ImportJobCollection)
122
122
  validate: (value) => !value || isInteger(value),
123
123
  })
124
124
  .addAttribute('status', {
125
- type: Object.values(ImportJobStatus),
125
+ type: Object.values(ImportJob.ImportJobStatus),
126
126
  required: true,
127
127
  })
128
128
  .addAttribute('startedAt', {
@@ -12,8 +12,6 @@
12
12
 
13
13
  import BaseModel from '../base/base.model.js';
14
14
 
15
- export const IMPORT_URL_EXPIRES_IN_DAYS = 30;
16
-
17
15
  /**
18
16
  * ImportUrl - A class representing an ImportUrl entity.
19
17
  * Provides methods to access and manipulate ImportUrl-specific data.
@@ -22,6 +20,8 @@ export const IMPORT_URL_EXPIRES_IN_DAYS = 30;
22
20
  * @extends BaseModel
23
21
  */
24
22
  class ImportUrl extends BaseModel {
23
+ static IMPORT_URL_EXPIRES_IN_DAYS = 30;
24
+
25
25
  // add your custom methods or overrides here
26
26
  }
27
27
 
@@ -14,10 +14,10 @@
14
14
 
15
15
  import { isIsoDate, isValidUrl } from '@adobe/spacecat-shared-utils';
16
16
 
17
- import { ImportUrlStatus } from '../import-job/import-job.model.js';
18
17
  import SchemaBuilder from '../base/schema.builder.js';
19
- import ImportUrl, { IMPORT_URL_EXPIRES_IN_DAYS } from './import-url.model.js';
18
+ import ImportUrl from './import-url.model.js';
20
19
  import ImportUrlCollection from './import-url.collection.js';
20
+ import { ImportJob } from '../import-job/index.js';
21
21
 
22
22
  /*
23
23
  Schema Doc: https://electrodb.dev/en/modeling/schema/
@@ -33,7 +33,7 @@ const schema = new SchemaBuilder(ImportUrl, ImportUrlCollection)
33
33
  validate: (value) => isIsoDate(value),
34
34
  default: () => {
35
35
  const date = new Date();
36
- date.setDate(date.getDate() + IMPORT_URL_EXPIRES_IN_DAYS);
36
+ date.setDate(date.getDate() + ImportUrl.IMPORT_URL_EXPIRES_IN_DAYS);
37
37
  return date.toISOString();
38
38
  },
39
39
  })
@@ -47,7 +47,7 @@ const schema = new SchemaBuilder(ImportUrl, ImportUrlCollection)
47
47
  type: 'string',
48
48
  })
49
49
  .addAttribute('status', {
50
- type: Object.values(ImportUrlStatus),
50
+ type: Object.values(ImportJob.ImportUrlStatus),
51
51
  required: true,
52
52
  })
53
53
  .addAttribute('url', {
@@ -12,17 +12,6 @@
12
12
 
13
13
  import BaseModel from '../base/base.model.js';
14
14
 
15
- export const KEY_EVENT_TYPES = {
16
- PERFORMANCE: 'PERFORMANCE',
17
- SEO: 'SEO',
18
- CONTENT: 'CONTENT',
19
- CODE: 'CODE',
20
- THIRD_PARTY: 'THIRD PARTY',
21
- EXPERIMENTATION: 'EXPERIMENTATION',
22
- NETWORK: 'NETWORK',
23
- STATUS_CHANGE: 'STATUS CHANGE',
24
- };
25
-
26
15
  /**
27
16
  * KeyEvent - A class representing an KeyEvent entity.
28
17
  * Provides methods to access and manipulate KeyEvent-specific data.
@@ -31,6 +20,17 @@ export const KEY_EVENT_TYPES = {
31
20
  * @extends BaseModel
32
21
  */
33
22
  class KeyEvent extends BaseModel {
23
+ static KEY_EVENT_TYPES = {
24
+ PERFORMANCE: 'PERFORMANCE',
25
+ SEO: 'SEO',
26
+ CONTENT: 'CONTENT',
27
+ CODE: 'CODE',
28
+ THIRD_PARTY: 'THIRD PARTY',
29
+ EXPERIMENTATION: 'EXPERIMENTATION',
30
+ NETWORK: 'NETWORK',
31
+ STATUS_CHANGE: 'STATUS CHANGE',
32
+ };
33
+
34
34
  // add your custom methods or overrides here
35
35
  }
36
36
 
@@ -15,7 +15,6 @@
15
15
  import { hasText, isIsoDate } from '@adobe/spacecat-shared-utils';
16
16
 
17
17
  import SchemaBuilder from '../base/schema.builder.js';
18
- import { KEY_EVENT_TYPES } from '../../../models/key-event.js';
19
18
  import KeyEvent from './key-event.model.js';
20
19
  import KeyEventCollection from './key-event.collection.js';
21
20
 
@@ -33,7 +32,7 @@ const schema = new SchemaBuilder(KeyEvent, KeyEventCollection)
33
32
  validate: (value) => hasText(value),
34
33
  })
35
34
  .addAttribute('type', {
36
- type: Object.values(KEY_EVENT_TYPES),
35
+ type: Object.values(KeyEvent.KEY_EVENT_TYPES),
37
36
  required: true,
38
37
  })
39
38
  .addAttribute('time', {
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import type {
14
- Audit, BaseCollection, BaseModel, Opportunity, Site,
14
+ Audit, BaseCollection, BaseModel, Opportunity, QueryOptions, Site,
15
15
  } from '../index';
16
16
 
17
17
  export interface LatestAudit extends BaseModel {
@@ -32,7 +32,7 @@ export interface LatestAudit extends BaseModel {
32
32
  export interface LatestAuditCollection extends BaseCollection<LatestAudit> {
33
33
  allByAuditId(auditId: string): Promise<LatestAudit[]>;
34
34
  allByAuditIdAndAuditType(auditId: string, auditType: string): Promise<LatestAudit[]>;
35
- allByAuditType(auditType: string): Promise<LatestAudit[]>;
35
+ allByAuditType(auditType: string, options?: QueryOptions): Promise<LatestAudit[]>;
36
36
  allBySiteId(siteId: string): Promise<LatestAudit[]>;
37
37
  allBySiteIdAndAuditType(siteId: string, auditType: string): Promise<LatestAudit[]>;
38
38
  findByAuditId(auditId: string): Promise<LatestAudit | null>;
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { isIsoDate, isNonEmptyObject } from '@adobe/spacecat-shared-utils';
16
16
 
17
- import { validateAuditResult } from '../audit/audit.model.js';
17
+ import Audit from '../audit/audit.model.js';
18
18
  import SchemaBuilder from '../base/schema.builder.js';
19
19
  import LatestAudit from './latest-audit.model.js';
20
20
  import LatestAuditCollection from './latest-audit.collection.js';
@@ -41,7 +41,7 @@ const schema = new SchemaBuilder(LatestAudit, LatestAuditCollection)
41
41
  set: (value, attributes) => {
42
42
  // as the electroDb validate function does not provide access to the model instance
43
43
  // we need to call the validate function from the model on setting the value
44
- validateAuditResult(value, attributes.auditType);
44
+ Audit.validateAuditResult(value, attributes.auditType);
45
45
  return value;
46
46
  },
47
47
  })
@@ -12,19 +12,6 @@
12
12
 
13
13
  import BaseModel from '../base/base.model.js';
14
14
 
15
- export const ORIGINS = {
16
- ESS_OPS: 'ESS_OPS',
17
- AI: 'AI',
18
- AUTOMATION: 'AUTOMATION',
19
- };
20
-
21
- export const STATUSES = {
22
- NEW: 'NEW',
23
- IN_PROGRESS: 'IN_PROGRESS',
24
- IGNORED: 'IGNORED',
25
- RESOLVED: 'RESOLVED',
26
- };
27
-
28
15
  /**
29
16
  * Opportunity - A class representing an Opportunity entity.
30
17
  * Provides methods to access and manipulate Opportunity-specific data,
@@ -35,6 +22,19 @@ export const STATUSES = {
35
22
  */
36
23
 
37
24
  class Opportunity extends BaseModel {
25
+ static ORIGINS = {
26
+ ESS_OPS: 'ESS_OPS',
27
+ AI: 'AI',
28
+ AUTOMATION: 'AUTOMATION',
29
+ };
30
+
31
+ static STATUSES = {
32
+ NEW: 'NEW',
33
+ IN_PROGRESS: 'IN_PROGRESS',
34
+ IGNORED: 'IGNORED',
35
+ RESOLVED: 'RESOLVED',
36
+ };
37
+
38
38
  /**
39
39
  * Adds the given suggestions to this Opportunity. Sets this opportunity as the parent
40
40
  * of each suggestion, as such the opportunity ID does not need to be provided.
@@ -15,7 +15,7 @@
15
15
  import { isNonEmptyObject, isValidUrl } from '@adobe/spacecat-shared-utils';
16
16
 
17
17
  import SchemaBuilder from '../base/schema.builder.js';
18
- import Opportunity, { ORIGINS, STATUSES } from './opportunity.model.js';
18
+ import Opportunity from './opportunity.model.js';
19
19
  import OpportunityCollection from './opportunity.collection.js';
20
20
 
21
21
  /*
@@ -43,7 +43,7 @@ const schema = new SchemaBuilder(Opportunity, OpportunityCollection)
43
43
  validate: (value) => !value || isNonEmptyObject(value),
44
44
  })
45
45
  .addAttribute('origin', {
46
- type: Object.values(ORIGINS),
46
+ type: Object.values(Opportunity.ORIGINS),
47
47
  required: true,
48
48
  })
49
49
  .addAttribute('title', {
@@ -54,7 +54,7 @@ const schema = new SchemaBuilder(Opportunity, OpportunityCollection)
54
54
  type: 'string',
55
55
  })
56
56
  .addAttribute('status', {
57
- type: Object.values(STATUSES),
57
+ type: Object.values(Opportunity.STATUSES),
58
58
  required: true,
59
59
  default: 'NEW',
60
60
  })
@@ -43,7 +43,6 @@ export interface Site extends BaseModel {
43
43
  getKeyEvents(): Promise<KeyEvent[]>
44
44
  getKeyEventsByTimestamp(timestamp: string): Promise<KeyEvent[]>
45
45
  getLatestAudit(): Promise<LatestAudit>;
46
- getLatestAuditByAuditType(auditType: string): Promise<LatestAudit>;
47
46
  getLatestAudits(): Promise<LatestAudit>;
48
47
  getLatestAuditByAuditType(auditType: string): Promise<LatestAudit>;
49
48
  getOpportunities(): Promise<Opportunity[]>;
@@ -74,6 +73,7 @@ export interface SiteCollection extends BaseCollection<Organization> {
74
73
  allByDeliveryType(deliveryType: string): Promise<Site[]>;
75
74
  allByOrganizationId(organizationId: string): Promise<Site[]>;
76
75
  allSitesToAudit(): Promise<string[]>;
76
+ allWithLatestAudit(auditType: string, order?: string, deliveryType?: string): Promise<Site[]>;
77
77
  findByBaseURL(baseURL: string): Promise<Site | null>;
78
78
  findByDeliveryType(deliveryType: string): Promise<Site | null>;
79
79
  findByOrganizationId(organizationId: string): Promise<Site | null>;
@@ -10,8 +10,13 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
+ import { hasText } from '@adobe/spacecat-shared-utils';
14
+
15
+ import DataAccessError from '../../errors/data-access.error.js';
13
16
  import BaseCollection from '../base/base.collection.js';
14
17
 
18
+ import Site from './site.model.js';
19
+
15
20
  /**
16
21
  * SiteCollection - A collection class responsible for managing Site entities.
17
22
  * Extends the BaseCollection to provide specific methods for interacting with Site records.
@@ -23,6 +28,47 @@ class SiteCollection extends BaseCollection {
23
28
  async allSitesToAudit() {
24
29
  return (await this.all({ attributes: ['siteId'] })).map((site) => site.getId());
25
30
  }
31
+
32
+ async allWithLatestAudit(auditType, order = 'asc', deliveryType = null) {
33
+ if (!hasText(auditType)) {
34
+ throw new DataAccessError('auditType is required', this);
35
+ }
36
+
37
+ const latestAuditCollection = this.entityRegistry.getCollection('LatestAuditCollection');
38
+
39
+ const sitesQuery = Object.values(Site.DELIVERY_TYPES)
40
+ .includes(deliveryType)
41
+ ? this.allByDeliveryType(deliveryType)
42
+ : this.all();
43
+
44
+ const [sites, latestAudits] = await Promise.all([
45
+ sitesQuery,
46
+ latestAuditCollection.all([auditType], { order }),
47
+ ]);
48
+
49
+ const sitesMap = new Map(sites.map((site) => [site.getId(), site]));
50
+ const orderedSites = [];
51
+
52
+ // First, append sites with a latest audit in the sorted order
53
+ latestAudits.forEach((audit) => {
54
+ const site = sitesMap.get(audit.getSiteId());
55
+ if (site) {
56
+ // eslint-disable-next-line no-underscore-dangle
57
+ site._accessorCache['getLatestAuditByAuditType:_'] = audit;
58
+ orderedSites.push(site);
59
+ sitesMap.delete(site.getId()); // Remove the site from the map to avoid adding it again
60
+ }
61
+ });
62
+
63
+ // Then, append the remaining sites (without a latest audit)
64
+ sitesMap.forEach((site) => {
65
+ // eslint-disable-next-line no-underscore-dangle,no-param-reassign
66
+ site._accessorCache['getLatestAuditByAuditType:_'] = null;
67
+ orderedSites.push(site);
68
+ });
69
+
70
+ return orderedSites;
71
+ }
26
72
  }
27
73
 
28
74
  export default SiteCollection;
@@ -13,20 +13,20 @@
13
13
  import { Config } from '../../../models/site/config.js';
14
14
  import BaseModel from '../base/base.model.js';
15
15
 
16
- export const DELIVERY_TYPES = {
17
- AEM_CS: 'aem_cs',
18
- AEM_EDGE: 'aem_edge',
19
- OTHER: 'other',
20
- };
21
-
22
- export const DEFAULT_DELIVERY_TYPE = DELIVERY_TYPES.AEM_EDGE;
23
-
24
16
  /**
25
17
  * A class representing a Site entity. Provides methods to access and manipulate Site-specific data.
26
18
  * @class Site
27
19
  * @extends BaseModel
28
20
  */
29
21
  class Site extends BaseModel {
22
+ static DELIVERY_TYPES = {
23
+ AEM_CS: 'aem_cs',
24
+ AEM_EDGE: 'aem_edge',
25
+ OTHER: 'other',
26
+ };
27
+
28
+ static DEFAULT_DELIVERY_TYPE = Site.DELIVERY_TYPES.AEM_EDGE;
29
+
30
30
  getConfig() {
31
31
  return Config(this.record.config);
32
32
  }
@@ -22,10 +22,7 @@ import {
22
22
  import { Config, DEFAULT_CONFIG, validateConfiguration } from '../../../models/site/config.js';
23
23
  import SchemaBuilder from '../base/schema.builder.js';
24
24
 
25
- import Site, {
26
- DEFAULT_DELIVERY_TYPE,
27
- DELIVERY_TYPES,
28
- } from './site.model.js';
25
+ import Site from './site.model.js';
29
26
  import SiteCollection from './site.collection.js';
30
27
 
31
28
  /*
@@ -59,8 +56,8 @@ const schema = new SchemaBuilder(Site, SiteCollection)
59
56
  get: (value) => Config(value),
60
57
  })
61
58
  .addAttribute('deliveryType', {
62
- type: Object.values(DELIVERY_TYPES),
63
- default: DEFAULT_DELIVERY_TYPE,
59
+ type: Object.values(Site.DELIVERY_TYPES),
60
+ default: Site.DEFAULT_DELIVERY_TYPE,
64
61
  required: true,
65
62
  })
66
63
  .addAttribute('gitHubURL', {
@@ -12,21 +12,6 @@
12
12
 
13
13
  import BaseModel from '../base/base.model.js';
14
14
 
15
- export const DEFAULT_UPDATED_BY = 'spacecat';
16
-
17
- export const SITE_CANDIDATE_SOURCES = {
18
- SPACECAT_SLACK_BOT: 'SPACECAT_SLACK_BOT',
19
- RUM: 'RUM',
20
- CDN: 'CDN',
21
- };
22
-
23
- export const SITE_CANDIDATE_STATUS = {
24
- PENDING: 'PENDING', // site candidate notification sent and waiting for human input
25
- IGNORED: 'IGNORED', // site candidate discarded: not to be added to star catalogue
26
- APPROVED: 'APPROVED', // site candidate is added to star catalogue
27
- ERROR: 'ERROR', // site candidate is discovered
28
- };
29
-
30
15
  /**
31
16
  * SiteCandidate - A class representing an SiteCandidate entity.
32
17
  * Provides methods to access and manipulate SiteCandidate-specific data.
@@ -35,6 +20,21 @@ export const SITE_CANDIDATE_STATUS = {
35
20
  * @extends BaseModel
36
21
  */
37
22
  class SiteCandidate extends BaseModel {
23
+ static DEFAULT_UPDATED_BY = 'spacecat';
24
+
25
+ static SITE_CANDIDATE_SOURCES = {
26
+ SPACECAT_SLACK_BOT: 'SPACECAT_SLACK_BOT',
27
+ RUM: 'RUM',
28
+ CDN: 'CDN',
29
+ };
30
+
31
+ static SITE_CANDIDATE_STATUS = {
32
+ PENDING: 'PENDING', // site candidate notification sent and waiting for human input
33
+ IGNORED: 'IGNORED', // site candidate discarded: not to be added to star catalogue
34
+ APPROVED: 'APPROVED', // site candidate is added to star catalogue
35
+ ERROR: 'ERROR', // site candidate is discovered
36
+ };
37
+
38
38
  // add your custom methods or overrides here
39
39
  }
40
40
 
@@ -17,7 +17,7 @@ import { isObject, isValidUrl } from '@adobe/spacecat-shared-utils';
17
17
  import { validate as uuidValidate } from 'uuid';
18
18
 
19
19
  import SchemaBuilder from '../base/schema.builder.js';
20
- import SiteCandidate, { SITE_CANDIDATE_SOURCES, SITE_CANDIDATE_STATUS } from './site-candidate.model.js';
20
+ import SiteCandidate from './site-candidate.model.js';
21
21
  import SiteCandidateCollection from './site-candidate.collection.js';
22
22
 
23
23
  /*
@@ -44,11 +44,11 @@ const schema = new SchemaBuilder(SiteCandidate, SiteCandidateCollection)
44
44
  validate: (value) => isObject(value),
45
45
  })
46
46
  .addAttribute('source', {
47
- type: Object.values(SITE_CANDIDATE_SOURCES),
47
+ type: Object.values(SiteCandidate.SITE_CANDIDATE_SOURCES),
48
48
  required: true,
49
49
  })
50
50
  .addAttribute('status', {
51
- type: Object.values(SITE_CANDIDATE_STATUS),
51
+ type: Object.values(SiteCandidate.SITE_CANDIDATE_STATUS),
52
52
  required: true,
53
53
  })
54
54
  .addAttribute('updatedBy', {
@@ -12,8 +12,6 @@
12
12
 
13
13
  import BaseModel from '../base/base.model.js';
14
14
 
15
- export const DEFAULT_GEO = 'global';
16
-
17
15
  /**
18
16
  * SiteTopPage - A class representing an SiteTopPage entity.
19
17
  * Provides methods to access and manipulate SiteTopPage-specific data.
@@ -22,6 +20,8 @@ export const DEFAULT_GEO = 'global';
22
20
  * @extends BaseModel
23
21
  */
24
22
  class SiteTopPage extends BaseModel {
23
+ static DEFAULT_GEO = 'global';
24
+
25
25
  // add your custom methods or overrides here
26
26
  }
27
27
 
@@ -17,7 +17,7 @@ import { isInteger, isIsoDate, isValidUrl } from '@adobe/spacecat-shared-utils';
17
17
  import { validate as uuidValidate } from 'uuid';
18
18
 
19
19
  import SchemaBuilder from '../base/schema.builder.js';
20
- import SiteTopPage, { DEFAULT_GEO } from './site-top-page.model.js';
20
+ import SiteTopPage from './site-top-page.model.js';
21
21
  import SiteTopPageCollection from './site-top-page.collection.js';
22
22
 
23
23
  /*
@@ -53,7 +53,7 @@ const schema = new SchemaBuilder(SiteTopPage, SiteTopPageCollection)
53
53
  .addAttribute('geo', {
54
54
  type: 'string',
55
55
  required: false,
56
- default: DEFAULT_GEO,
56
+ default: SiteTopPage.DEFAULT_GEO,
57
57
  })
58
58
  .addAttribute('importedAt', {
59
59
  type: 'string',
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import BaseCollection from '../base/base.collection.js';
14
- import { STATUSES } from './suggestion.model.js';
14
+ import Suggestion from './suggestion.model.js';
15
15
 
16
16
  /**
17
17
  * SuggestionCollection - A collection class responsible for managing Suggestion entities.
@@ -38,8 +38,8 @@ class SuggestionCollection extends BaseCollection {
38
38
  throw new Error('Suggestions must be an array');
39
39
  }
40
40
 
41
- if (!Object.values(STATUSES).includes(status)) {
42
- throw new Error(`Invalid status: ${status}. Must be one of: ${Object.values(STATUSES).join(', ')}`);
41
+ if (!Object.values(Suggestion.STATUSES).includes(status)) {
42
+ throw new Error(`Invalid status: ${status}. Must be one of: ${Object.values(Suggestion.STATUSES).join(', ')}`);
43
43
  }
44
44
 
45
45
  suggestions.forEach((suggestion) => {
@@ -12,21 +12,6 @@
12
12
 
13
13
  import BaseModel from '../base/base.model.js';
14
14
 
15
- export const STATUSES = {
16
- NEW: 'NEW',
17
- APPROVED: 'APPROVED',
18
- SKIPPED: 'SKIPPED',
19
- FIXED: 'FIXED',
20
- ERROR: 'ERROR',
21
- };
22
-
23
- export const TYPES = {
24
- CODE_CHANGE: 'CODE_CHANGE',
25
- CONTENT_UPDATE: 'CONTENT_UPDATE',
26
- REDIRECT_UPDATE: 'REDIRECT_UPDATE',
27
- METADATA_UPDATE: 'METADATA_UPDATE',
28
- };
29
-
30
15
  /**
31
16
  * Suggestion - A class representing a Suggestion entity.
32
17
  * Provides methods to access and manipulate Suggestion-specific data,
@@ -36,6 +21,21 @@ export const TYPES = {
36
21
  * @extends BaseModel
37
22
  */
38
23
  class Suggestion extends BaseModel {
24
+ static STATUSES = {
25
+ NEW: 'NEW',
26
+ APPROVED: 'APPROVED',
27
+ SKIPPED: 'SKIPPED',
28
+ FIXED: 'FIXED',
29
+ ERROR: 'ERROR',
30
+ };
31
+
32
+ static TYPES = {
33
+ CODE_CHANGE: 'CODE_CHANGE',
34
+ CONTENT_UPDATE: 'CONTENT_UPDATE',
35
+ REDIRECT_UPDATE: 'REDIRECT_UPDATE',
36
+ METADATA_UPDATE: 'METADATA_UPDATE',
37
+ };
38
+
39
39
  // add your customized methods here
40
40
  }
41
41
 
@@ -15,7 +15,7 @@
15
15
  import { isNonEmptyObject } from '@adobe/spacecat-shared-utils';
16
16
 
17
17
  import SchemaBuilder from '../base/schema.builder.js';
18
- import Suggestion, { STATUSES, TYPES } from './suggestion.model.js';
18
+ import Suggestion from './suggestion.model.js';
19
19
  import SuggestionCollection from './suggestion.collection.js';
20
20
 
21
21
  /*
@@ -27,7 +27,7 @@ Indexes Doc: https://electrodb.dev/en/modeling/indexes/
27
27
  const schema = new SchemaBuilder(Suggestion, SuggestionCollection)
28
28
  .addReference('belongs_to', 'Opportunity', ['status', 'rank'])
29
29
  .addAttribute('type', {
30
- type: Object.values(TYPES),
30
+ type: Object.values(Suggestion.TYPES),
31
31
  required: true,
32
32
  readOnly: true,
33
33
  })
@@ -45,9 +45,9 @@ const schema = new SchemaBuilder(Suggestion, SuggestionCollection)
45
45
  validate: (value) => !value || isNonEmptyObject(value),
46
46
  })
47
47
  .addAttribute('status', {
48
- type: Object.values(STATUSES),
48
+ type: Object.values(Suggestion.STATUSES),
49
49
  required: true,
50
- default: STATUSES.NEW,
50
+ default: Suggestion.STATUSES.NEW,
51
51
  });
52
52
 
53
53
  export default schema.build();