@adobe/spacecat-shared-data-access 1.53.1 → 1.54.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 +7 -0
- package/package.json +4 -1
- package/src/index.d.ts +6 -0
- package/src/index.js +5 -0
- package/src/service/index.js +52 -0
- package/src/v2/errors/index.d.ts +13 -0
- package/src/v2/errors/index.js +15 -0
- package/src/v2/errors/validation.error.js +13 -0
- package/src/v2/index.d.ts +15 -0
- package/src/v2/index.js +15 -0
- package/src/v2/models/base.collection.js +118 -0
- package/src/v2/models/base.model.js +127 -0
- package/src/v2/models/index.d.ts +100 -0
- package/src/v2/models/index.js +27 -0
- package/src/v2/models/model.factory.js +74 -0
- package/src/v2/models/opportunity.collection.js +78 -0
- package/src/v2/models/opportunity.model.js +238 -0
- package/src/v2/models/suggestion.collection.js +80 -0
- package/src/v2/models/suggestion.model.js +138 -0
- package/src/v2/readme.md +260 -0
- package/src/v2/schema/opportunity.schema.js +145 -0
- package/src/v2/schema/suggestion.schema.js +122 -0
- package/src/v2/util/guards.d.ts +62 -0
- package/src/v2/util/guards.js +165 -0
- package/src/v2/util/index.d.ts +13 -0
- package/src/v2/util/index.js +22 -0
- package/src/v2/util/patcher.js +183 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-data-access-v1.54.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.53.1...@adobe/spacecat-shared-data-access-v1.54.0) (2024-11-20)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* opportunity & suggestion model (+electrodb) ([#447](https://github.com/adobe/spacecat-shared/issues/447)) ([91cf931](https://github.com/adobe/spacecat-shared/commit/91cf931facbc7f13a6fe6eebe71f2948a4ec007e))
|
|
7
|
+
|
|
1
8
|
# [@adobe/spacecat-shared-data-access-v1.53.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v1.53.0...@adobe/spacecat-shared-data-access-v1.53.1) (2024-11-16)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/spacecat-shared-data-access",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.54.0",
|
|
4
4
|
"description": "Shared modules of the Spacecat Services - Data Access",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"clean": "rm -rf package-lock.json node_modules"
|
|
17
17
|
},
|
|
18
18
|
"mocha": {
|
|
19
|
+
"require": "test/setup-env.js",
|
|
19
20
|
"reporter": "mocha-multi-reporters",
|
|
20
21
|
"reporter-options": "configFile=.mocha-multi.json"
|
|
21
22
|
},
|
|
@@ -38,6 +39,8 @@
|
|
|
38
39
|
"@aws-sdk/client-dynamodb": "3.693.0",
|
|
39
40
|
"@aws-sdk/lib-dynamodb": "3.693.0",
|
|
40
41
|
"@types/joi": "17.2.3",
|
|
42
|
+
"aws-xray-sdk": "3.10.2",
|
|
43
|
+
"electrodb": "3.0.1",
|
|
41
44
|
"joi": "17.13.3",
|
|
42
45
|
"uuid": "11.0.3"
|
|
43
46
|
},
|
package/src/index.d.ts
CHANGED
|
@@ -892,6 +892,10 @@ export interface DataAccess {
|
|
|
892
892
|
getExperiments: (siteId: string, experimentId?: string) => Promise<Experiment[]>;
|
|
893
893
|
getExperiment: (siteId: string, experimentId: string, url: string) => Promise<Experiment | null>;
|
|
894
894
|
upsertExperiment: (experimentData: object) => Promise<Experiment>;
|
|
895
|
+
|
|
896
|
+
// electro-based entities
|
|
897
|
+
Opportunity: object,
|
|
898
|
+
Suggestion: object,
|
|
895
899
|
}
|
|
896
900
|
|
|
897
901
|
interface DataAccessConfig {
|
|
@@ -930,3 +934,5 @@ export function createDataAccess(
|
|
|
930
934
|
config: DataAccessConfig,
|
|
931
935
|
logger: object,
|
|
932
936
|
): DataAccess;
|
|
937
|
+
|
|
938
|
+
export type * from './v2/index.d.ts';
|
package/src/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { createDataAccess } from './service/index.js';
|
|
|
14
14
|
|
|
15
15
|
export { ImportJobStatus, ImportUrlStatus, ImportOptions } from './models/importer/import-constants.js';
|
|
16
16
|
|
|
17
|
+
const TABLE_NAME_DATA = 'spacecat-services-data-dev';
|
|
17
18
|
const TABLE_NAME_AUDITS = 'spacecat-services-audits-dev';
|
|
18
19
|
const TABLE_NAME_KEY_EVENTS = 'spacecat-services-key-events';
|
|
19
20
|
const TABLE_NAME_LATEST_AUDITS = 'spacecat-services-latest-audits-dev';
|
|
@@ -52,6 +53,7 @@ export default function dataAccessWrapper(fn) {
|
|
|
52
53
|
const { log } = context;
|
|
53
54
|
|
|
54
55
|
const {
|
|
56
|
+
DYNAMO_TABLE_NAME_DATA = TABLE_NAME_DATA,
|
|
55
57
|
DYNAMO_TABLE_NAME_AUDITS = TABLE_NAME_AUDITS,
|
|
56
58
|
DYNAMO_TABLE_NAME_KEY_EVENTS = TABLE_NAME_KEY_EVENTS,
|
|
57
59
|
DYNAMO_TABLE_NAME_LATEST_AUDITS = TABLE_NAME_LATEST_AUDITS,
|
|
@@ -82,6 +84,7 @@ export default function dataAccessWrapper(fn) {
|
|
|
82
84
|
} = context.env;
|
|
83
85
|
|
|
84
86
|
context.dataAccess = createDataAccess({
|
|
87
|
+
tableNameData: DYNAMO_TABLE_NAME_DATA,
|
|
85
88
|
tableNameAudits: DYNAMO_TABLE_NAME_AUDITS,
|
|
86
89
|
tableNameKeyEvents: DYNAMO_TABLE_NAME_KEY_EVENTS,
|
|
87
90
|
tableNameLatestAudits: DYNAMO_TABLE_NAME_LATEST_AUDITS,
|
|
@@ -118,3 +121,5 @@ export default function dataAccessWrapper(fn) {
|
|
|
118
121
|
return fn(request, context);
|
|
119
122
|
};
|
|
120
123
|
}
|
|
124
|
+
|
|
125
|
+
export * from './v2/index.js';
|
package/src/service/index.js
CHANGED
|
@@ -11,6 +11,17 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { createClient } from '@adobe/spacecat-shared-dynamo';
|
|
14
|
+
import { DynamoDB } from '@aws-sdk/client-dynamodb';
|
|
15
|
+
import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb';
|
|
16
|
+
import AWSXray from 'aws-xray-sdk';
|
|
17
|
+
import { Service } from 'electrodb';
|
|
18
|
+
|
|
19
|
+
import ModelFactory from '../v2/models/model.factory.js';
|
|
20
|
+
import OpportunityCollection from '../v2/models/opportunity.collection.js';
|
|
21
|
+
import SuggestionCollection from '../v2/models/suggestion.collection.js';
|
|
22
|
+
import OpportunitySchema from '../v2/schema/opportunity.schema.js';
|
|
23
|
+
import SuggestionSchema from '../v2/schema/suggestion.schema.js';
|
|
24
|
+
|
|
14
25
|
import { auditFunctions } from './audits/index.js';
|
|
15
26
|
import { keyEventFunctions } from './key-events/index.js';
|
|
16
27
|
import { siteFunctions } from './sites/index.js';
|
|
@@ -23,6 +34,36 @@ import { importUrlFunctions } from './import-url/index.js';
|
|
|
23
34
|
import { experimentFunctions } from './experiments/index.js';
|
|
24
35
|
import { apiKeyFunctions } from './api-key/index.js';
|
|
25
36
|
|
|
37
|
+
const createRawClient = () => {
|
|
38
|
+
const dbClient = AWSXray.captureAWSv3Client(new DynamoDB());
|
|
39
|
+
return DynamoDBDocument.from(dbClient, {
|
|
40
|
+
marshallOptions: {
|
|
41
|
+
convertEmptyValues: true,
|
|
42
|
+
removeUndefinedValues: true,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const createElectroService = (client, config, log) => {
|
|
48
|
+
const { tableNameData: table } = config;
|
|
49
|
+
/* c8 ignore start */
|
|
50
|
+
const logger = (event) => {
|
|
51
|
+
log.debug(JSON.stringify(event, null, 4));
|
|
52
|
+
};
|
|
53
|
+
/* c8 ignore end */
|
|
54
|
+
return new Service(
|
|
55
|
+
{
|
|
56
|
+
opportunity: OpportunitySchema,
|
|
57
|
+
suggestion: SuggestionSchema,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
client,
|
|
61
|
+
table,
|
|
62
|
+
logger,
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
26
67
|
/**
|
|
27
68
|
* Creates a data access object.
|
|
28
69
|
*
|
|
@@ -51,6 +92,14 @@ export const createDataAccess = (config, log = console) => {
|
|
|
51
92
|
const experimentFuncs = experimentFunctions(dynamoClient, config, log);
|
|
52
93
|
const apiKeyFuncs = apiKeyFunctions(dynamoClient, config, log);
|
|
53
94
|
|
|
95
|
+
// electro-based data access objects
|
|
96
|
+
const rawClient = createRawClient();
|
|
97
|
+
const electroService = createElectroService(rawClient, config, log);
|
|
98
|
+
const modelFactory = new ModelFactory(electroService, log);
|
|
99
|
+
|
|
100
|
+
const Opportunity = modelFactory.getCollection(OpportunityCollection.name);
|
|
101
|
+
const Suggestion = modelFactory.getCollection(SuggestionCollection.name);
|
|
102
|
+
|
|
54
103
|
return {
|
|
55
104
|
...auditFuncs,
|
|
56
105
|
...keyEventFuncs,
|
|
@@ -63,5 +112,8 @@ export const createDataAccess = (config, log = console) => {
|
|
|
63
112
|
...importUrlFuncs,
|
|
64
113
|
...experimentFuncs,
|
|
65
114
|
...apiKeyFuncs,
|
|
115
|
+
// electro-based data access objects
|
|
116
|
+
Opportunity,
|
|
117
|
+
Suggestion,
|
|
66
118
|
};
|
|
67
119
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
export type ValidationError = Error
|
|
@@ -0,0 +1,15 @@
|
|
|
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 ValidationError from './validation.error.js';
|
|
14
|
+
|
|
15
|
+
export { ValidationError };
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
export default class ValidationError extends Error {}
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
export type * from './errors/index.d.ts';
|
|
14
|
+
export type * from './models/index.d.ts';
|
|
15
|
+
export type * from './util/index.d.ts';
|
package/src/v2/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
export * from './errors/index.js';
|
|
14
|
+
export * from './models/index.js';
|
|
15
|
+
export * from './util/index.js';
|
|
@@ -0,0 +1,118 @@
|
|
|
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 { isNonEmptyObject } from '@adobe/spacecat-shared-utils';
|
|
14
|
+
|
|
15
|
+
import { guardId } from '../util/guards.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* BaseCollection - A base class for managing collections of entities in the application.
|
|
19
|
+
* This class uses ElectroDB to interact with entities and provides common functionality
|
|
20
|
+
* for data operations.
|
|
21
|
+
*
|
|
22
|
+
* @class BaseCollection
|
|
23
|
+
*/
|
|
24
|
+
class BaseCollection {
|
|
25
|
+
/**
|
|
26
|
+
* Constructs an instance of BaseCollection.
|
|
27
|
+
* @constructor
|
|
28
|
+
* @param {Object} electroService - The ElectroDB service used for managing entities.
|
|
29
|
+
* @param {Object} modelFactory - A factory for creating model instances.
|
|
30
|
+
* @param {Class} clazz - The model class that represents the entity.
|
|
31
|
+
* @param {Object} log - A logger for capturing logging information.
|
|
32
|
+
*/
|
|
33
|
+
constructor(electroService, modelFactory, clazz, log) {
|
|
34
|
+
this.electroService = electroService;
|
|
35
|
+
this.modelFactory = modelFactory;
|
|
36
|
+
this.clazz = clazz;
|
|
37
|
+
this.entityName = this.clazz.name.toLowerCase();
|
|
38
|
+
this.entity = electroService.entities[this.entityName];
|
|
39
|
+
this.idName = `${this.entityName}Id`;
|
|
40
|
+
this.log = log;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates an instance of a model from a record.
|
|
45
|
+
* @protected
|
|
46
|
+
* @param {Object} record - The record containing data to create the model instance.
|
|
47
|
+
* @returns {BaseModel|null} - Returns an instance of the model class if the data is valid,
|
|
48
|
+
* otherwise null.
|
|
49
|
+
*/
|
|
50
|
+
_createInstance(record) {
|
|
51
|
+
if (!isNonEmptyObject(record?.data)) {
|
|
52
|
+
this.log.warn(`Failed to create instance of [${this.entityName}]: record is empty`);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
// eslint-disable-next-line new-cap
|
|
56
|
+
return new this.clazz(
|
|
57
|
+
this.electroService,
|
|
58
|
+
this.modelFactory,
|
|
59
|
+
record.data,
|
|
60
|
+
this.log,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Creates instances of models from a set of records.
|
|
66
|
+
* @protected
|
|
67
|
+
* @param {Object} records - The records containing data to create the model instances.
|
|
68
|
+
* @returns {Array<BaseModel>} - An array of instances of the model class.
|
|
69
|
+
*/
|
|
70
|
+
_createInstances(records) {
|
|
71
|
+
if (!Array.isArray(records?.data)) {
|
|
72
|
+
this.log.warn(`Failed to create instances of [${this.entityName}]: records are empty`);
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
return records.data.map((record) => this._createInstance({ data: record }));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Finds an entity by its ID.
|
|
80
|
+
* @async
|
|
81
|
+
* @param {string} id - The unique identifier of the entity to be found.
|
|
82
|
+
* @returns {Promise<BaseModel|null>} - A promise that resolves to an instance of
|
|
83
|
+
* the model if found, otherwise null.
|
|
84
|
+
* @throws {Error} - Throws an error if the ID is not provided.
|
|
85
|
+
*/
|
|
86
|
+
async findById(id) {
|
|
87
|
+
guardId(this.idName, id, this.entityName);
|
|
88
|
+
|
|
89
|
+
const record = await this.entity.get({ [this.idName]: id }).go();
|
|
90
|
+
|
|
91
|
+
return this._createInstance(record);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Creates a new entity in the collection.
|
|
96
|
+
* @async
|
|
97
|
+
* @param {Object} data - The data for the entity to be created.
|
|
98
|
+
* @returns {Promise<BaseModel>} - A promise that resolves to the created model instance.
|
|
99
|
+
* @throws {Error} - Throws an error if the data is invalid or if the creation process fails.
|
|
100
|
+
*/
|
|
101
|
+
async create(data) {
|
|
102
|
+
if (!isNonEmptyObject(data)) {
|
|
103
|
+
this.log.error(`Failed to create [${this.entityName}]: data is required`);
|
|
104
|
+
throw new Error(`Failed to create [${this.entityName}]: data is required`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
// todo: validate associations
|
|
109
|
+
const record = await this.entity.create(data).go();
|
|
110
|
+
return this._createInstance(record);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
this.log.error(`Failed to create [${this.entityName}]`, error);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export default BaseCollection;
|
|
@@ -0,0 +1,127 @@
|
|
|
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 Patcher from '../util/patcher.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Base - A base class for representing individual entities in the application.
|
|
17
|
+
* Provides common functionality for entity management, including fetching, updating,
|
|
18
|
+
* and deleting records.
|
|
19
|
+
*
|
|
20
|
+
* @class BaseModel
|
|
21
|
+
*/
|
|
22
|
+
class BaseModel {
|
|
23
|
+
/**
|
|
24
|
+
* Constructs an instance of BaseModel.
|
|
25
|
+
* @constructor
|
|
26
|
+
* @param {Object} electroService - The ElectroDB service used for managing entities.
|
|
27
|
+
* @param {Object} modelFactory - A factory for creating model instances.
|
|
28
|
+
* @param {Object} record - The initial data for the entity instance.
|
|
29
|
+
* @param {Object} log - A logger for capturing logging information.
|
|
30
|
+
*/
|
|
31
|
+
constructor(electroService, modelFactory, record, log) {
|
|
32
|
+
this.modelFactory = modelFactory;
|
|
33
|
+
this.record = record;
|
|
34
|
+
this.entityName = this.constructor.name.toLowerCase();
|
|
35
|
+
this.entity = electroService.entities[this.entityName];
|
|
36
|
+
this.idName = `${this.entityName}Id`;
|
|
37
|
+
this.log = log;
|
|
38
|
+
|
|
39
|
+
this.patcher = new Patcher(this.entity, this.record);
|
|
40
|
+
this.associationsCache = {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Gets the association of the model by querying another related model. Retrieved
|
|
45
|
+
* associations are cached to avoid redundant queries.
|
|
46
|
+
* @protected
|
|
47
|
+
* @async
|
|
48
|
+
* @param {string} modelName - The name of the related model.
|
|
49
|
+
* @param {string} method - The method to use for querying the related model.
|
|
50
|
+
* @param {...*} args - Additional arguments to be passed to the method.
|
|
51
|
+
* @returns {Promise<Object>} - A promise that resolves to the associated model instance.
|
|
52
|
+
*/
|
|
53
|
+
async _getAssociation(modelName, method, ...args) {
|
|
54
|
+
const cache = this.associationsCache;
|
|
55
|
+
|
|
56
|
+
cache[modelName] = cache[modelName] || {};
|
|
57
|
+
|
|
58
|
+
if (!(method in cache[modelName])) {
|
|
59
|
+
cache[modelName][method] = await this.modelFactory.getCollection(modelName)[method](...args);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return cache[modelName][method];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Gets the ID of the current entity.
|
|
67
|
+
* @returns {string} - The unique identifier of the entity.
|
|
68
|
+
*/
|
|
69
|
+
getId() {
|
|
70
|
+
return this.record[this.idName];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Gets the creation timestamp of the current entity.
|
|
75
|
+
* @returns {string} - The ISO string representing when the entity was created.
|
|
76
|
+
*/
|
|
77
|
+
getCreatedAt() {
|
|
78
|
+
return new Date(this.record.createdAt).toISOString();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Gets the update timestamp of the current entity.
|
|
83
|
+
* @returns {string} - The ISO string representing when the entity was last updated.
|
|
84
|
+
*/
|
|
85
|
+
getUpdatedAt() {
|
|
86
|
+
return new Date(this.record.updatedAt).toISOString();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Removes the current entity from the database.
|
|
91
|
+
* @async
|
|
92
|
+
* @returns {Promise<BaseModel>} - A promise that resolves to the current instance of the entity
|
|
93
|
+
* after it has been removed.
|
|
94
|
+
* @throws {Error} - Throws an error if the removal fails.
|
|
95
|
+
*/
|
|
96
|
+
async remove() {
|
|
97
|
+
try {
|
|
98
|
+
// todo: remove dependents (child associations)
|
|
99
|
+
await this.entity.remove({ [this.idName]: this.getId() }).go();
|
|
100
|
+
return this;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
this.log.error('Failed to remove record', error);
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Saves the current entity to the database. This method must be called after making changes
|
|
109
|
+
* to the entity via their respective setter methods.
|
|
110
|
+
* @async
|
|
111
|
+
* @returns {Promise<BaseModel>} - A promise that resolves to the current instance of the entity
|
|
112
|
+
* after it has been saved.
|
|
113
|
+
* @throws {Error} - Throws an error if the save operation fails.
|
|
114
|
+
*/
|
|
115
|
+
async save() {
|
|
116
|
+
// todo: validate associations
|
|
117
|
+
try {
|
|
118
|
+
await this.patcher.save();
|
|
119
|
+
return this;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
this.log.error('Failed to save record', error);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export default BaseModel;
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
* Interface representing a base model for interacting with a data entity.
|
|
15
|
+
*/
|
|
16
|
+
export interface BaseModel {
|
|
17
|
+
getId(): string;
|
|
18
|
+
getCreatedAt(): string;
|
|
19
|
+
getUpdatedAt(): string;
|
|
20
|
+
remove(): Promise<this>;
|
|
21
|
+
save(): Promise<this>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Interface representing an Opportunity model, extending BaseModel.
|
|
26
|
+
*/
|
|
27
|
+
export interface Opportunity extends BaseModel {
|
|
28
|
+
// eslint-disable-next-line no-use-before-define
|
|
29
|
+
getSuggestions(): Promise<Suggestion[]>;
|
|
30
|
+
getSiteId(): string;
|
|
31
|
+
setSiteId(siteId: string): Opportunity;
|
|
32
|
+
getAuditId(): string;
|
|
33
|
+
setAuditId(auditId: string): Opportunity;
|
|
34
|
+
getRunbook(): string;
|
|
35
|
+
setRunbook(runbook: string): Opportunity;
|
|
36
|
+
getGuidance(): string;
|
|
37
|
+
setGuidance(guidance: string): Opportunity;
|
|
38
|
+
getTitle(): string;
|
|
39
|
+
setTitle(title: string): Opportunity;
|
|
40
|
+
getDescription(): string;
|
|
41
|
+
setDescription(description: string): Opportunity;
|
|
42
|
+
getType(): string;
|
|
43
|
+
getStatus(): string;
|
|
44
|
+
setStatus(status: string): Opportunity;
|
|
45
|
+
getOrigin(): string;
|
|
46
|
+
setOrigin(origin: string): Opportunity;
|
|
47
|
+
getTags(): string[];
|
|
48
|
+
setTags(tags: string[]): Opportunity;
|
|
49
|
+
getData(): object;
|
|
50
|
+
setData(data: object): Opportunity;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Interface representing a Suggestion model, extending BaseModel.
|
|
55
|
+
*/
|
|
56
|
+
export interface Suggestion extends BaseModel {
|
|
57
|
+
getOpportunity(): Promise<Opportunity>;
|
|
58
|
+
getOpportunityId(): string;
|
|
59
|
+
setOpportunityId(opportunityId: string): Suggestion;
|
|
60
|
+
getType(): string;
|
|
61
|
+
getStatus(): string;
|
|
62
|
+
setStatus(status: string): Suggestion;
|
|
63
|
+
getRank(): number;
|
|
64
|
+
setRank(rank: number): Suggestion;
|
|
65
|
+
getData(): object;
|
|
66
|
+
setData(data: object): Suggestion;
|
|
67
|
+
getKpiDeltas(): object;
|
|
68
|
+
setKpiDeltas(kpiDeltas: object): Suggestion;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Interface representing a base collection for interacting with data entities.
|
|
73
|
+
*/
|
|
74
|
+
export interface BaseCollection<T extends BaseModel> {
|
|
75
|
+
findById(id: string): Promise<T>;
|
|
76
|
+
create(data: object): Promise<T>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Interface representing the Opportunity collection, extending BaseCollection.
|
|
81
|
+
*/
|
|
82
|
+
export interface OpportunityCollection extends BaseCollection<Opportunity> {
|
|
83
|
+
allBySiteId(siteId: string): Promise<Opportunity[]>;
|
|
84
|
+
allBySiteIdAndStatus(siteId: string, status: string): Promise<Opportunity[]>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Interface representing the Suggestion collection, extending BaseCollection.
|
|
89
|
+
*/
|
|
90
|
+
export interface SuggestionCollection extends BaseCollection<Suggestion> {
|
|
91
|
+
allByOpportunityId(opportunityId: string): Promise<Suggestion[]>;
|
|
92
|
+
allByOpportunityIdAndStatus(opportunityId: string, status: string): Promise<Suggestion[]>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Interface representing the Model Factory for creating and managing model collections.
|
|
97
|
+
*/
|
|
98
|
+
export interface ModelFactory {
|
|
99
|
+
getCollection<T extends BaseModel>(collectionName: string): BaseCollection<T>;
|
|
100
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
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 ModelFactory from './model.factory.js';
|
|
14
|
+
import BaseModel from './base.model.js';
|
|
15
|
+
import Opportunity from './opportunity.model.js';
|
|
16
|
+
import OpportunityCollection from './opportunity.collection.js';
|
|
17
|
+
import Suggestion from './suggestion.model.js';
|
|
18
|
+
import SuggestionCollection from './suggestion.collection.js';
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
ModelFactory,
|
|
22
|
+
BaseModel,
|
|
23
|
+
Opportunity,
|
|
24
|
+
OpportunityCollection,
|
|
25
|
+
Suggestion,
|
|
26
|
+
SuggestionCollection,
|
|
27
|
+
};
|