@autofleet/sadot 0.5.4-beta.23 → 0.5.4-beta.25
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/hooks/create.js +5 -3
- package/dist/hooks/enrich.js +3 -3
- package/dist/hooks/update.js +2 -3
- package/dist/index.js +1 -0
- package/dist/repository/definition.d.ts +6 -3
- package/dist/repository/definition.js +27 -13
- package/dist/repository/value.d.ts +5 -1
- package/dist/repository/value.js +9 -6
- package/dist/types/index.d.ts +16 -2
- package/dist/utils/init.d.ts +1 -0
- package/dist/utils/init.js +7 -1
- package/dist/utils/scopeAttributes.d.ts +2 -3
- package/dist/utils/scopeAttributes.js +4 -22
- package/package.json +1 -1
- package/src/hooks/create.ts +8 -5
- package/src/hooks/enrich.ts +6 -4
- package/src/hooks/find.ts +0 -1
- package/src/hooks/update.ts +3 -4
- package/src/index.ts +4 -1
- package/src/repository/definition.ts +34 -13
- package/src/repository/value.ts +15 -11
- package/src/types/index.ts +20 -2
- package/src/utils/init.ts +7 -0
- package/src/utils/scopeAttributes.ts +4 -24
package/dist/hooks/create.js
CHANGED
|
@@ -27,6 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.beforeCreate = exports.beforeBulkCreate = void 0;
|
|
30
|
+
const logger_1 = __importDefault(require("../utils/logger"));
|
|
30
31
|
const ValueRepo = __importStar(require("../repository/value"));
|
|
31
32
|
const DefinitionRepo = __importStar(require("../repository/definition"));
|
|
32
33
|
const errors_1 = require("../errors");
|
|
@@ -45,12 +46,12 @@ exports.beforeBulkCreate = beforeBulkCreate;
|
|
|
45
46
|
* TODO - cleanup if update fail
|
|
46
47
|
*/
|
|
47
48
|
const beforeCreate = (scopeAttributes, modelOptions = {}) => async (instance, options) => {
|
|
49
|
+
logger_1.default.debug('sadot - before create hook');
|
|
48
50
|
const { fields } = options;
|
|
49
51
|
const modelType = instance.constructor.name;
|
|
50
|
-
const
|
|
51
|
-
const identifiers = await (0, scopeAttributes_1.default)(instance, scopeAttributes, scopeAttributeReplacer);
|
|
52
|
+
const identifiers = (0, scopeAttributes_1.default)(instance, scopeAttributes);
|
|
52
53
|
// get all model's required definitions
|
|
53
|
-
const requiredFieldsNames = await DefinitionRepo.getRequiredFields(modelType, instance.id, identifiers);
|
|
54
|
+
const requiredFieldsNames = await DefinitionRepo.getRequiredFields(modelType, instance.id, identifiers, modelOptions);
|
|
54
55
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
55
56
|
const { customFields } = instance;
|
|
56
57
|
if (customFieldsIdx > -1 && customFields) {
|
|
@@ -61,6 +62,7 @@ const beforeCreate = (scopeAttributes, modelOptions = {}) => async (instance, op
|
|
|
61
62
|
}
|
|
62
63
|
await ValueRepo.updateValues(modelType, instance.id, identifiers, customFields, {
|
|
63
64
|
transaction: options.transaction,
|
|
65
|
+
modelOptions,
|
|
64
66
|
});
|
|
65
67
|
// eslint-disable-next-line no-param-reassign
|
|
66
68
|
fields.splice(customFieldsIdx, 1);
|
package/dist/hooks/enrich.js
CHANGED
|
@@ -46,7 +46,6 @@ const serializeCustomFields = (customFieldValues, customFieldDefinitionsHash) =>
|
|
|
46
46
|
* A hook to attach the custom fields when fetching a model instances.
|
|
47
47
|
*/
|
|
48
48
|
const enrichResults = (modelType, scopeAttributes, hookType, modelOptions = {}) => async (instancesOrInstance, options) => {
|
|
49
|
-
const { scopeAttributeReplacer } = modelOptions;
|
|
50
49
|
if (options.originalAttributes?.length > 0
|
|
51
50
|
&& !options.originalAttributes?.includes?.('customFields')) {
|
|
52
51
|
return;
|
|
@@ -56,14 +55,15 @@ const enrichResults = (modelType, scopeAttributes, hookType, modelOptions = {})
|
|
|
56
55
|
? instancesOrInstance
|
|
57
56
|
: [instancesOrInstance];
|
|
58
57
|
instances = instances.filter(Boolean);
|
|
59
|
-
const identifiers =
|
|
58
|
+
const identifiers = (0, scopeAttributes_1.default)(instances, scopeAttributes);
|
|
60
59
|
logger_1.default.info('enrichResults - sadot - indentifiers: ', identifiers);
|
|
61
60
|
const uniqueIdentifiers = [...new Set(identifiers)].filter(Boolean);
|
|
61
|
+
console.log({ uniqueIdentifiers });
|
|
62
62
|
const identifierCustomFieldDefinitionsMapping = uniqueIdentifiers.reduce((map, identifier) => ({
|
|
63
63
|
...map,
|
|
64
64
|
[identifier]: [],
|
|
65
65
|
}), {});
|
|
66
|
-
const customFieldDefinitions = await DefinitionRepo.findByEntityIds(modelType, uniqueIdentifiers, { transaction: options.transaction });
|
|
66
|
+
const customFieldDefinitions = await DefinitionRepo.findByEntityIds(modelType, uniqueIdentifiers, { transaction: options.transaction, modelOptions });
|
|
67
67
|
const definitionsMap = customFieldDefinitions.reduce((map, definition) => ({
|
|
68
68
|
...map,
|
|
69
69
|
[definition.id]: definition,
|
package/dist/hooks/update.js
CHANGED
|
@@ -46,13 +46,12 @@ exports.beforeBulkUpdate = beforeBulkUpdate;
|
|
|
46
46
|
const beforeUpdate = (scopeAttributes, modelOptions = {}) => async (instance, options) => {
|
|
47
47
|
logger_1.default.debug('sadot - before update hook');
|
|
48
48
|
const { fields } = options;
|
|
49
|
-
const { scopeAttributeReplacer } = modelOptions;
|
|
50
49
|
const modelType = instance.constructor.name;
|
|
51
|
-
const identifiers =
|
|
50
|
+
const identifiers = (0, scopeAttributes_1.default)(instance, scopeAttributes);
|
|
52
51
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
53
52
|
if (customFieldsIdx > -1) {
|
|
54
53
|
const { customFields } = instance;
|
|
55
|
-
await ValueRepo.updateValues(modelType, instance.id, identifiers, customFields, { transaction: options.transaction });
|
|
54
|
+
await ValueRepo.updateValues(modelType, instance.id, identifiers, customFields, { transaction: options.transaction, modelOptions });
|
|
56
55
|
// eslint-disable-next-line no-param-reassign
|
|
57
56
|
fields.splice(customFieldsIdx, 1);
|
|
58
57
|
}
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,7 @@ const useCustomFields = async (app, getModel, options) => {
|
|
|
42
42
|
(0, init_1.addHooks)(models, getModel);
|
|
43
43
|
await (0, models_1.initTables)(sequelize, options.getUser);
|
|
44
44
|
(0, init_1.addScopes)(models, getModel);
|
|
45
|
+
(0, init_1.applyCustomAssociation)(models);
|
|
45
46
|
logger_1.default.debug('sadot - custom fields finished initializing with models', models);
|
|
46
47
|
return sequelize;
|
|
47
48
|
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { WhereOptions } from 'sequelize';
|
|
1
|
+
import { FindOptions, WhereOptions } from 'sequelize';
|
|
2
2
|
import { CustomFieldDefinition } from '../models';
|
|
3
3
|
import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../types/definition';
|
|
4
|
+
import { ModelOptions } from '../types';
|
|
4
5
|
export declare const create: (data: CreateCustomFieldDefinition) => Promise<CustomFieldDefinition>;
|
|
5
6
|
export declare const findAll: (where: WhereOptions, options?: any) => Promise<CustomFieldDefinition[]>;
|
|
6
7
|
export declare const findByIds: (ids: string[], options?: any) => Promise<CustomFieldDefinition[]>;
|
|
7
8
|
export declare const findById: (id: string, options?: any) => Promise<CustomFieldDefinition | null>;
|
|
8
|
-
export declare const findByEntityIds: (modelType: string, entityIds: string[], options?:
|
|
9
|
+
export declare const findByEntityIds: (modelType: string, entityIds: string[], options?: FindOptions & {
|
|
10
|
+
modelOptions?: ModelOptions;
|
|
11
|
+
}) => Promise<CustomFieldDefinition[]>;
|
|
9
12
|
export declare const findByWhere: (where: any) => Promise<CustomFieldDefinition | null>;
|
|
10
13
|
export declare const findDefinitionsByModels: (modelTypes: string[], options?: any) => Promise<CustomFieldDefinition[]>;
|
|
11
14
|
export declare const update: (id: string, data: UpdateCustomFieldDefinition) => Promise<CustomFieldDefinition>;
|
|
@@ -14,4 +17,4 @@ export declare const destroy: (id: string) => Promise<any>;
|
|
|
14
17
|
/**
|
|
15
18
|
* Return the names of the required fields for a given model
|
|
16
19
|
*/
|
|
17
|
-
export declare const getRequiredFields: (modelType: string, modelId: string | string[], entityId: string | string[]) => Promise<string[]>;
|
|
20
|
+
export declare const getRequiredFields: (modelType: string, modelId: string | string[], entityId: string | string[], modelOptions?: ModelOptions) => Promise<string[]>;
|
|
@@ -13,6 +13,8 @@ const findAll = (where, options = { withDisabled: false }) => {
|
|
|
13
13
|
where,
|
|
14
14
|
transaction: options.transaction,
|
|
15
15
|
raw: true,
|
|
16
|
+
include: options.include,
|
|
17
|
+
logging: console.log,
|
|
16
18
|
});
|
|
17
19
|
};
|
|
18
20
|
exports.findAll = findAll;
|
|
@@ -26,14 +28,21 @@ const findById = (id, options = { withDisabled: false }) => {
|
|
|
26
28
|
return models_1.CustomFieldDefinition.scope('userScope').findByPk(id);
|
|
27
29
|
};
|
|
28
30
|
exports.findById = findById;
|
|
29
|
-
const findByEntityIds = async (modelType, entityIds, options = {}) =>
|
|
30
|
-
|
|
31
|
+
const findByEntityIds = async (modelType, entityIds, options = {}) => {
|
|
32
|
+
const { include, useEntityIdFromInclude } = options.modelOptions;
|
|
33
|
+
const where = {
|
|
31
34
|
modelType,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
};
|
|
36
|
+
if (!useEntityIdFromInclude) {
|
|
37
|
+
where.entityId = entityIds;
|
|
38
|
+
}
|
|
39
|
+
return models_1.CustomFieldDefinition.findAll({
|
|
40
|
+
where,
|
|
41
|
+
transaction: options.transaction,
|
|
42
|
+
include: include?.(entityIds),
|
|
43
|
+
raw: true,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
37
46
|
exports.findByEntityIds = findByEntityIds;
|
|
38
47
|
const findByWhere = (where) => models_1.CustomFieldDefinition.scope('userScope').findOne({
|
|
39
48
|
where,
|
|
@@ -63,14 +72,19 @@ exports.destroy = destroy;
|
|
|
63
72
|
/**
|
|
64
73
|
* Return the names of the required fields for a given model
|
|
65
74
|
*/
|
|
66
|
-
const getRequiredFields = async (modelType, modelId, entityId) => {
|
|
75
|
+
const getRequiredFields = async (modelType, modelId, entityId, modelOptions = {}) => {
|
|
67
76
|
const entityIds = Array.isArray(entityId) ? entityId : [entityId];
|
|
77
|
+
const { include, useEntityIdFromInclude } = modelOptions;
|
|
78
|
+
const where = {
|
|
79
|
+
modelType,
|
|
80
|
+
required: true,
|
|
81
|
+
};
|
|
82
|
+
if (!useEntityIdFromInclude) {
|
|
83
|
+
where.entityId = entityIds;
|
|
84
|
+
}
|
|
68
85
|
const requiredFields = await models_1.CustomFieldDefinition.findAll({
|
|
69
|
-
where
|
|
70
|
-
|
|
71
|
-
modelType,
|
|
72
|
-
entityId: { [sequelize_1.Op.in]: entityIds },
|
|
73
|
-
},
|
|
86
|
+
where,
|
|
87
|
+
include: include?.(entityIds),
|
|
74
88
|
logging: true,
|
|
75
89
|
});
|
|
76
90
|
const requiredFieldsNames = requiredFields.map((definition) => definition.name);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { FindOptions } from 'sequelize';
|
|
1
2
|
import { CustomFieldValue } from '../models';
|
|
2
3
|
import { CreateCustomFieldValue, ValuesToUpdate } from '../types/value';
|
|
4
|
+
import { ModelOptions } from '../types';
|
|
3
5
|
export declare const findByModelIdAndDefinition: (modelId: string, customFieldDefinitionId: string) => Promise<CustomFieldValue[]>;
|
|
4
6
|
export declare const create: (data: CreateCustomFieldValue, withAssociations?: boolean) => Promise<CustomFieldValue>;
|
|
5
7
|
export declare const findAllValues: () => Promise<CustomFieldValue[]>;
|
|
@@ -20,5 +22,7 @@ export declare const findValuesByModelIds: (modelIds: string[], options?: any) =
|
|
|
20
22
|
* Create new value record if not exists, but fails if value's definition not exist.
|
|
21
23
|
* Return the updated values
|
|
22
24
|
*/
|
|
23
|
-
export declare const updateValues: (modelType: string, modelId: string, identifiers: string[], valuesToUpdate: ValuesToUpdate, options?:
|
|
25
|
+
export declare const updateValues: (modelType: string, modelId: string, identifiers: string[], valuesToUpdate: ValuesToUpdate, options?: FindOptions & {
|
|
26
|
+
modelOptions?: ModelOptions;
|
|
27
|
+
}) => Promise<CustomFieldValue[]>;
|
|
24
28
|
export declare const deleteValue: (id: string, options?: any) => Promise<any>;
|
package/dist/repository/value.js
CHANGED
|
@@ -27,7 +27,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.deleteValue = exports.updateValues = exports.findValuesByModelIds = exports.findValuesByModelId = exports.findAllValues = exports.create = exports.findByModelIdAndDefinition = void 0;
|
|
30
|
-
/* eslint-disable max-len */
|
|
31
30
|
const models_1 = require("../models");
|
|
32
31
|
const DefinitionRepo = __importStar(require("./definition"));
|
|
33
32
|
const logger_1 = __importDefault(require("../utils/logger"));
|
|
@@ -74,14 +73,18 @@ exports.findValuesByModelIds = findValuesByModelIds;
|
|
|
74
73
|
*/
|
|
75
74
|
const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, options = {}) => {
|
|
76
75
|
const names = Object.keys(valuesToUpdate);
|
|
77
|
-
logger_1.default.info(`custom-
|
|
78
|
-
names, options, valuesToUpdate,
|
|
76
|
+
logger_1.default.info(`custom-fiel ds: updating values for ${modelType} ${modelId}`, {
|
|
77
|
+
names, options, valuesToUpdate, identifiers,
|
|
79
78
|
});
|
|
80
|
-
const
|
|
79
|
+
const { modelOptions, transaction } = options;
|
|
80
|
+
const where = {
|
|
81
81
|
modelType,
|
|
82
82
|
name: names,
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
};
|
|
84
|
+
if (!options.modelOptions?.useEntityIdFromInclude) {
|
|
85
|
+
where.entityId = identifiers;
|
|
86
|
+
}
|
|
87
|
+
const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true, transaction, include: modelOptions.include?.(identifiers) }) || [];
|
|
85
88
|
const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
|
|
86
89
|
if (fieldDefinitions.length !== names.length) {
|
|
87
90
|
logger_1.default.info(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
|
+
import { Includeable } from 'sequelize';
|
|
2
|
+
import { ModelCtor } from 'sequelize-typescript';
|
|
1
3
|
export type ModelFetcher = (name: string) => any;
|
|
2
4
|
export type ModelOptions = {
|
|
3
|
-
|
|
5
|
+
include?: (entityIds: string[]) => Includeable[];
|
|
6
|
+
/**
|
|
7
|
+
* A function that will be called to resolve the entity id.
|
|
8
|
+
* @param {string | string[]} - The entity id.
|
|
9
|
+
* @returns {void}
|
|
10
|
+
*/
|
|
11
|
+
customAssociation?: (model: ModelCtor) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Whether to use the entity id from the instance per scope attribute
|
|
14
|
+
* @param {string} -
|
|
15
|
+
* @returns {void}
|
|
16
|
+
*/
|
|
17
|
+
useEntityIdFromInclude?: boolean;
|
|
4
18
|
};
|
|
5
19
|
export type Models = {
|
|
6
20
|
name: string;
|
|
7
|
-
scopeAttributes:
|
|
21
|
+
scopeAttributes: any[];
|
|
8
22
|
modelOptions?: ModelOptions;
|
|
9
23
|
creationWebhookHandler?: (instance: any) => any;
|
|
10
24
|
updateWebhookHandler?: (instance: any) => any;
|
package/dist/utils/init.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ import type { ModelFetcher, Models } from '../types';
|
|
|
2
2
|
export declare const addHooks: (models: Models[], getModel: ModelFetcher) => void;
|
|
3
3
|
export declare const removeHooks: (models: Models[], getModel: ModelFetcher) => void;
|
|
4
4
|
export declare const addScopes: (models: Models[], getModel: ModelFetcher) => void;
|
|
5
|
+
export declare const applyCustomAssociation: (models: Models[]) => void;
|
package/dist/utils/init.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.addScopes = exports.removeHooks = exports.addHooks = void 0;
|
|
6
|
+
exports.applyCustomAssociation = exports.addScopes = exports.removeHooks = exports.addHooks = void 0;
|
|
7
7
|
const sequelize_1 = require("sequelize");
|
|
8
8
|
const common_types_1 = require("@autofleet/common-types");
|
|
9
9
|
const models_1 = require("../models");
|
|
@@ -98,3 +98,9 @@ const addScopes = (models, getModel) => {
|
|
|
98
98
|
});
|
|
99
99
|
};
|
|
100
100
|
exports.addScopes = addScopes;
|
|
101
|
+
const applyCustomAssociation = (models) => {
|
|
102
|
+
models.forEach(({ modelOptions }) => {
|
|
103
|
+
modelOptions?.customAssociation?.(models_1.CustomFieldDefinition);
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
exports.applyCustomAssociation = applyCustomAssociation;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export default getScopeAttributes;
|
|
1
|
+
declare const buildAttributesByInstance: (instance: any, scopeAttributes: string[]) => any[];
|
|
2
|
+
export default buildAttributesByInstance;
|
|
@@ -1,29 +1,11 @@
|
|
|
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
|
-
const logger_1 = __importDefault(require("./logger"));
|
|
7
|
-
const attributeCache = new Map();
|
|
8
3
|
const mapAttributeToInstance = (scopeAttributes, instance) => scopeAttributes.map((attr) => instance[attr]);
|
|
9
4
|
const buildAttributesByInstance = (instance, scopeAttributes) => {
|
|
5
|
+
const uniqueAttributes = Array.from(new Set(scopeAttributes));
|
|
10
6
|
if (Array.isArray(instance)) {
|
|
11
|
-
return instance.flatMap((ins) => mapAttributeToInstance(
|
|
12
|
-
}
|
|
13
|
-
return mapAttributeToInstance(scopeAttributes, instance);
|
|
14
|
-
};
|
|
15
|
-
const getScopeAttributes = async (instance, scopeAttributes, scopeAttributeReplacer) => {
|
|
16
|
-
logger_1.default.info('Getting scope attributes', { instance, scopeAttributes });
|
|
17
|
-
const attributes = buildAttributesByInstance(instance, scopeAttributes);
|
|
18
|
-
const attributeCacheKey = attributes.join();
|
|
19
|
-
const cachedAttributes = attributeCache.get(attributeCacheKey);
|
|
20
|
-
if (cachedAttributes) {
|
|
21
|
-
logger_1.default.info('Using cached attributes', { cachedAttributes });
|
|
22
|
-
return cachedAttributes;
|
|
7
|
+
return instance.flatMap((ins) => mapAttributeToInstance(uniqueAttributes, ins));
|
|
23
8
|
}
|
|
24
|
-
|
|
25
|
-
? await scopeAttributeReplacer(attributes) : attributes);
|
|
26
|
-
attributeCache.set(attributeCacheKey, attributeResult);
|
|
27
|
-
return attributeResult;
|
|
9
|
+
return mapAttributeToInstance(uniqueAttributes, instance);
|
|
28
10
|
};
|
|
29
|
-
exports.default =
|
|
11
|
+
exports.default = buildAttributesByInstance;
|
package/package.json
CHANGED
package/src/hooks/create.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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
5
|
import { ModelOptions } from '../types';
|
|
5
|
-
import
|
|
6
|
+
import buildAttributesByInstance from '../utils/scopeAttributes';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* A hook to create the custom fields when updating a model (more then one instance).
|
|
@@ -20,15 +21,16 @@ export const beforeCreate = (scopeAttributes: string[], modelOptions: ModelOptio
|
|
|
20
21
|
instance,
|
|
21
22
|
options,
|
|
22
23
|
): Promise<void> => {
|
|
24
|
+
logger.debug('sadot - before create hook');
|
|
23
25
|
const { fields } = options;
|
|
24
26
|
const modelType = instance.constructor.name;
|
|
25
|
-
const { scopeAttributeReplacer } = modelOptions;
|
|
26
27
|
|
|
27
|
-
const identifiers =
|
|
28
|
+
const identifiers = buildAttributesByInstance(instance, scopeAttributes);
|
|
29
|
+
|
|
28
30
|
// get all model's required definitions
|
|
29
31
|
const requiredFieldsNames = await DefinitionRepo.getRequiredFields(modelType,
|
|
30
|
-
instance.id,
|
|
31
|
-
|
|
32
|
+
instance.id, identifiers, modelOptions);
|
|
33
|
+
|
|
32
34
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
33
35
|
const { customFields } = instance;
|
|
34
36
|
if (customFieldsIdx > -1 && customFields) {
|
|
@@ -45,6 +47,7 @@ export const beforeCreate = (scopeAttributes: string[], modelOptions: ModelOptio
|
|
|
45
47
|
customFields,
|
|
46
48
|
{
|
|
47
49
|
transaction: options.transaction,
|
|
50
|
+
modelOptions,
|
|
48
51
|
},
|
|
49
52
|
);
|
|
50
53
|
|
package/src/hooks/enrich.ts
CHANGED
|
@@ -6,7 +6,7 @@ import CustomFieldDefinition from '../models/CustomFieldDefinition';
|
|
|
6
6
|
import { SerializedCustomFields } from '../types/definition';
|
|
7
7
|
import logger from '../utils/logger';
|
|
8
8
|
import { ModelOptions } from '../types';
|
|
9
|
-
import
|
|
9
|
+
import buildAttributesByInstance from '../utils/scopeAttributes';
|
|
10
10
|
|
|
11
11
|
type SupportedHookTypes = 'afterFind' | 'afterCreate' | 'afterUpdate';
|
|
12
12
|
|
|
@@ -38,7 +38,6 @@ const enrichResults = (
|
|
|
38
38
|
instancesOrInstance: any | any[],
|
|
39
39
|
options,
|
|
40
40
|
): Promise<void> => {
|
|
41
|
-
const { scopeAttributeReplacer } = modelOptions;
|
|
42
41
|
if (
|
|
43
42
|
options.originalAttributes?.length > 0
|
|
44
43
|
&& !options.originalAttributes?.includes?.('customFields')
|
|
@@ -53,11 +52,14 @@ const enrichResults = (
|
|
|
53
52
|
|
|
54
53
|
instances = instances.filter(Boolean);
|
|
55
54
|
|
|
56
|
-
const identifiers =
|
|
55
|
+
const identifiers = buildAttributesByInstance(instances, scopeAttributes);
|
|
56
|
+
|
|
57
57
|
logger.info('enrichResults - sadot - indentifiers: ', identifiers);
|
|
58
58
|
|
|
59
59
|
const uniqueIdentifiers = [...new Set(identifiers)].filter(Boolean);
|
|
60
60
|
|
|
61
|
+
console.log({ uniqueIdentifiers });
|
|
62
|
+
|
|
61
63
|
const identifierCustomFieldDefinitionsMapping = uniqueIdentifiers.reduce((map, identifier) => ({
|
|
62
64
|
...map,
|
|
63
65
|
[identifier]: [],
|
|
@@ -67,7 +69,7 @@ const enrichResults = (
|
|
|
67
69
|
const customFieldDefinitions = await DefinitionRepo.findByEntityIds(
|
|
68
70
|
modelType,
|
|
69
71
|
uniqueIdentifiers,
|
|
70
|
-
{ transaction: options.transaction },
|
|
72
|
+
{ transaction: options.transaction, modelOptions },
|
|
71
73
|
);
|
|
72
74
|
|
|
73
75
|
const definitionsMap = customFieldDefinitions.reduce((map, definition) => ({
|
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,7 +1,7 @@
|
|
|
1
1
|
import logger from '../utils/logger';
|
|
2
2
|
import * as ValueRepo from '../repository/value';
|
|
3
3
|
import { ModelOptions } from '../types';
|
|
4
|
-
import
|
|
4
|
+
import buildAttributesByInstance from '../utils/scopeAttributes';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* A hook to update the custom fields when updating a model (more then one instance).
|
|
@@ -22,9 +22,8 @@ export const beforeUpdate = (scopeAttributes: string[], modelOptions: ModelOptio
|
|
|
22
22
|
): Promise<void> => {
|
|
23
23
|
logger.debug('sadot - before update hook');
|
|
24
24
|
const { fields } = options;
|
|
25
|
-
const { scopeAttributeReplacer } = modelOptions;
|
|
26
25
|
const modelType = instance.constructor.name;
|
|
27
|
-
const identifiers =
|
|
26
|
+
const identifiers = buildAttributesByInstance(instance, scopeAttributes);
|
|
28
27
|
|
|
29
28
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
30
29
|
if (customFieldsIdx > -1) {
|
|
@@ -34,7 +33,7 @@ export const beforeUpdate = (scopeAttributes: string[], modelOptions: ModelOptio
|
|
|
34
33
|
instance.id,
|
|
35
34
|
identifiers,
|
|
36
35
|
customFields,
|
|
37
|
-
{ transaction: options.transaction },
|
|
36
|
+
{ transaction: options.transaction, modelOptions },
|
|
38
37
|
|
|
39
38
|
);
|
|
40
39
|
// eslint-disable-next-line no-param-reassign
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,9 @@ import api from './api';
|
|
|
7
7
|
import initDB from './utils/db';
|
|
8
8
|
import logger from './utils/logger';
|
|
9
9
|
import type { CustomFieldOptions, ModelFetcher } from './types';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
addHooks, addScopes, applyCustomAssociation, removeHooks,
|
|
12
|
+
} from './utils/init';
|
|
11
13
|
|
|
12
14
|
export * from './utils/validations/custom-fields';
|
|
13
15
|
|
|
@@ -34,6 +36,7 @@ const useCustomFields = async (
|
|
|
34
36
|
addHooks(models, getModel);
|
|
35
37
|
await initTables(sequelize, options.getUser);
|
|
36
38
|
addScopes(models, getModel);
|
|
39
|
+
applyCustomAssociation(models);
|
|
37
40
|
logger.debug('sadot - custom fields finished initializing with models', models);
|
|
38
41
|
return sequelize;
|
|
39
42
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
+
FindOptions,
|
|
2
3
|
Op, WhereOptions,
|
|
3
4
|
} from 'sequelize';
|
|
4
5
|
import { CustomFieldDefinition } from '../models';
|
|
5
6
|
import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../types/definition';
|
|
7
|
+
import { ModelOptions } from '../types';
|
|
6
8
|
|
|
7
9
|
export const create = (data: CreateCustomFieldDefinition): Promise<CustomFieldDefinition> =>
|
|
8
10
|
CustomFieldDefinition.create(data);
|
|
@@ -19,6 +21,8 @@ export const findAll = (
|
|
|
19
21
|
where,
|
|
20
22
|
transaction: options.transaction,
|
|
21
23
|
raw: true,
|
|
24
|
+
include: options.include,
|
|
25
|
+
logging: console.log,
|
|
22
26
|
});
|
|
23
27
|
};
|
|
24
28
|
|
|
@@ -41,15 +45,23 @@ export const findById = (
|
|
|
41
45
|
export const findByEntityIds = async (
|
|
42
46
|
modelType: string,
|
|
43
47
|
entityIds: string[],
|
|
44
|
-
options:
|
|
45
|
-
): Promise<CustomFieldDefinition[]> =>
|
|
46
|
-
|
|
48
|
+
options: FindOptions & { modelOptions?: ModelOptions } = {},
|
|
49
|
+
): Promise<CustomFieldDefinition[]> => {
|
|
50
|
+
const { include, useEntityIdFromInclude } = options.modelOptions;
|
|
51
|
+
const where: WhereOptions = {
|
|
47
52
|
modelType,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (!useEntityIdFromInclude) {
|
|
56
|
+
where.entityId = entityIds;
|
|
57
|
+
}
|
|
58
|
+
return CustomFieldDefinition.findAll({
|
|
59
|
+
where,
|
|
60
|
+
transaction: options.transaction,
|
|
61
|
+
include: include?.(entityIds),
|
|
62
|
+
raw: true,
|
|
63
|
+
});
|
|
64
|
+
};
|
|
53
65
|
|
|
54
66
|
export const findByWhere = (where): Promise<CustomFieldDefinition | null> =>
|
|
55
67
|
CustomFieldDefinition.scope('userScope').findOne({
|
|
@@ -95,14 +107,23 @@ export const getRequiredFields = async (
|
|
|
95
107
|
modelType: string,
|
|
96
108
|
modelId: string | string[],
|
|
97
109
|
entityId: string | string[],
|
|
110
|
+
modelOptions: ModelOptions = {},
|
|
98
111
|
): Promise<string[]> => {
|
|
99
112
|
const entityIds = Array.isArray(entityId) ? entityId : [entityId];
|
|
113
|
+
const { include, useEntityIdFromInclude } = modelOptions;
|
|
114
|
+
|
|
115
|
+
const where: WhereOptions = {
|
|
116
|
+
modelType,
|
|
117
|
+
required: true,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
if (!useEntityIdFromInclude) {
|
|
121
|
+
where.entityId = entityIds;
|
|
122
|
+
}
|
|
123
|
+
|
|
100
124
|
const requiredFields = await CustomFieldDefinition.findAll({
|
|
101
|
-
where
|
|
102
|
-
|
|
103
|
-
modelType,
|
|
104
|
-
entityId: { [Op.in]: entityIds },
|
|
105
|
-
},
|
|
125
|
+
where,
|
|
126
|
+
include: include?.(entityIds),
|
|
106
127
|
logging: true,
|
|
107
128
|
});
|
|
108
129
|
const requiredFieldsNames = requiredFields.map((definition) => definition.name);
|
package/src/repository/value.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/* eslint-disable max-len */
|
|
2
|
+
import { FindOptions, WhereOptions } from 'sequelize';
|
|
2
3
|
import { CustomFieldValue, CustomFieldDefinition } from '../models';
|
|
3
4
|
import * as DefinitionRepo from './definition';
|
|
4
5
|
import { CreateCustomFieldValue, ValuesToUpdate } from '../types/value';
|
|
5
6
|
import logger from '../utils/logger';
|
|
6
7
|
import { MissingDefinitionError } from '../errors';
|
|
8
|
+
import { ModelOptions } from '../types';
|
|
7
9
|
|
|
8
10
|
export const findByModelIdAndDefinition = async (modelId: string, customFieldDefinitionId: string) =>
|
|
9
11
|
CustomFieldValue.findAll({ where: { modelId, customFieldDefinitionId }, include: [CustomFieldDefinition] });
|
|
@@ -50,21 +52,23 @@ export const updateValues = async (
|
|
|
50
52
|
modelId: string,
|
|
51
53
|
identifiers: string[],
|
|
52
54
|
valuesToUpdate: ValuesToUpdate,
|
|
53
|
-
options:
|
|
55
|
+
options: FindOptions & { modelOptions?: ModelOptions } = {},
|
|
54
56
|
): Promise<CustomFieldValue[]> => {
|
|
55
57
|
const names = Object.keys(valuesToUpdate);
|
|
56
|
-
logger.info(`custom-
|
|
57
|
-
names, options, valuesToUpdate,
|
|
58
|
+
logger.info(`custom-fiel ds: updating values for ${modelType} ${modelId}`, {
|
|
59
|
+
names, options, valuesToUpdate, identifiers,
|
|
58
60
|
});
|
|
61
|
+
const { modelOptions, transaction } = options;
|
|
62
|
+
|
|
63
|
+
const where: WhereOptions = {
|
|
64
|
+
modelType,
|
|
65
|
+
name: names,
|
|
66
|
+
};
|
|
59
67
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
entityId: identifiers,
|
|
65
|
-
},
|
|
66
|
-
{ withDisabled: true, transaction: options.transaction },
|
|
67
|
-
) || [];
|
|
68
|
+
if (!options.modelOptions?.useEntityIdFromInclude) {
|
|
69
|
+
where.entityId = identifiers;
|
|
70
|
+
}
|
|
71
|
+
const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true, transaction, include: modelOptions.include?.(identifiers) }) || [];
|
|
68
72
|
|
|
69
73
|
const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
|
|
70
74
|
if (fieldDefinitions.length !== names.length) {
|
package/src/types/index.ts
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
|
+
import { Includeable } from 'sequelize';
|
|
2
|
+
import { ModelCtor } from 'sequelize-typescript';
|
|
3
|
+
|
|
1
4
|
export type ModelFetcher = (name: string) => any;
|
|
2
5
|
|
|
3
6
|
export type ModelOptions = {
|
|
4
|
-
|
|
7
|
+
include?: (entityIds: string[]) => Includeable[];
|
|
8
|
+
/**
|
|
9
|
+
* A function that will be called to resolve the entity id.
|
|
10
|
+
* @param {string | string[]} - The entity id.
|
|
11
|
+
* @returns {void}
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
customAssociation?: (model: ModelCtor) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Whether to use the entity id from the instance per scope attribute
|
|
17
|
+
* @param {string} -
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
useEntityIdFromInclude?: boolean
|
|
5
22
|
}
|
|
6
23
|
export type Models = {
|
|
7
24
|
name: string;
|
|
8
|
-
scopeAttributes:
|
|
25
|
+
scopeAttributes: any[];
|
|
9
26
|
modelOptions?: ModelOptions;
|
|
10
27
|
creationWebhookHandler?: (instance: any) => any;
|
|
11
28
|
updateWebhookHandler?: (instance: any) => any;
|
|
@@ -16,4 +33,5 @@ export type CustomFieldOptions = {
|
|
|
16
33
|
models: Models[];
|
|
17
34
|
databaseConfig: any;
|
|
18
35
|
getUser: () => any;
|
|
36
|
+
|
|
19
37
|
};
|
package/src/utils/init.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { DataTypes } from 'sequelize';
|
|
|
2
2
|
import { ModelCtor } from 'sequelize-typescript';
|
|
3
3
|
import { customFields } from '@autofleet/common-types';
|
|
4
4
|
import {
|
|
5
|
+
CustomFieldDefinition,
|
|
5
6
|
CustomFieldValue,
|
|
6
7
|
} from '../models';
|
|
7
8
|
import {
|
|
@@ -103,3 +104,9 @@ export const addScopes = (models: Models[], getModel: ModelFetcher): void => {
|
|
|
103
104
|
}
|
|
104
105
|
});
|
|
105
106
|
};
|
|
107
|
+
|
|
108
|
+
export const applyCustomAssociation = (models: Models[]): void => {
|
|
109
|
+
models.forEach(({ modelOptions }) => {
|
|
110
|
+
modelOptions?.customAssociation?.(CustomFieldDefinition);
|
|
111
|
+
});
|
|
112
|
+
};
|
|
@@ -1,32 +1,12 @@
|
|
|
1
|
-
import { ModelOptions } from '../types';
|
|
2
|
-
import logger from './logger';
|
|
3
|
-
|
|
4
|
-
const attributeCache = new Map<string, string[]>();
|
|
5
|
-
|
|
6
1
|
const mapAttributeToInstance = (scopeAttributes: string[], instance: any) =>
|
|
7
2
|
scopeAttributes.map((attr) => instance[attr]);
|
|
8
3
|
|
|
9
4
|
const buildAttributesByInstance = (instance: any, scopeAttributes: string[]) => {
|
|
5
|
+
const uniqueAttributes = Array.from(new Set(scopeAttributes));
|
|
10
6
|
if (Array.isArray(instance)) {
|
|
11
|
-
return instance.flatMap((ins) => mapAttributeToInstance(
|
|
12
|
-
}
|
|
13
|
-
return mapAttributeToInstance(scopeAttributes, instance);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const getScopeAttributes = async (instance: any, scopeAttributes: string[],
|
|
17
|
-
scopeAttributeReplacer?: ModelOptions['scopeAttributeReplacer']): Promise<string[]> => {
|
|
18
|
-
logger.info('Getting scope attributes', { instance, scopeAttributes });
|
|
19
|
-
const attributes = buildAttributesByInstance(instance, scopeAttributes);
|
|
20
|
-
const attributeCacheKey = attributes.join();
|
|
21
|
-
const cachedAttributes = attributeCache.get(attributeCacheKey);
|
|
22
|
-
if (cachedAttributes) {
|
|
23
|
-
logger.info('Using cached attributes', { cachedAttributes });
|
|
24
|
-
return cachedAttributes;
|
|
7
|
+
return instance.flatMap((ins) => mapAttributeToInstance(uniqueAttributes, ins));
|
|
25
8
|
}
|
|
26
|
-
|
|
27
|
-
? await scopeAttributeReplacer(attributes) : attributes);
|
|
28
|
-
attributeCache.set(attributeCacheKey, attributeResult);
|
|
29
|
-
return attributeResult;
|
|
9
|
+
return mapAttributeToInstance(uniqueAttributes, instance);
|
|
30
10
|
};
|
|
31
11
|
|
|
32
|
-
export default
|
|
12
|
+
export default buildAttributesByInstance;
|