@autofleet/sadot 0.5.4-beta.19 → 0.5.4-beta.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.
- package/dist/api/v1/definition/index.js +1 -1
- package/dist/hooks/create.d.ts +1 -2
- package/dist/hooks/create.js +8 -9
- package/dist/hooks/enrich.d.ts +1 -2
- package/dist/hooks/enrich.js +4 -13
- package/dist/hooks/update.d.ts +1 -2
- package/dist/hooks/update.js +2 -6
- package/dist/models/index.d.ts +1 -3
- package/dist/models/index.js +2 -8
- package/dist/repository/definition.d.ts +2 -2
- package/dist/repository/definition.js +8 -13
- package/dist/repository/value.js +2 -8
- package/dist/tests/helpers/database-config.d.ts +0 -1
- package/dist/tests/helpers/database-config.js +0 -1
- package/dist/tests/helpers/index.js +0 -2
- package/dist/tests/mocks/definition.mock.d.ts +0 -6
- package/dist/tests/mocks/definition.mock.js +1 -7
- package/dist/tests/mocks/testModel.d.ts +1 -1
- package/dist/tests/mocks/testModel.js +1 -1
- package/dist/types/index.d.ts +1 -5
- package/dist/utils/init.d.ts +4 -4
- package/dist/utils/init.js +6 -6
- package/package.json +3 -21
- package/src/api/v1/definition/index.ts +4 -2
- package/src/hooks/create.ts +5 -12
- package/src/hooks/enrich.ts +3 -17
- package/src/hooks/find.ts +0 -1
- package/src/hooks/update.ts +3 -8
- package/src/models/index.ts +1 -7
- package/src/repository/definition.ts +13 -15
- package/src/repository/value.ts +5 -11
- package/src/tests/helpers/database-config.ts +0 -1
- package/src/tests/helpers/index.ts +1 -5
- package/src/tests/mocks/definition.mock.ts +0 -7
- package/src/tests/mocks/testModel.ts +1 -1
- package/src/types/index.ts +2 -6
- package/src/utils/init.ts +10 -12
- package/.env +0 -3
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.d.ts +0 -10
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.js +0 -55
- package/dist/models/tests/contextAwareModels/ContextModel.d.ts +0 -13
- package/dist/models/tests/contextAwareModels/ContextModel.js +0 -47
- package/src/models/tests/contextAwareModels/ContextAwareTestModel.ts +0 -45
- package/src/models/tests/contextAwareModels/ContextModel.ts +0 -38
|
@@ -83,7 +83,7 @@ router.get('/', async (req, res) => {
|
|
|
83
83
|
if (entityIds?.length > 0) {
|
|
84
84
|
where.entityId = entityIds;
|
|
85
85
|
}
|
|
86
|
-
const customFieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true });
|
|
86
|
+
const customFieldDefinitions = await DefinitionRepo.findAll({ ...where }, { withDisabled: true });
|
|
87
87
|
return res.json(customFieldDefinitions);
|
|
88
88
|
}
|
|
89
89
|
catch (err) {
|
package/dist/hooks/create.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ModelOptions } from '../types';
|
|
2
1
|
/**
|
|
3
2
|
* A hook to create the custom fields when updating a model (more then one instance).
|
|
4
3
|
*/
|
|
@@ -7,4 +6,4 @@ export declare const beforeBulkCreate: (options: any) => void;
|
|
|
7
6
|
* A hook to create the custom fields when updating a model instance.
|
|
8
7
|
* TODO - cleanup if update fail
|
|
9
8
|
*/
|
|
10
|
-
export declare const beforeCreate: (scopeAttributes: string[]
|
|
9
|
+
export declare const beforeCreate: (scopeAttributes: string[]) => (instance: any, options: any) => Promise<void>;
|
package/dist/hooks/create.js
CHANGED
|
@@ -22,8 +22,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
25
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
29
|
exports.beforeCreate = exports.beforeBulkCreate = void 0;
|
|
30
|
+
const logger_1 = __importDefault(require("../utils/logger"));
|
|
27
31
|
const ValueRepo = __importStar(require("../repository/value"));
|
|
28
32
|
const DefinitionRepo = __importStar(require("../repository/definition"));
|
|
29
33
|
const errors_1 = require("../errors");
|
|
@@ -40,14 +44,11 @@ exports.beforeBulkCreate = beforeBulkCreate;
|
|
|
40
44
|
* A hook to create the custom fields when updating a model instance.
|
|
41
45
|
* TODO - cleanup if update fail
|
|
42
46
|
*/
|
|
43
|
-
const beforeCreate = (scopeAttributes
|
|
47
|
+
const beforeCreate = (scopeAttributes) => async (instance, options) => {
|
|
48
|
+
logger_1.default.debug('sadot - before create hook');
|
|
44
49
|
const { fields } = options;
|
|
45
50
|
const modelType = instance.constructor.name;
|
|
46
|
-
const
|
|
47
|
-
let identifiers = scopeAttributes.map((attribute) => instance[attribute]);
|
|
48
|
-
if (scopeAttributeReplacer) {
|
|
49
|
-
identifiers = await scopeAttributeReplacer(identifiers);
|
|
50
|
-
}
|
|
51
|
+
const identifiers = scopeAttributes.map((attribute) => instance[attribute]);
|
|
51
52
|
// get all model's required definitions
|
|
52
53
|
const requiredFieldsNames = await DefinitionRepo.getRequiredFields(modelType, instance.id, identifiers);
|
|
53
54
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
@@ -58,9 +59,7 @@ const beforeCreate = (scopeAttributes, modelOptions) => async (instance, options
|
|
|
58
59
|
if (missingFields?.length > 0) {
|
|
59
60
|
throw new errors_1.MissingRequiredCustomFieldError(missingFields);
|
|
60
61
|
}
|
|
61
|
-
await ValueRepo.updateValues(modelType, instance.id, identifiers, customFields, {
|
|
62
|
-
transaction: options.transaction,
|
|
63
|
-
});
|
|
62
|
+
await ValueRepo.updateValues(modelType, instance.id, identifiers, customFields, { transaction: options.transaction });
|
|
64
63
|
// eslint-disable-next-line no-param-reassign
|
|
65
64
|
fields.splice(customFieldsIdx, 1);
|
|
66
65
|
}
|
package/dist/hooks/enrich.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { ModelOptions } from '../types';
|
|
2
1
|
type SupportedHookTypes = 'afterFind' | 'afterCreate' | 'afterUpdate';
|
|
3
2
|
/**
|
|
4
3
|
* A hook to attach the custom fields when fetching a model instances.
|
|
5
4
|
*/
|
|
6
|
-
declare const enrichResults: (modelType: string, scopeAttributes: string[],
|
|
5
|
+
declare const enrichResults: (modelType: string, scopeAttributes: string[], hookType?: SupportedHookTypes) => (instancesOrInstance: any | any[], options: any) => Promise<void>;
|
|
7
6
|
export default enrichResults;
|
package/dist/hooks/enrich.js
CHANGED
|
@@ -22,14 +22,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
26
|
/* eslint-disable no-param-reassign */
|
|
30
27
|
const ValueRepo = __importStar(require("../repository/value"));
|
|
31
28
|
const DefinitionRepo = __importStar(require("../repository/definition"));
|
|
32
|
-
const logger_1 = __importDefault(require("../utils/logger"));
|
|
33
29
|
/**
|
|
34
30
|
* Serialize custom fields value into the format of {[name] -> [fieldData]}
|
|
35
31
|
*/
|
|
@@ -44,8 +40,7 @@ const serializeCustomFields = (customFieldValues, customFieldDefinitionsHash) =>
|
|
|
44
40
|
/**
|
|
45
41
|
* A hook to attach the custom fields when fetching a model instances.
|
|
46
42
|
*/
|
|
47
|
-
const enrichResults = (modelType, scopeAttributes,
|
|
48
|
-
const { scopeAttributeReplacer } = modelOptions;
|
|
43
|
+
const enrichResults = (modelType, scopeAttributes, hookType) => async (instancesOrInstance, options) => {
|
|
49
44
|
if (options.originalAttributes?.length > 0
|
|
50
45
|
&& !options.originalAttributes?.includes?.('customFields')) {
|
|
51
46
|
return;
|
|
@@ -55,24 +50,20 @@ const enrichResults = (modelType, scopeAttributes, getScope, hookType, modelOpti
|
|
|
55
50
|
? instancesOrInstance
|
|
56
51
|
: [instancesOrInstance];
|
|
57
52
|
instances = instances.filter(Boolean);
|
|
58
|
-
|
|
53
|
+
const identifiers = instances.map((instance) => scopeAttributes
|
|
59
54
|
.map((attr) => instance[attr])).flat();
|
|
60
|
-
if (scopeAttributeReplacer) {
|
|
61
|
-
identifiers = await scopeAttributeReplacer(identifiers);
|
|
62
|
-
}
|
|
63
|
-
logger_1.default.info('enrichResults - sadot - indentifiers: ', identifiers);
|
|
64
55
|
const uniqueIdentifiers = [...new Set(identifiers)].filter(Boolean);
|
|
65
56
|
const identifierCustomFieldDefinitionsMapping = uniqueIdentifiers.reduce((map, identifier) => ({
|
|
66
57
|
...map,
|
|
67
58
|
[identifier]: [],
|
|
68
59
|
}), {});
|
|
69
|
-
const customFieldDefinitions = await DefinitionRepo.findByEntityIds(modelType, uniqueIdentifiers, { transaction: options.transaction
|
|
60
|
+
const customFieldDefinitions = await DefinitionRepo.findByEntityIds(modelType, uniqueIdentifiers, { transaction: options.transaction });
|
|
70
61
|
const definitionsMap = customFieldDefinitions.reduce((map, definition) => ({
|
|
71
62
|
...map,
|
|
72
63
|
[definition.id]: definition,
|
|
73
64
|
}), {});
|
|
74
65
|
customFieldDefinitions.forEach((cfd) => {
|
|
75
|
-
identifierCustomFieldDefinitionsMapping[cfd.entityId]
|
|
66
|
+
identifierCustomFieldDefinitionsMapping[cfd.entityId].push(cfd);
|
|
76
67
|
});
|
|
77
68
|
// Get the values per instates ids:
|
|
78
69
|
const instancesIds = instances.map((i) => i[primaryKey]);
|
package/dist/hooks/update.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ModelOptions } from '../types';
|
|
2
1
|
/**
|
|
3
2
|
* A hook to update the custom fields when updating a model (more then one instance).
|
|
4
3
|
*/
|
|
@@ -7,4 +6,4 @@ export declare const beforeBulkUpdate: (options: any) => void;
|
|
|
7
6
|
* A hook to update the custom fields when updating a model instance.
|
|
8
7
|
* TODO - cleanup if update fail
|
|
9
8
|
*/
|
|
10
|
-
export declare const beforeUpdate: (scopeAttributes: string[]
|
|
9
|
+
export declare const beforeUpdate: (scopeAttributes: string[]) => (instance: any, options: any) => Promise<void>;
|
package/dist/hooks/update.js
CHANGED
|
@@ -42,15 +42,11 @@ exports.beforeBulkUpdate = beforeBulkUpdate;
|
|
|
42
42
|
* A hook to update the custom fields when updating a model instance.
|
|
43
43
|
* TODO - cleanup if update fail
|
|
44
44
|
*/
|
|
45
|
-
const beforeUpdate = (scopeAttributes
|
|
45
|
+
const beforeUpdate = (scopeAttributes) => async (instance, options) => {
|
|
46
46
|
logger_1.default.debug('sadot - before update hook');
|
|
47
47
|
const { fields } = options;
|
|
48
|
-
const { scopeAttributeReplacer } = modelOptions;
|
|
49
48
|
const modelType = instance.constructor.name;
|
|
50
|
-
|
|
51
|
-
if (scopeAttributeReplacer) {
|
|
52
|
-
identifiers = await scopeAttributeReplacer(identifiers);
|
|
53
|
-
}
|
|
49
|
+
const identifiers = scopeAttributes.map((attribute) => instance[attribute]);
|
|
54
50
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
55
51
|
if (customFieldsIdx > -1) {
|
|
56
52
|
const { customFields } = instance;
|
package/dist/models/index.d.ts
CHANGED
|
@@ -2,9 +2,7 @@ import type { Sequelize } from 'sequelize-typescript';
|
|
|
2
2
|
import CustomFieldDefinition from './CustomFieldDefinition';
|
|
3
3
|
import CustomFieldValue from './CustomFieldValue';
|
|
4
4
|
import TestModel from './tests/TestModel';
|
|
5
|
-
import ContextAwareTestModel from './tests/contextAwareModels/ContextAwareTestModel';
|
|
6
|
-
import ContextModel from './tests/contextAwareModels/ContextModel';
|
|
7
5
|
import AssociatedTestModel from './tests/AssociatedTestModel';
|
|
8
6
|
declare const initTables: (sequelize: Sequelize, getUser: any) => Promise<void>;
|
|
9
7
|
declare const initTestModels: (sequelize: Sequelize) => Promise<void>;
|
|
10
|
-
export { CustomFieldValue, CustomFieldDefinition, TestModel, AssociatedTestModel,
|
|
8
|
+
export { CustomFieldValue, CustomFieldDefinition, TestModel, AssociatedTestModel, initTables, initTestModels, };
|
package/dist/models/index.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.initTestModels = exports.initTables = exports.
|
|
6
|
+
exports.initTestModels = exports.initTables = exports.AssociatedTestModel = exports.TestModel = exports.CustomFieldDefinition = exports.CustomFieldValue = void 0;
|
|
7
7
|
/* eslint-disable no-param-reassign */
|
|
8
8
|
const sequelize_1 = require("sequelize");
|
|
9
9
|
const logger_1 = __importDefault(require("../utils/logger"));
|
|
@@ -13,14 +13,10 @@ const CustomFieldValue_1 = __importDefault(require("./CustomFieldValue"));
|
|
|
13
13
|
exports.CustomFieldValue = CustomFieldValue_1.default;
|
|
14
14
|
const TestModel_1 = __importDefault(require("./tests/TestModel"));
|
|
15
15
|
exports.TestModel = TestModel_1.default;
|
|
16
|
-
const ContextAwareTestModel_1 = __importDefault(require("./tests/contextAwareModels/ContextAwareTestModel"));
|
|
17
|
-
exports.ContextAwareTestModel = ContextAwareTestModel_1.default;
|
|
18
|
-
const ContextModel_1 = __importDefault(require("./tests/contextAwareModels/ContextModel"));
|
|
19
|
-
exports.ContextModel = ContextModel_1.default;
|
|
20
16
|
const AssociatedTestModel_1 = __importDefault(require("./tests/AssociatedTestModel"));
|
|
21
17
|
exports.AssociatedTestModel = AssociatedTestModel_1.default;
|
|
22
18
|
const productionModels = [CustomFieldDefinition_1.default, CustomFieldValue_1.default];
|
|
23
|
-
const testModels = [TestModel_1.default, AssociatedTestModel_1.default
|
|
19
|
+
const testModels = [TestModel_1.default, AssociatedTestModel_1.default];
|
|
24
20
|
const SADOT_MIGRATION_PREFIX = 'sadot-migration';
|
|
25
21
|
const SCHEMA_VERSION = 1;
|
|
26
22
|
const CUSTOM_FIELDS_SCHEMA_VERSION = `${SADOT_MIGRATION_PREFIX}_${SCHEMA_VERSION}`;
|
|
@@ -86,8 +82,6 @@ const initTestModels = async (sequelize) => {
|
|
|
86
82
|
logger_1.default.info('custom-fields: test models added');
|
|
87
83
|
await TestModel_1.default.sync({ alter: true });
|
|
88
84
|
await AssociatedTestModel_1.default.sync({ alter: true });
|
|
89
|
-
await ContextModel_1.default.sync({ alter: true });
|
|
90
|
-
await ContextAwareTestModel_1.default.sync({ alter: true });
|
|
91
85
|
logger_1.default.info('custom-fields: test models synced');
|
|
92
86
|
};
|
|
93
87
|
exports.initTestModels = initTestModels;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { WhereOptions } from 'sequelize';
|
|
2
1
|
import { CustomFieldDefinition } from '../models';
|
|
3
2
|
import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../types/definition';
|
|
4
3
|
export declare const create: (data: CreateCustomFieldDefinition) => Promise<CustomFieldDefinition>;
|
|
5
|
-
export declare const findAll: (where:
|
|
4
|
+
export declare const findAll: (where: any, options?: any) => Promise<CustomFieldDefinition[]>;
|
|
6
5
|
export declare const findByIds: (ids: string[], options?: any) => Promise<CustomFieldDefinition[]>;
|
|
7
6
|
export declare const findById: (id: string, options?: any) => Promise<CustomFieldDefinition | null>;
|
|
7
|
+
export declare const findByEntityId: (entityId: string, options?: any) => Promise<CustomFieldDefinition[]>;
|
|
8
8
|
export declare const findByEntityIds: (modelType: string, entityIds: string[], options?: any) => Promise<CustomFieldDefinition[]>;
|
|
9
9
|
export declare const findByWhere: (where: any) => Promise<CustomFieldDefinition | null>;
|
|
10
10
|
export declare const findDefinitionsByModels: (modelTypes: string[], options?: any) => Promise<CustomFieldDefinition[]>;
|
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getRequiredFields = exports.destroy = exports.disable = exports.update = exports.findDefinitionsByModels = exports.findByWhere = exports.findByEntityIds = exports.findById = exports.findByIds = exports.findAll = exports.create = void 0;
|
|
3
|
+
exports.getRequiredFields = exports.destroy = exports.disable = exports.update = exports.findDefinitionsByModels = exports.findByWhere = exports.findByEntityIds = exports.findByEntityId = exports.findById = exports.findByIds = exports.findAll = exports.create = void 0;
|
|
7
4
|
const sequelize_1 = require("sequelize");
|
|
8
5
|
const models_1 = require("../models");
|
|
9
|
-
const logger_1 = __importDefault(require("../utils/logger"));
|
|
10
6
|
const create = (data) => models_1.CustomFieldDefinition.create(data);
|
|
11
7
|
exports.create = create;
|
|
12
8
|
const findAll = (where, options = { withDisabled: false }) => {
|
|
13
9
|
const queryModel = options.withDisabled
|
|
14
10
|
? models_1.CustomFieldDefinition.unscoped()
|
|
15
11
|
: models_1.CustomFieldDefinition;
|
|
16
|
-
logger_1.default.info('sadot - whereQuery', { options });
|
|
17
12
|
return queryModel.scope('userScope').findAll({
|
|
18
13
|
where,
|
|
14
|
+
transaction: options.transaction,
|
|
19
15
|
raw: true,
|
|
20
|
-
logging: true,
|
|
21
16
|
});
|
|
22
17
|
};
|
|
23
18
|
exports.findAll = findAll;
|
|
@@ -31,13 +26,17 @@ const findById = (id, options = { withDisabled: false }) => {
|
|
|
31
26
|
return models_1.CustomFieldDefinition.scope('userScope').findByPk(id);
|
|
32
27
|
};
|
|
33
28
|
exports.findById = findById;
|
|
29
|
+
const findByEntityId = async (entityId, options = {}) => models_1.CustomFieldDefinition.findAll({
|
|
30
|
+
where: { entityId },
|
|
31
|
+
transaction: options.transaction,
|
|
32
|
+
});
|
|
33
|
+
exports.findByEntityId = findByEntityId;
|
|
34
34
|
const findByEntityIds = async (modelType, entityIds, options = {}) => models_1.CustomFieldDefinition.findAll({
|
|
35
35
|
where: {
|
|
36
36
|
modelType,
|
|
37
37
|
entityId: entityIds,
|
|
38
38
|
},
|
|
39
39
|
transaction: options.transaction,
|
|
40
|
-
include: options.include,
|
|
41
40
|
raw: true,
|
|
42
41
|
});
|
|
43
42
|
exports.findByEntityIds = findByEntityIds;
|
|
@@ -72,11 +71,7 @@ exports.destroy = destroy;
|
|
|
72
71
|
const getRequiredFields = async (modelType, modelId, entityId) => {
|
|
73
72
|
const entityIds = Array.isArray(entityId) ? entityId : [entityId];
|
|
74
73
|
const requiredFields = await models_1.CustomFieldDefinition.findAll({
|
|
75
|
-
where: {
|
|
76
|
-
required: true,
|
|
77
|
-
modelType,
|
|
78
|
-
entityId: { [sequelize_1.Op.in]: entityIds },
|
|
79
|
-
},
|
|
74
|
+
where: { required: true, modelType, entityId: { [sequelize_1.Op.in]: entityIds } },
|
|
80
75
|
logging: true,
|
|
81
76
|
});
|
|
82
77
|
const requiredFieldsNames = requiredFields.map((definition) => definition.name);
|
package/dist/repository/value.js
CHANGED
|
@@ -73,15 +73,9 @@ exports.findValuesByModelIds = findValuesByModelIds;
|
|
|
73
73
|
* Return the updated values
|
|
74
74
|
*/
|
|
75
75
|
const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, options = {}) => {
|
|
76
|
+
logger_1.default.info(`custom-fields: updating values for ${modelType} ${modelId}`, { valuesToUpdate });
|
|
76
77
|
const names = Object.keys(valuesToUpdate);
|
|
77
|
-
|
|
78
|
-
names, options, valuesToUpdate, entityId: identifiers,
|
|
79
|
-
});
|
|
80
|
-
const fieldDefinitions = (await DefinitionRepo.findAll({
|
|
81
|
-
modelType,
|
|
82
|
-
name: names,
|
|
83
|
-
entityId: identifiers,
|
|
84
|
-
}, { withDisabled: true, transaction: options.transaction })) || [];
|
|
78
|
+
const fieldDefinitions = await DefinitionRepo.findAll({ modelType, name: names, entityId: identifiers }, { withDisabled: true, transaction: options.transaction }) || [];
|
|
85
79
|
const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
|
|
86
80
|
if (fieldDefinitions.length !== names.length) {
|
|
87
81
|
logger_1.default.info(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
|
|
@@ -6,7 +6,6 @@ exports.default = {
|
|
|
6
6
|
password: process.env.DB_PASSWORD || null,
|
|
7
7
|
database: process.env.DB_NAME || 'sadot_package_test',
|
|
8
8
|
host: process.env.DB_HOST || '127.0.0.1',
|
|
9
|
-
port: process.env.DB_PORT || 5432,
|
|
10
9
|
dialect: process.env.DB_TYPE || 'postgres',
|
|
11
10
|
define: {
|
|
12
11
|
underscored: true,
|
|
@@ -7,8 +7,6 @@ const cleanup = async () => {
|
|
|
7
7
|
if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development') {
|
|
8
8
|
await models_1.CustomFieldDefinition.unscoped().destroy({ where: {} });
|
|
9
9
|
await models_1.TestModel.destroy({ where: {} });
|
|
10
|
-
await models_1.ContextAwareTestModel.destroy({ where: {} });
|
|
11
|
-
await models_1.ContextModel.destroy({ where: {} });
|
|
12
10
|
}
|
|
13
11
|
};
|
|
14
12
|
exports.cleanup = cleanup;
|
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
import { CreateCustomFieldDefinition, CustomFieldDefinitionDTO } from '../../types/definition';
|
|
2
|
-
export declare const contextAwareFieldDefinition: {
|
|
3
|
-
name: string;
|
|
4
|
-
modelType: string;
|
|
5
|
-
fieldType: string;
|
|
6
|
-
entityType: string;
|
|
7
|
-
};
|
|
8
2
|
export declare const coolFieldDefinition: CreateCustomFieldDefinition;
|
|
9
3
|
export declare const coolFieldDefinition2: {
|
|
10
4
|
name: string;
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createDefinitions = exports.createDefinition = exports.rangeField = exports.enumField = exports.booleanField = exports.coolFieldDefinition3 = exports.coolFieldDefinition2 = exports.coolFieldDefinition =
|
|
3
|
+
exports.createDefinitions = exports.createDefinition = exports.rangeField = exports.enumField = exports.booleanField = exports.coolFieldDefinition3 = exports.coolFieldDefinition2 = exports.coolFieldDefinition = void 0;
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
|
-
exports.contextAwareFieldDefinition = {
|
|
6
|
-
name: 'cool field',
|
|
7
|
-
modelType: 'ContextAwareTestModel',
|
|
8
|
-
fieldType: 'number',
|
|
9
|
-
entityType: 'fleetId',
|
|
10
|
-
};
|
|
11
5
|
exports.coolFieldDefinition = {
|
|
12
6
|
name: 'cool field',
|
|
13
7
|
modelType: 'TestModel',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TestModel } from '../../models';
|
|
2
|
-
export declare const createTestModel: (payload?: {}
|
|
2
|
+
export declare const createTestModel: (payload?: {}) => Promise<TestModel>;
|
|
3
3
|
export declare const createTestModels: (fleetId: any, total: number) => Promise<TestModel[]>;
|
|
4
4
|
export declare const upsertTestModel: (payload: any) => Promise<[TestModel, boolean]>;
|
|
5
5
|
export declare const destroyTestModels: () => Promise<number>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getTestModelWithAssociation = exports.createTestModelWithAssociation = exports.updateTestModel = exports.getSomeTestModels = exports.getTestModel = exports.destroyTestModels = exports.upsertTestModel = exports.createTestModels = exports.createTestModel = void 0;
|
|
4
4
|
const models_1 = require("../../models");
|
|
5
|
-
const createTestModel = (payload = {}
|
|
5
|
+
const createTestModel = (payload = {}) => models_1.TestModel.create(payload);
|
|
6
6
|
exports.createTestModel = createTestModel;
|
|
7
7
|
const createTestModels = (fleetId, total) => {
|
|
8
8
|
const models = [];
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
export type ModelFetcher = (name: string) => any;
|
|
2
2
|
export type ModelOptions = {
|
|
3
|
-
scopeAttributeReplacer?: (attributes?: string[]) => Promise<any>;
|
|
4
|
-
};
|
|
5
|
-
export type Models = {
|
|
6
3
|
name: string;
|
|
7
4
|
scopeAttributes: string[];
|
|
8
|
-
modelOptions?: ModelOptions;
|
|
9
5
|
creationWebhookHandler?: (instance: any) => any;
|
|
10
6
|
updateWebhookHandler?: (instance: any) => any;
|
|
11
7
|
deletionWebhookHandler?: (instance: any) => any;
|
|
12
8
|
};
|
|
13
9
|
export type CustomFieldOptions = {
|
|
14
|
-
models:
|
|
10
|
+
models: ModelOptions[];
|
|
15
11
|
databaseConfig: any;
|
|
16
12
|
getUser: () => any;
|
|
17
13
|
};
|
package/dist/utils/init.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ModelFetcher,
|
|
2
|
-
export declare const addHooks: (models:
|
|
3
|
-
export declare const removeHooks: (models:
|
|
4
|
-
export declare const addScopes: (models:
|
|
1
|
+
import type { ModelFetcher, ModelOptions } from '../types';
|
|
2
|
+
export declare const addHooks: (models: ModelOptions[], getModel: ModelFetcher) => void;
|
|
3
|
+
export declare const removeHooks: (models: ModelOptions[], getModel: ModelFetcher) => void;
|
|
4
|
+
export declare const addScopes: (models: ModelOptions[], getModel: ModelFetcher) => void;
|
package/dist/utils/init.js
CHANGED
|
@@ -12,7 +12,7 @@ const scopes_1 = require("../scopes");
|
|
|
12
12
|
const logger_1 = __importDefault(require("./logger"));
|
|
13
13
|
const { CUSTOM_FIELDS_FILTER_SCOPE } = common_types_1.customFields;
|
|
14
14
|
const addHooks = (models, getModel) => {
|
|
15
|
-
models.forEach(async ({ name, scopeAttributes
|
|
15
|
+
models.forEach(async ({ name, scopeAttributes }) => {
|
|
16
16
|
try {
|
|
17
17
|
const model = getModel(name);
|
|
18
18
|
if (!model) {
|
|
@@ -31,11 +31,11 @@ const addHooks = (models, getModel) => {
|
|
|
31
31
|
model.addHook('beforeFind', 'sadot-beforeFind', (0, hooks_1.beforeFind)(scopeAttributes));
|
|
32
32
|
model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', hooks_1.beforeBulkCreate);
|
|
33
33
|
model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', hooks_1.beforeBulkUpdate);
|
|
34
|
-
model.addHook('beforeCreate', 'sadot-beforeCreate', (0, hooks_1.beforeCreate)(scopeAttributes
|
|
35
|
-
model.addHook('beforeUpdate', 'sadot-beforeUpdate', (0, hooks_1.beforeUpdate)(scopeAttributes
|
|
36
|
-
model.addHook('afterFind', 'sadot-afterFind', (0, hooks_1.enrichResults)(name, scopeAttributes, 'afterFind'
|
|
37
|
-
model.addHook('afterUpdate', 'sadot-afterUpdate', (0, hooks_1.enrichResults)(name, scopeAttributes
|
|
38
|
-
model.addHook('afterCreate', 'sadot-afterCreate', (0, hooks_1.enrichResults)(name, scopeAttributes
|
|
34
|
+
model.addHook('beforeCreate', 'sadot-beforeCreate', (0, hooks_1.beforeCreate)(scopeAttributes));
|
|
35
|
+
model.addHook('beforeUpdate', 'sadot-beforeUpdate', (0, hooks_1.beforeUpdate)(scopeAttributes));
|
|
36
|
+
model.addHook('afterFind', 'sadot-afterFind', (0, hooks_1.enrichResults)(name, scopeAttributes, 'afterFind'));
|
|
37
|
+
model.addHook('afterUpdate', 'sadot-afterUpdate', (0, hooks_1.enrichResults)(name, scopeAttributes));
|
|
38
|
+
model.addHook('afterCreate', 'sadot-afterCreate', (0, hooks_1.enrichResults)(name, scopeAttributes));
|
|
39
39
|
}
|
|
40
40
|
catch (e) {
|
|
41
41
|
logger_1.default.error(`Could not add custom fields hook to model ${name}. `, e);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/sadot",
|
|
3
|
-
"version": "0.5.4-beta.
|
|
3
|
+
"version": "0.5.4-beta.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,24 +9,7 @@
|
|
|
9
9
|
"linter": "./node_modules/.bin/eslint .",
|
|
10
10
|
"test": "jest --forceExit --runInBand",
|
|
11
11
|
"coverage": "jest --coverage --forceExit --runInBand && rm -rf ./coverage",
|
|
12
|
-
"
|
|
13
|
-
"dev": "nodemon",
|
|
14
|
-
"watch": "npm-watch build-to-local-repo"
|
|
15
|
-
},
|
|
16
|
-
"watch": {
|
|
17
|
-
"build-to-local-repo": {
|
|
18
|
-
"extensions": [
|
|
19
|
-
"js",
|
|
20
|
-
"jsx",
|
|
21
|
-
"ts",
|
|
22
|
-
"tsx",
|
|
23
|
-
"css"
|
|
24
|
-
],
|
|
25
|
-
"patterns": [
|
|
26
|
-
"src"
|
|
27
|
-
],
|
|
28
|
-
"quiet": false
|
|
29
|
-
}
|
|
12
|
+
"dev": "nodemon"
|
|
30
13
|
},
|
|
31
14
|
"dependencies": {
|
|
32
15
|
"@autofleet/common-types": "^1.7.29",
|
|
@@ -35,7 +18,6 @@
|
|
|
35
18
|
"@autofleet/logger": "^2.0.5",
|
|
36
19
|
"express": "^4.18.2",
|
|
37
20
|
"joi": "^17.7.0",
|
|
38
|
-
"npm-watch": "^0.11.0",
|
|
39
21
|
"pg": "^8.10.0",
|
|
40
22
|
"reflect-metadata": "^0.1.13",
|
|
41
23
|
"sequelize": "^6.31.1",
|
|
@@ -59,4 +41,4 @@
|
|
|
59
41
|
},
|
|
60
42
|
"author": "Autofleet",
|
|
61
43
|
"license": "ISC"
|
|
62
|
-
}
|
|
44
|
+
}
|
|
@@ -65,8 +65,10 @@ router.get('/', async (req, res) => {
|
|
|
65
65
|
if (entityIds?.length > 0) {
|
|
66
66
|
where.entityId = entityIds;
|
|
67
67
|
}
|
|
68
|
-
const customFieldDefinitions = await DefinitionRepo.findAll(
|
|
69
|
-
{
|
|
68
|
+
const customFieldDefinitions = await DefinitionRepo.findAll(
|
|
69
|
+
{ ...where },
|
|
70
|
+
{ withDisabled: true },
|
|
71
|
+
);
|
|
70
72
|
return res.json(customFieldDefinitions);
|
|
71
73
|
} catch (err) {
|
|
72
74
|
logger.error('Failed to fetch custom field definitions', err);
|
package/src/hooks/create.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import logger from '../utils/logger';
|
|
1
2
|
import * as ValueRepo from '../repository/value';
|
|
2
3
|
import * as DefinitionRepo from '../repository/definition';
|
|
3
4
|
import { MissingRequiredCustomFieldError } from '../errors';
|
|
4
|
-
import { ModelOptions } from '../types';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* A hook to create the custom fields when updating a model (more then one instance).
|
|
@@ -15,18 +15,14 @@ export const beforeBulkCreate = (options): void => {
|
|
|
15
15
|
* A hook to create the custom fields when updating a model instance.
|
|
16
16
|
* TODO - cleanup if update fail
|
|
17
17
|
*/
|
|
18
|
-
export const beforeCreate = (scopeAttributes: string[]
|
|
18
|
+
export const beforeCreate = (scopeAttributes: string[]) => async (
|
|
19
19
|
instance,
|
|
20
20
|
options,
|
|
21
21
|
): Promise<void> => {
|
|
22
|
+
logger.debug('sadot - before create hook');
|
|
22
23
|
const { fields } = options;
|
|
23
24
|
const modelType = instance.constructor.name;
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
let identifiers = scopeAttributes.map((attribute) => instance[attribute]);
|
|
27
|
-
if (scopeAttributeReplacer) {
|
|
28
|
-
identifiers = await scopeAttributeReplacer(identifiers);
|
|
29
|
-
}
|
|
25
|
+
const identifiers = scopeAttributes.map((attribute) => instance[attribute]);
|
|
30
26
|
// get all model's required definitions
|
|
31
27
|
const requiredFieldsNames = await DefinitionRepo.getRequiredFields(modelType,
|
|
32
28
|
instance.id,
|
|
@@ -45,11 +41,8 @@ export const beforeCreate = (scopeAttributes: string[], modelOptions: ModelOptio
|
|
|
45
41
|
instance.id,
|
|
46
42
|
identifiers,
|
|
47
43
|
customFields,
|
|
48
|
-
{
|
|
49
|
-
transaction: options.transaction,
|
|
50
|
-
},
|
|
44
|
+
{ transaction: options.transaction },
|
|
51
45
|
);
|
|
52
|
-
|
|
53
46
|
// eslint-disable-next-line no-param-reassign
|
|
54
47
|
fields.splice(customFieldsIdx, 1);
|
|
55
48
|
} else if (requiredFieldsNames?.length > 0) {
|
package/src/hooks/enrich.ts
CHANGED
|
@@ -4,8 +4,6 @@ import * as DefinitionRepo from '../repository/definition';
|
|
|
4
4
|
import CustomFieldValue from '../models/CustomFieldValue';
|
|
5
5
|
import CustomFieldDefinition from '../models/CustomFieldDefinition';
|
|
6
6
|
import { SerializedCustomFields } from '../types/definition';
|
|
7
|
-
import logger from '../utils/logger';
|
|
8
|
-
import { ModelOptions } from '../types';
|
|
9
7
|
|
|
10
8
|
type SupportedHookTypes = 'afterFind' | 'afterCreate' | 'afterUpdate';
|
|
11
9
|
|
|
@@ -31,14 +29,11 @@ const serializeCustomFields = (
|
|
|
31
29
|
const enrichResults = (
|
|
32
30
|
modelType: string,
|
|
33
31
|
scopeAttributes: string[],
|
|
34
|
-
getScope,
|
|
35
32
|
hookType?: SupportedHookTypes,
|
|
36
|
-
modelOptions?: ModelOptions,
|
|
37
33
|
) => async (
|
|
38
34
|
instancesOrInstance: any | any[],
|
|
39
35
|
options,
|
|
40
36
|
): Promise<void> => {
|
|
41
|
-
const { scopeAttributeReplacer } = modelOptions;
|
|
42
37
|
if (
|
|
43
38
|
options.originalAttributes?.length > 0
|
|
44
39
|
&& !options.originalAttributes?.includes?.('customFields')
|
|
@@ -53,27 +48,19 @@ const enrichResults = (
|
|
|
53
48
|
|
|
54
49
|
instances = instances.filter(Boolean);
|
|
55
50
|
|
|
56
|
-
|
|
51
|
+
const identifiers = instances.map((instance) =>
|
|
57
52
|
scopeAttributes
|
|
58
53
|
.map((attr) => instance[attr])).flat();
|
|
59
54
|
|
|
60
|
-
if (scopeAttributeReplacer) {
|
|
61
|
-
identifiers = await scopeAttributeReplacer(identifiers);
|
|
62
|
-
}
|
|
63
|
-
logger.info('enrichResults - sadot - indentifiers: ', identifiers);
|
|
64
|
-
|
|
65
55
|
const uniqueIdentifiers = [...new Set(identifiers)].filter(Boolean);
|
|
66
|
-
|
|
67
56
|
const identifierCustomFieldDefinitionsMapping = uniqueIdentifiers.reduce((map, identifier) => ({
|
|
68
57
|
...map,
|
|
69
58
|
[identifier]: [],
|
|
70
|
-
|
|
71
59
|
}), {});
|
|
72
|
-
|
|
73
60
|
const customFieldDefinitions = await DefinitionRepo.findByEntityIds(
|
|
74
61
|
modelType,
|
|
75
62
|
uniqueIdentifiers,
|
|
76
|
-
{ transaction: options.transaction
|
|
63
|
+
{ transaction: options.transaction },
|
|
77
64
|
);
|
|
78
65
|
|
|
79
66
|
const definitionsMap = customFieldDefinitions.reduce((map, definition) => ({
|
|
@@ -82,7 +69,7 @@ const enrichResults = (
|
|
|
82
69
|
}), {});
|
|
83
70
|
|
|
84
71
|
customFieldDefinitions.forEach((cfd) => {
|
|
85
|
-
identifierCustomFieldDefinitionsMapping[cfd.entityId]
|
|
72
|
+
identifierCustomFieldDefinitionsMapping[cfd.entityId].push(cfd);
|
|
86
73
|
});
|
|
87
74
|
|
|
88
75
|
// Get the values per instates ids:
|
|
@@ -117,7 +104,6 @@ const enrichResults = (
|
|
|
117
104
|
|
|
118
105
|
scopeAttributes.forEach((attribute) => {
|
|
119
106
|
const identifier = instance[attribute];
|
|
120
|
-
|
|
121
107
|
const entityCustomFieldDefinitions = identifierCustomFieldDefinitionsMapping[identifier];
|
|
122
108
|
if (entityCustomFieldDefinitions?.length > 0) {
|
|
123
109
|
entityCustomFieldDefinitions.forEach((customFieldDefinition) => {
|
package/src/hooks/find.ts
CHANGED
|
@@ -16,7 +16,6 @@ const doScopeAttributesMissing = (
|
|
|
16
16
|
// eslint-disable-next-line import/prefer-default-export
|
|
17
17
|
export const beforeFind = (scopeAttributes: string[]) => (options): void => {
|
|
18
18
|
const { attributes: queryAttributes } = options;
|
|
19
|
-
|
|
20
19
|
if (queryAttributes?.includes?.('customFields')) {
|
|
21
20
|
const missingScopeAttributes = doScopeAttributesMissing(scopeAttributes, queryAttributes);
|
|
22
21
|
logger.debug('sadot - before find hook');
|
package/src/hooks/update.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import logger from '../utils/logger';
|
|
2
2
|
import * as ValueRepo from '../repository/value';
|
|
3
|
-
import { ModelOptions } from '../types';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* A hook to update the custom fields when updating a model (more then one instance).
|
|
@@ -15,18 +14,15 @@ export const beforeBulkUpdate = (options): void => {
|
|
|
15
14
|
* A hook to update the custom fields when updating a model instance.
|
|
16
15
|
* TODO - cleanup if update fail
|
|
17
16
|
*/
|
|
18
|
-
export const beforeUpdate = (scopeAttributes: string[]
|
|
17
|
+
export const beforeUpdate = (scopeAttributes: string[]) => async (
|
|
19
18
|
instance,
|
|
20
19
|
options,
|
|
21
20
|
): Promise<void> => {
|
|
22
21
|
logger.debug('sadot - before update hook');
|
|
23
22
|
const { fields } = options;
|
|
24
|
-
const { scopeAttributeReplacer } = modelOptions;
|
|
25
23
|
const modelType = instance.constructor.name;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
identifiers = await scopeAttributeReplacer(identifiers);
|
|
29
|
-
}
|
|
24
|
+
const identifiers = scopeAttributes.map((attribute) => instance[attribute]);
|
|
25
|
+
|
|
30
26
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
31
27
|
if (customFieldsIdx > -1) {
|
|
32
28
|
const { customFields } = instance;
|
|
@@ -36,7 +32,6 @@ export const beforeUpdate = (scopeAttributes: string[], modelOptions: ModelOptio
|
|
|
36
32
|
identifiers,
|
|
37
33
|
customFields,
|
|
38
34
|
{ transaction: options.transaction },
|
|
39
|
-
|
|
40
35
|
);
|
|
41
36
|
// eslint-disable-next-line no-param-reassign
|
|
42
37
|
fields.splice(customFieldsIdx, 1);
|
package/src/models/index.ts
CHANGED
|
@@ -5,12 +5,10 @@ import logger from '../utils/logger';
|
|
|
5
5
|
import CustomFieldDefinition from './CustomFieldDefinition';
|
|
6
6
|
import CustomFieldValue from './CustomFieldValue';
|
|
7
7
|
import TestModel from './tests/TestModel';
|
|
8
|
-
import ContextAwareTestModel from './tests/contextAwareModels/ContextAwareTestModel';
|
|
9
|
-
import ContextModel from './tests/contextAwareModels/ContextModel';
|
|
10
8
|
import AssociatedTestModel from './tests/AssociatedTestModel';
|
|
11
9
|
|
|
12
10
|
const productionModels = [CustomFieldDefinition, CustomFieldValue];
|
|
13
|
-
const testModels = [TestModel, AssociatedTestModel
|
|
11
|
+
const testModels = [TestModel, AssociatedTestModel];
|
|
14
12
|
|
|
15
13
|
const SADOT_MIGRATION_PREFIX = 'sadot-migration';
|
|
16
14
|
const SCHEMA_VERSION = 1;
|
|
@@ -90,8 +88,6 @@ const initTestModels = async (sequelize: Sequelize): Promise<void> => {
|
|
|
90
88
|
logger.info('custom-fields: test models added');
|
|
91
89
|
await TestModel.sync({ alter: true });
|
|
92
90
|
await AssociatedTestModel.sync({ alter: true });
|
|
93
|
-
await ContextModel.sync({ alter: true });
|
|
94
|
-
await ContextAwareTestModel.sync({ alter: true });
|
|
95
91
|
logger.info('custom-fields: test models synced');
|
|
96
92
|
};
|
|
97
93
|
|
|
@@ -100,8 +96,6 @@ export {
|
|
|
100
96
|
CustomFieldDefinition,
|
|
101
97
|
TestModel,
|
|
102
98
|
AssociatedTestModel,
|
|
103
|
-
ContextAwareTestModel,
|
|
104
|
-
ContextModel,
|
|
105
99
|
initTables,
|
|
106
100
|
initTestModels,
|
|
107
101
|
};
|
|
@@ -1,26 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Op, WhereOptions,
|
|
3
|
-
} from 'sequelize';
|
|
1
|
+
import { Op, WhereOptions } from 'sequelize';
|
|
4
2
|
import { CustomFieldDefinition } from '../models';
|
|
5
3
|
import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../types/definition';
|
|
6
|
-
import logger from '../utils/logger';
|
|
7
4
|
|
|
8
5
|
export const create = (data: CreateCustomFieldDefinition): Promise<CustomFieldDefinition> =>
|
|
9
6
|
CustomFieldDefinition.create(data);
|
|
10
7
|
|
|
11
8
|
export const findAll = (
|
|
12
|
-
where:
|
|
9
|
+
where: any,
|
|
13
10
|
options: any = { withDisabled: false },
|
|
14
11
|
): Promise<CustomFieldDefinition[]> => {
|
|
15
12
|
const queryModel = options.withDisabled
|
|
16
13
|
? CustomFieldDefinition.unscoped()
|
|
17
14
|
: CustomFieldDefinition;
|
|
18
|
-
|
|
19
|
-
logger.info('sadot - whereQuery', { options });
|
|
20
15
|
return queryModel.scope('userScope').findAll({
|
|
21
16
|
where,
|
|
17
|
+
transaction: options.transaction,
|
|
22
18
|
raw: true,
|
|
23
|
-
logging: true,
|
|
24
19
|
});
|
|
25
20
|
};
|
|
26
21
|
|
|
@@ -40,6 +35,14 @@ export const findById = (
|
|
|
40
35
|
return CustomFieldDefinition.scope('userScope').findByPk(id);
|
|
41
36
|
};
|
|
42
37
|
|
|
38
|
+
export const findByEntityId = async (
|
|
39
|
+
entityId: string,
|
|
40
|
+
options: any = {},
|
|
41
|
+
): Promise<CustomFieldDefinition[]> => CustomFieldDefinition.findAll({
|
|
42
|
+
where: { entityId },
|
|
43
|
+
transaction: options.transaction,
|
|
44
|
+
});
|
|
45
|
+
|
|
43
46
|
export const findByEntityIds = async (
|
|
44
47
|
modelType: string,
|
|
45
48
|
entityIds: string[],
|
|
@@ -50,7 +53,6 @@ export const findByEntityIds = async (
|
|
|
50
53
|
entityId: entityIds,
|
|
51
54
|
},
|
|
52
55
|
transaction: options.transaction,
|
|
53
|
-
include: options.include,
|
|
54
56
|
raw: true,
|
|
55
57
|
});
|
|
56
58
|
|
|
@@ -101,13 +103,9 @@ export const getRequiredFields = async (
|
|
|
101
103
|
): Promise<string[]> => {
|
|
102
104
|
const entityIds = Array.isArray(entityId) ? entityId : [entityId];
|
|
103
105
|
const requiredFields = await CustomFieldDefinition.findAll({
|
|
104
|
-
where: {
|
|
105
|
-
required: true,
|
|
106
|
-
modelType,
|
|
107
|
-
entityId: { [Op.in]: entityIds },
|
|
108
|
-
},
|
|
106
|
+
where: { required: true, modelType, entityId: { [Op.in]: entityIds } },
|
|
109
107
|
logging: true,
|
|
110
108
|
});
|
|
111
|
-
const requiredFieldsNames = requiredFields.map((definition
|
|
109
|
+
const requiredFieldsNames = requiredFields.map((definition) => definition.name);
|
|
112
110
|
return [...new Set(requiredFieldsNames)];
|
|
113
111
|
};
|
package/src/repository/value.ts
CHANGED
|
@@ -52,19 +52,13 @@ export const updateValues = async (
|
|
|
52
52
|
valuesToUpdate: ValuesToUpdate,
|
|
53
53
|
options: any = {},
|
|
54
54
|
): Promise<CustomFieldValue[]> => {
|
|
55
|
-
|
|
56
|
-
logger.info(`custom-fields: updating values for ${modelType} ${modelId}`, {
|
|
57
|
-
names, options, valuesToUpdate, entityId: identifiers,
|
|
58
|
-
});
|
|
55
|
+
logger.info(`custom-fields: updating values for ${modelType} ${modelId}`, { valuesToUpdate });
|
|
59
56
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
name: names,
|
|
64
|
-
entityId: identifiers,
|
|
65
|
-
},
|
|
57
|
+
const names = Object.keys(valuesToUpdate);
|
|
58
|
+
const fieldDefinitions = await DefinitionRepo.findAll(
|
|
59
|
+
{ modelType, name: names, entityId: identifiers },
|
|
66
60
|
{ withDisabled: true, transaction: options.transaction },
|
|
67
|
-
)
|
|
61
|
+
) || [];
|
|
68
62
|
|
|
69
63
|
const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
|
|
70
64
|
if (fieldDefinitions.length !== names.length) {
|
|
@@ -4,7 +4,6 @@ export default {
|
|
|
4
4
|
password: process.env.DB_PASSWORD || null,
|
|
5
5
|
database: process.env.DB_NAME || 'sadot_package_test',
|
|
6
6
|
host: process.env.DB_HOST || '127.0.0.1',
|
|
7
|
-
port: process.env.DB_PORT || 5432,
|
|
8
7
|
dialect: process.env.DB_TYPE || 'postgres',
|
|
9
8
|
define: {
|
|
10
9
|
underscored: true,
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ContextAwareTestModel, ContextModel, CustomFieldDefinition, TestModel,
|
|
3
|
-
} from '../../models';
|
|
1
|
+
import { CustomFieldDefinition, TestModel } from '../../models';
|
|
4
2
|
|
|
5
3
|
// eslint-disable-next-line import/prefer-default-export
|
|
6
4
|
export const cleanup = async (): Promise<void> => {
|
|
7
5
|
if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development') {
|
|
8
6
|
await CustomFieldDefinition.unscoped().destroy({ where: {} });
|
|
9
7
|
await TestModel.destroy({ where: {} });
|
|
10
|
-
await ContextAwareTestModel.destroy({ where: {} });
|
|
11
|
-
await ContextModel.destroy({ where: {} });
|
|
12
8
|
}
|
|
13
9
|
};
|
|
14
10
|
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { v4 as uuidv4 } from 'uuid';
|
|
2
2
|
import { CreateCustomFieldDefinition, CustomFieldDefinitionDTO } from '../../types/definition';
|
|
3
3
|
|
|
4
|
-
export const contextAwareFieldDefinition = {
|
|
5
|
-
name: 'cool field',
|
|
6
|
-
modelType: 'ContextAwareTestModel',
|
|
7
|
-
fieldType: 'number',
|
|
8
|
-
entityType: 'fleetId',
|
|
9
|
-
};
|
|
10
|
-
|
|
11
4
|
export const coolFieldDefinition: CreateCustomFieldDefinition = {
|
|
12
5
|
name: 'cool field',
|
|
13
6
|
modelType: 'TestModel',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TestModel, AssociatedTestModel } from '../../models';
|
|
2
2
|
|
|
3
|
-
export const createTestModel = (payload = {}
|
|
3
|
+
export const createTestModel = (payload = {}) => TestModel.create(payload);
|
|
4
4
|
|
|
5
5
|
export const createTestModels = (fleetId, total: number) => {
|
|
6
6
|
const models: Promise<TestModel>[] = [];
|
package/src/types/index.ts
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
export type ModelFetcher = (name: string) => any;
|
|
2
2
|
|
|
3
3
|
export type ModelOptions = {
|
|
4
|
-
scopeAttributeReplacer?: (attributes?: string[]) => Promise<any>;
|
|
5
|
-
}
|
|
6
|
-
export type Models = {
|
|
7
4
|
name: string;
|
|
8
5
|
scopeAttributes: string[];
|
|
9
|
-
modelOptions?: ModelOptions
|
|
10
6
|
creationWebhookHandler?: (instance: any) => any;
|
|
11
7
|
updateWebhookHandler?: (instance: any) => any;
|
|
12
8
|
deletionWebhookHandler?: (instance: any) => any;
|
|
13
9
|
}
|
|
14
10
|
|
|
15
|
-
export type CustomFieldOptions
|
|
16
|
-
models:
|
|
11
|
+
export type CustomFieldOptions= {
|
|
12
|
+
models: ModelOptions[];
|
|
17
13
|
databaseConfig: any;
|
|
18
14
|
getUser: () => any;
|
|
19
15
|
};
|
package/src/utils/init.ts
CHANGED
|
@@ -14,14 +14,12 @@ import {
|
|
|
14
14
|
} from '../hooks';
|
|
15
15
|
import { customFieldsFilterScope } from '../scopes';
|
|
16
16
|
import logger from './logger';
|
|
17
|
-
import type { ModelFetcher,
|
|
17
|
+
import type { ModelFetcher, ModelOptions } from '../types';
|
|
18
18
|
|
|
19
19
|
const { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;
|
|
20
20
|
|
|
21
|
-
export const addHooks = (models:
|
|
22
|
-
models.forEach(async ({
|
|
23
|
-
name, scopeAttributes, modelOptions,
|
|
24
|
-
}) => {
|
|
21
|
+
export const addHooks = (models: ModelOptions[], getModel: ModelFetcher): void => {
|
|
22
|
+
models.forEach(async ({ name, scopeAttributes }) => {
|
|
25
23
|
try {
|
|
26
24
|
const model = getModel(name);
|
|
27
25
|
if (!model) {
|
|
@@ -40,18 +38,18 @@ export const addHooks = (models: Models[], getModel: ModelFetcher): void => {
|
|
|
40
38
|
model.addHook('beforeFind', 'sadot-beforeFind', beforeFind(scopeAttributes));
|
|
41
39
|
model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', beforeBulkCreate);
|
|
42
40
|
model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', beforeBulkUpdate);
|
|
43
|
-
model.addHook('beforeCreate', 'sadot-beforeCreate', beforeCreate(scopeAttributes
|
|
44
|
-
model.addHook('beforeUpdate', 'sadot-beforeUpdate', beforeUpdate(scopeAttributes
|
|
45
|
-
model.addHook('afterFind', 'sadot-afterFind', enrichResults(name, scopeAttributes, 'afterFind'
|
|
46
|
-
model.addHook('afterUpdate', 'sadot-afterUpdate', enrichResults(name, scopeAttributes
|
|
47
|
-
model.addHook('afterCreate', 'sadot-afterCreate', enrichResults(name, scopeAttributes
|
|
41
|
+
model.addHook('beforeCreate', 'sadot-beforeCreate', beforeCreate(scopeAttributes));
|
|
42
|
+
model.addHook('beforeUpdate', 'sadot-beforeUpdate', beforeUpdate(scopeAttributes));
|
|
43
|
+
model.addHook('afterFind', 'sadot-afterFind', enrichResults(name, scopeAttributes, 'afterFind'));
|
|
44
|
+
model.addHook('afterUpdate', 'sadot-afterUpdate', enrichResults(name, scopeAttributes));
|
|
45
|
+
model.addHook('afterCreate', 'sadot-afterCreate', enrichResults(name, scopeAttributes));
|
|
48
46
|
} catch (e) {
|
|
49
47
|
logger.error(`Could not add custom fields hook to model ${name}. `, e);
|
|
50
48
|
}
|
|
51
49
|
});
|
|
52
50
|
};
|
|
53
51
|
|
|
54
|
-
export const removeHooks = (models:
|
|
52
|
+
export const removeHooks = (models: ModelOptions[], getModel: ModelFetcher): void => {
|
|
55
53
|
models.forEach(async ({ name }) => {
|
|
56
54
|
try {
|
|
57
55
|
const model = getModel(name);
|
|
@@ -83,7 +81,7 @@ const addAssociations = (model: ModelCtor, modelName: string): void => {
|
|
|
83
81
|
CustomFieldValue.belongsTo(model, { foreignKey: 'modelId', as: modelName });
|
|
84
82
|
};
|
|
85
83
|
|
|
86
|
-
export const addScopes = (models:
|
|
84
|
+
export const addScopes = (models: ModelOptions[], getModel: ModelFetcher): void => {
|
|
87
85
|
models.forEach(async ({ name, scopeAttributes }) => {
|
|
88
86
|
try {
|
|
89
87
|
const model = getModel(name);
|
package/.env
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Model } from 'sequelize-typescript';
|
|
2
|
-
import ContextModel from './ContextModel';
|
|
3
|
-
declare class ContextAwareTestModel extends Model {
|
|
4
|
-
id: string;
|
|
5
|
-
contextId: string;
|
|
6
|
-
coolAttribute?: boolean;
|
|
7
|
-
customFields?: any;
|
|
8
|
-
context: ContextModel;
|
|
9
|
-
}
|
|
10
|
-
export default ContextAwareTestModel;
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
16
|
-
const sequelize_typescript_1 = require("sequelize-typescript");
|
|
17
|
-
const ContextModel_1 = __importDefault(require("./ContextModel"));
|
|
18
|
-
let ContextAwareTestModel = class ContextAwareTestModel extends sequelize_typescript_1.Model {
|
|
19
|
-
};
|
|
20
|
-
__decorate([
|
|
21
|
-
sequelize_typescript_1.PrimaryKey,
|
|
22
|
-
(0, sequelize_typescript_1.Column)({
|
|
23
|
-
type: sequelize_typescript_1.DataType.UUID,
|
|
24
|
-
defaultValue: sequelize_typescript_1.DataType.UUIDV4,
|
|
25
|
-
allowNull: false,
|
|
26
|
-
}),
|
|
27
|
-
__metadata("design:type", String)
|
|
28
|
-
], ContextAwareTestModel.prototype, "id", void 0);
|
|
29
|
-
__decorate([
|
|
30
|
-
(0, sequelize_typescript_1.ForeignKey)(() => ContextModel_1.default),
|
|
31
|
-
(0, sequelize_typescript_1.Column)({
|
|
32
|
-
type: sequelize_typescript_1.DataType.UUID,
|
|
33
|
-
}),
|
|
34
|
-
__metadata("design:type", String)
|
|
35
|
-
], ContextAwareTestModel.prototype, "contextId", void 0);
|
|
36
|
-
__decorate([
|
|
37
|
-
(0, sequelize_typescript_1.Column)({
|
|
38
|
-
type: sequelize_typescript_1.DataType.BOOLEAN,
|
|
39
|
-
}),
|
|
40
|
-
__metadata("design:type", Boolean)
|
|
41
|
-
], ContextAwareTestModel.prototype, "coolAttribute", void 0);
|
|
42
|
-
__decorate([
|
|
43
|
-
(0, sequelize_typescript_1.Column)({
|
|
44
|
-
type: sequelize_typescript_1.DataType.VIRTUAL,
|
|
45
|
-
}),
|
|
46
|
-
__metadata("design:type", Object)
|
|
47
|
-
], ContextAwareTestModel.prototype, "customFields", void 0);
|
|
48
|
-
__decorate([
|
|
49
|
-
(0, sequelize_typescript_1.BelongsTo)(() => ContextModel_1.default),
|
|
50
|
-
__metadata("design:type", ContextModel_1.default)
|
|
51
|
-
], ContextAwareTestModel.prototype, "context", void 0);
|
|
52
|
-
ContextAwareTestModel = __decorate([
|
|
53
|
-
(0, sequelize_typescript_1.Table)({ createdAt: false, updatedAt: false })
|
|
54
|
-
], ContextAwareTestModel);
|
|
55
|
-
exports.default = ContextAwareTestModel;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Model } from 'sequelize-typescript';
|
|
2
|
-
declare enum EEntityTypes {
|
|
3
|
-
BUSINESS_MODEL = "businessModel",
|
|
4
|
-
CONTEXT = "context",
|
|
5
|
-
DEMAND_SOURCE = "demandSource",
|
|
6
|
-
FLEET = "fleet"
|
|
7
|
-
}
|
|
8
|
-
declare class ContextModel extends Model {
|
|
9
|
-
id: string;
|
|
10
|
-
entityId: string;
|
|
11
|
-
entityType: EEntityTypes;
|
|
12
|
-
}
|
|
13
|
-
export default ContextModel;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
13
|
-
const sequelize_typescript_1 = require("sequelize-typescript");
|
|
14
|
-
// eslint-disable-next-line no-shadow
|
|
15
|
-
var EEntityTypes;
|
|
16
|
-
(function (EEntityTypes) {
|
|
17
|
-
EEntityTypes["BUSINESS_MODEL"] = "businessModel";
|
|
18
|
-
EEntityTypes["CONTEXT"] = "context";
|
|
19
|
-
EEntityTypes["DEMAND_SOURCE"] = "demandSource";
|
|
20
|
-
EEntityTypes["FLEET"] = "fleet";
|
|
21
|
-
})(EEntityTypes || (EEntityTypes = {}));
|
|
22
|
-
let ContextModel = class ContextModel extends sequelize_typescript_1.Model {
|
|
23
|
-
};
|
|
24
|
-
__decorate([
|
|
25
|
-
sequelize_typescript_1.PrimaryKey,
|
|
26
|
-
(0, sequelize_typescript_1.Column)({
|
|
27
|
-
defaultValue: sequelize_typescript_1.DataType.UUIDV4,
|
|
28
|
-
type: sequelize_typescript_1.DataType.UUID,
|
|
29
|
-
}),
|
|
30
|
-
__metadata("design:type", String)
|
|
31
|
-
], ContextModel.prototype, "id", void 0);
|
|
32
|
-
__decorate([
|
|
33
|
-
(0, sequelize_typescript_1.Column)({
|
|
34
|
-
type: sequelize_typescript_1.DataType.UUID,
|
|
35
|
-
}),
|
|
36
|
-
__metadata("design:type", String)
|
|
37
|
-
], ContextModel.prototype, "entityId", void 0);
|
|
38
|
-
__decorate([
|
|
39
|
-
(0, sequelize_typescript_1.Column)({
|
|
40
|
-
type: sequelize_typescript_1.DataType.ENUM(...Object.values(EEntityTypes)),
|
|
41
|
-
}),
|
|
42
|
-
__metadata("design:type", String)
|
|
43
|
-
], ContextModel.prototype, "entityType", void 0);
|
|
44
|
-
ContextModel = __decorate([
|
|
45
|
-
(0, sequelize_typescript_1.Table)({ createdAt: false, updatedAt: false })
|
|
46
|
-
], ContextModel);
|
|
47
|
-
exports.default = ContextModel;
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
-
import {
|
|
3
|
-
Table,
|
|
4
|
-
Column,
|
|
5
|
-
Model,
|
|
6
|
-
PrimaryKey,
|
|
7
|
-
DataType,
|
|
8
|
-
HasMany,
|
|
9
|
-
BelongsTo,
|
|
10
|
-
ForeignKey,
|
|
11
|
-
} from 'sequelize-typescript';
|
|
12
|
-
import AssociatedTestModel from '../AssociatedTestModel';
|
|
13
|
-
import ContextModel from './ContextModel';
|
|
14
|
-
|
|
15
|
-
@Table({ createdAt: false, updatedAt: false })
|
|
16
|
-
class ContextAwareTestModel extends Model {
|
|
17
|
-
@PrimaryKey
|
|
18
|
-
@Column({
|
|
19
|
-
type: DataType.UUID,
|
|
20
|
-
defaultValue: DataType.UUIDV4,
|
|
21
|
-
allowNull: false,
|
|
22
|
-
})
|
|
23
|
-
id!: string;
|
|
24
|
-
|
|
25
|
-
@ForeignKey(() => ContextModel)
|
|
26
|
-
@Column({
|
|
27
|
-
type: DataType.UUID,
|
|
28
|
-
})
|
|
29
|
-
contextId: string;
|
|
30
|
-
|
|
31
|
-
@Column({
|
|
32
|
-
type: DataType.BOOLEAN,
|
|
33
|
-
})
|
|
34
|
-
coolAttribute?: boolean;
|
|
35
|
-
|
|
36
|
-
@Column({
|
|
37
|
-
type: DataType.VIRTUAL,
|
|
38
|
-
})
|
|
39
|
-
customFields?: any;
|
|
40
|
-
|
|
41
|
-
@BelongsTo(() => ContextModel)
|
|
42
|
-
context: ContextModel;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export default ContextAwareTestModel;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
-
import {
|
|
3
|
-
Table,
|
|
4
|
-
Column,
|
|
5
|
-
Model,
|
|
6
|
-
PrimaryKey,
|
|
7
|
-
DataType,
|
|
8
|
-
} from 'sequelize-typescript';
|
|
9
|
-
|
|
10
|
-
// eslint-disable-next-line no-shadow
|
|
11
|
-
enum EEntityTypes {
|
|
12
|
-
BUSINESS_MODEL = 'businessModel',
|
|
13
|
-
CONTEXT = 'context',
|
|
14
|
-
DEMAND_SOURCE = 'demandSource',
|
|
15
|
-
FLEET = 'fleet',
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
@Table({ createdAt: false, updatedAt: false })
|
|
19
|
-
class ContextModel extends Model {
|
|
20
|
-
@PrimaryKey
|
|
21
|
-
@Column({
|
|
22
|
-
defaultValue: DataType.UUIDV4,
|
|
23
|
-
type: DataType.UUID,
|
|
24
|
-
})
|
|
25
|
-
id: string;
|
|
26
|
-
|
|
27
|
-
@Column({
|
|
28
|
-
type: DataType.UUID,
|
|
29
|
-
})
|
|
30
|
-
entityId: string;
|
|
31
|
-
|
|
32
|
-
@Column({
|
|
33
|
-
type: DataType.ENUM(...Object.values(EEntityTypes)),
|
|
34
|
-
})
|
|
35
|
-
entityType: EEntityTypes;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export default ContextModel;
|