@autofleet/sadot 0.9.1 → 0.10.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/dist/errors/index.js +1 -1
- package/dist/hooks/create.d.ts +2 -2
- package/dist/hooks/create.js +12 -5
- package/dist/hooks/update.d.ts +2 -2
- package/dist/hooks/update.js +13 -26
- package/dist/hooks/utils/updateInstanceValues.d.ts +15 -0
- package/dist/hooks/utils/updateInstanceValues.js +50 -0
- package/dist/index.js +1 -1
- package/dist/repository/definition.js +6 -1
- package/dist/repository/entries.d.ts +13 -0
- package/dist/repository/entries.js +77 -0
- package/dist/tests/helpers/commonHooks.d.ts +3 -1
- package/dist/tests/helpers/commonHooks.js +3 -2
- package/dist/tests/helpers/index.d.ts +2 -1
- package/dist/tests/helpers/index.js +4 -1
- package/dist/utils/init.d.ts +3 -1
- package/dist/utils/init.js +3 -3
- package/dist/utils/validations/index.js +3 -0
- package/package.json +1 -1
- package/src/errors/index.ts +3 -1
- package/src/hooks/create.ts +12 -7
- package/src/hooks/update.ts +16 -7
- package/src/hooks/utils/updateInstanceValues.ts +63 -0
- package/src/index.ts +1 -1
- package/src/repository/definition.ts +7 -1
- package/src/repository/entries.ts +71 -0
- package/src/tests/helpers/commonHooks.ts +7 -2
- package/src/tests/helpers/index.ts +7 -2
- package/src/utils/init.ts +7 -3
- package/src/utils/validations/index.ts +3 -0
package/dist/errors/index.js
CHANGED
|
@@ -52,7 +52,7 @@ class InvalidEntriesError extends errors_1.BadRequest {
|
|
|
52
52
|
constructor(modelId, validationErrors) {
|
|
53
53
|
const errors = validationErrors.map((validationError) => new InvalidValueError(validationError.value, validationError.fieldDefinitionName, validationError.joiValidationError));
|
|
54
54
|
super(errors, null, null);
|
|
55
|
-
this.message = `Invalid entries on ${modelId}`;
|
|
55
|
+
this.message = `Invalid entries on ${modelId}\n${validationErrors.map((validationError) => (`${validationError.fieldDefinitionName} - ${validationError.joiValidationError.message}`)).join('\n')}`;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
exports.InvalidEntriesError = InvalidEntriesError;
|
package/dist/hooks/create.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ModelOptions } from '../types';
|
|
1
|
+
import type { CustomFieldOptions, ModelOptions } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* A hook to create the custom fields when updating a model (more then one instance).
|
|
4
4
|
*/
|
|
@@ -7,4 +7,4 @@ export declare const beforeBulkCreate: (options: any) => void;
|
|
|
7
7
|
* A hook to create the custom fields when updating a model instance.
|
|
8
8
|
* TODO - cleanup if update fail
|
|
9
9
|
*/
|
|
10
|
-
export declare const beforeCreate: (scopeAttributes: string[], modelOptions?: ModelOptions) => (instance: any, options: any) => Promise<void>;
|
|
10
|
+
export declare const beforeCreate: (scopeAttributes: string[], modelOptions?: ModelOptions, sadotOptions?: Pick<CustomFieldOptions, 'useCustomFieldsEntries'>) => (instance: any, options: any) => Promise<void>;
|
package/dist/hooks/create.js
CHANGED
|
@@ -28,10 +28,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.beforeCreate = exports.beforeBulkCreate = void 0;
|
|
30
30
|
const logger_1 = __importDefault(require("../utils/logger"));
|
|
31
|
-
const ValueRepo = __importStar(require("../repository/value"));
|
|
32
31
|
const DefinitionRepo = __importStar(require("../repository/definition"));
|
|
33
32
|
const errors_1 = require("../errors");
|
|
34
33
|
const scopeAttributes_1 = __importDefault(require("../utils/scopeAttributes"));
|
|
34
|
+
const updateInstanceValues_1 = __importDefault(require("./utils/updateInstanceValues"));
|
|
35
35
|
/**
|
|
36
36
|
* A hook to create the custom fields when updating a model (more then one instance).
|
|
37
37
|
*/
|
|
@@ -45,7 +45,7 @@ exports.beforeBulkCreate = beforeBulkCreate;
|
|
|
45
45
|
* A hook to create the custom fields when updating a model instance.
|
|
46
46
|
* TODO - cleanup if update fail
|
|
47
47
|
*/
|
|
48
|
-
const beforeCreate = (scopeAttributes, modelOptions = {}) => async (instance, options) => {
|
|
48
|
+
const beforeCreate = (scopeAttributes, modelOptions = {}, sadotOptions = { useCustomFieldsEntries: false }) => async (instance, options) => {
|
|
49
49
|
logger_1.default.debug('sadot - before create hook');
|
|
50
50
|
const { fields } = options;
|
|
51
51
|
const { include, useEntityIdFromInclude } = modelOptions;
|
|
@@ -80,9 +80,16 @@ const beforeCreate = (scopeAttributes, modelOptions = {}) => async (instance, op
|
|
|
80
80
|
if (missingFields?.length > 0) {
|
|
81
81
|
throw new errors_1.MissingRequiredCustomFieldError(missingFields);
|
|
82
82
|
}
|
|
83
|
-
await
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
await (0, updateInstanceValues_1.default)({
|
|
84
|
+
modelId: instance.id,
|
|
85
|
+
modelType,
|
|
86
|
+
identifiers,
|
|
87
|
+
customFields,
|
|
88
|
+
options: {
|
|
89
|
+
useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,
|
|
90
|
+
transaction: options.transaction,
|
|
91
|
+
modelOptions,
|
|
92
|
+
},
|
|
86
93
|
});
|
|
87
94
|
// eslint-disable-next-line no-param-reassign
|
|
88
95
|
fields.splice(customFieldsIdx, 1);
|
package/dist/hooks/update.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ModelOptions } from '../types';
|
|
1
|
+
import type { CustomFieldOptions, ModelOptions } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* A hook to update the custom fields when updating a model (more then one instance).
|
|
4
4
|
*/
|
|
@@ -7,4 +7,4 @@ export declare const beforeBulkUpdate: (options: any) => void;
|
|
|
7
7
|
* A hook to update the custom fields when updating a model instance.
|
|
8
8
|
* TODO - cleanup if update fail
|
|
9
9
|
*/
|
|
10
|
-
export declare const beforeUpdate: (scopeAttributes: string[], modelOptions?: ModelOptions) => (instance: any, options: any) => Promise<void>;
|
|
10
|
+
export declare const beforeUpdate: (scopeAttributes: string[], modelOptions?: ModelOptions, sadotOptions?: Pick<CustomFieldOptions, 'useCustomFieldsEntries'>) => (instance: any, options: any) => Promise<void>;
|
package/dist/hooks/update.js
CHANGED
|
@@ -1,35 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
4
|
};
|
|
28
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
6
|
exports.beforeUpdate = exports.beforeBulkUpdate = void 0;
|
|
30
7
|
const logger_1 = __importDefault(require("../utils/logger"));
|
|
31
|
-
const ValueRepo = __importStar(require("../repository/value"));
|
|
32
8
|
const scopeAttributes_1 = __importDefault(require("../utils/scopeAttributes"));
|
|
9
|
+
const updateInstanceValues_1 = __importDefault(require("./utils/updateInstanceValues"));
|
|
33
10
|
/**
|
|
34
11
|
* A hook to update the custom fields when updating a model (more then one instance).
|
|
35
12
|
*/
|
|
@@ -43,7 +20,7 @@ exports.beforeBulkUpdate = beforeBulkUpdate;
|
|
|
43
20
|
* A hook to update the custom fields when updating a model instance.
|
|
44
21
|
* TODO - cleanup if update fail
|
|
45
22
|
*/
|
|
46
|
-
const beforeUpdate = (scopeAttributes, modelOptions = {}) => async (instance, options) => {
|
|
23
|
+
const beforeUpdate = (scopeAttributes, modelOptions = {}, sadotOptions = { useCustomFieldsEntries: false }) => async (instance, options) => {
|
|
47
24
|
logger_1.default.debug('sadot - before update hook');
|
|
48
25
|
const { fields } = options;
|
|
49
26
|
const modelType = instance.constructor.name;
|
|
@@ -51,7 +28,17 @@ const beforeUpdate = (scopeAttributes, modelOptions = {}) => async (instance, op
|
|
|
51
28
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
52
29
|
if (customFieldsIdx > -1) {
|
|
53
30
|
const { customFields } = instance;
|
|
54
|
-
await
|
|
31
|
+
await (0, updateInstanceValues_1.default)({
|
|
32
|
+
modelId: instance.id,
|
|
33
|
+
modelType,
|
|
34
|
+
identifiers,
|
|
35
|
+
customFields,
|
|
36
|
+
options: {
|
|
37
|
+
useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,
|
|
38
|
+
transaction: options.transaction,
|
|
39
|
+
modelOptions,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
55
42
|
// eslint-disable-next-line no-param-reassign
|
|
56
43
|
fields.splice(customFieldsIdx, 1);
|
|
57
44
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Transaction } from 'sequelize';
|
|
2
|
+
import type { ModelOptions } from '../../types';
|
|
3
|
+
interface UpdateInstanceValuesParams {
|
|
4
|
+
modelId: string;
|
|
5
|
+
modelType: string;
|
|
6
|
+
identifiers: string[];
|
|
7
|
+
customFields: Record<string, any>;
|
|
8
|
+
options?: {
|
|
9
|
+
transaction?: Transaction;
|
|
10
|
+
modelOptions?: ModelOptions;
|
|
11
|
+
useCustomFieldsEntries?: boolean;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
declare const updateInstanceValues: ({ modelId, modelType, identifiers, customFields, options, }: UpdateInstanceValuesParams) => Promise<void>;
|
|
15
|
+
export default updateInstanceValues;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const ValueRepo = __importStar(require("../../repository/value"));
|
|
27
|
+
const EntriesRepo = __importStar(require("../../repository/entries"));
|
|
28
|
+
const updateInstanceValues = async ({ modelId, modelType, identifiers, customFields, options = {
|
|
29
|
+
modelOptions: {},
|
|
30
|
+
useCustomFieldsEntries: false,
|
|
31
|
+
}, }) => {
|
|
32
|
+
await ValueRepo.updateValues(modelType, modelId, identifiers, customFields, {
|
|
33
|
+
...options,
|
|
34
|
+
modelOptions: options.modelOptions ?? {},
|
|
35
|
+
});
|
|
36
|
+
/*
|
|
37
|
+
T.Y TODO - Once we're ready to switch from custom_field_values to custom_field_entries, we should remove the ValueRepo.updateValues call.
|
|
38
|
+
Currently, We're updating both tables to keep the data in sync, but not all microservices are using the new table yet.
|
|
39
|
+
*/
|
|
40
|
+
if (!options?.useCustomFieldsEntries) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const { dataValues: { customFields: oldCustomFields } } = await EntriesRepo.findEntriesByModelId(modelId) ?? { dataValues: {} };
|
|
44
|
+
const newCustomFields = { ...oldCustomFields, ...customFields };
|
|
45
|
+
await EntriesRepo.updateEntries(modelId, modelType, newCustomFields, identifiers, {
|
|
46
|
+
...options,
|
|
47
|
+
modelOptions: options.modelOptions ?? {},
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
exports.default = updateInstanceValues;
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ const useCustomFields = async (app, getModel, options) => {
|
|
|
40
40
|
await (0, models_1.initTestModels)(sequelize);
|
|
41
41
|
}
|
|
42
42
|
// The order is important
|
|
43
|
-
(0, init_1.addHooks)(models, getModel);
|
|
43
|
+
(0, init_1.addHooks)(models, getModel, { useCustomFieldsEntries });
|
|
44
44
|
await (0, models_1.initTables)(sequelize, options.getUser, { useCustomFieldsEntries });
|
|
45
45
|
(0, init_1.addScopes)(models, getModel);
|
|
46
46
|
(0, init_1.applyCustomAssociation)(models);
|
|
@@ -104,7 +104,12 @@ const getCustomFieldDefinitionsDictionary = async (instances, options = { withDi
|
|
|
104
104
|
customFields.add(fieldName);
|
|
105
105
|
});
|
|
106
106
|
});
|
|
107
|
-
const
|
|
107
|
+
const where = {
|
|
108
|
+
modelType,
|
|
109
|
+
entityId: { [sequelize_1.Op.in]: Array.from(entityIds) },
|
|
110
|
+
name: { [sequelize_1.Op.in]: Array.from(customFields) },
|
|
111
|
+
};
|
|
112
|
+
const definitions = await (0, exports.findAll)(where, { ...options });
|
|
108
113
|
const matchedDefinitions = definitions.filter((def) => customFields.has(def.name));
|
|
109
114
|
const matchedDefinitionsByName = Object.fromEntries(matchedDefinitions.map((definition) => [definition.name, definition]));
|
|
110
115
|
if (!definitions?.length || matchedDefinitions.length !== customFields.size) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FindOptions, Includeable, Transaction } from 'sequelize';
|
|
2
|
+
import { CustomFieldEntries } from '../models';
|
|
3
|
+
import type { ModelOptions } from '../types';
|
|
4
|
+
type CustomFieldEntriesModelOptions = ModelOptions & {
|
|
5
|
+
include?: Includeable;
|
|
6
|
+
transaction?: Transaction;
|
|
7
|
+
};
|
|
8
|
+
export declare const findEntriesByModelId: (modelId: string) => Promise<CustomFieldEntries>;
|
|
9
|
+
export declare const findEntriesByModelIds: (modelIds: string[], options?: CustomFieldEntriesModelOptions) => Promise<CustomFieldEntries[]>;
|
|
10
|
+
export declare const updateEntries: (modelId: string, modelType: string, customFields: Record<string, any>, identifiers: string[], options?: FindOptions & {
|
|
11
|
+
modelOptions?: ModelOptions;
|
|
12
|
+
}) => Promise<[CustomFieldEntries, boolean]>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.updateEntries = exports.findEntriesByModelIds = exports.findEntriesByModelId = void 0;
|
|
30
|
+
const models_1 = require("../models");
|
|
31
|
+
const logger_1 = __importDefault(require("../utils/logger"));
|
|
32
|
+
const errors_1 = require("../errors");
|
|
33
|
+
const DefinitionRepo = __importStar(require("./definition"));
|
|
34
|
+
const findEntriesByModelId = async (modelId) => models_1.CustomFieldEntries.findOne({ where: { modelId } });
|
|
35
|
+
exports.findEntriesByModelId = findEntriesByModelId;
|
|
36
|
+
const findEntriesByModelIds = async (modelIds, options = {}) => {
|
|
37
|
+
const { transaction } = options;
|
|
38
|
+
return models_1.CustomFieldEntries.findAll({
|
|
39
|
+
where: { modelId: modelIds },
|
|
40
|
+
transaction,
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
exports.findEntriesByModelIds = findEntriesByModelIds;
|
|
44
|
+
const updateEntries = async (modelId, modelType, customFields, identifiers, options = {}) => {
|
|
45
|
+
const customFieldsNames = Object.keys(customFields);
|
|
46
|
+
logger_1.default.debug(`custom-fields: updating entries for ${modelType} ${modelId}`, {
|
|
47
|
+
customFieldsNames,
|
|
48
|
+
optionsKeys: options ? Object.keys(options) : null,
|
|
49
|
+
customFields,
|
|
50
|
+
identifiers,
|
|
51
|
+
});
|
|
52
|
+
const { modelOptions, transaction } = options;
|
|
53
|
+
const where = {
|
|
54
|
+
modelType,
|
|
55
|
+
name: customFieldsNames,
|
|
56
|
+
...(!options.modelOptions?.useEntityIdFromInclude && { entityId: identifiers }),
|
|
57
|
+
};
|
|
58
|
+
const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true, transaction, include: modelOptions.include?.(identifiers) }) ?? [];
|
|
59
|
+
const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
|
|
60
|
+
if (fieldDefinitions.length !== customFieldsNames.length) {
|
|
61
|
+
logger_1.default.warn(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names: customFieldsNames, fieldDefinitions });
|
|
62
|
+
const missingDefinitions = customFieldsNames.filter((name) => !fieldDefinitions.some((def) => def.name === name));
|
|
63
|
+
throw new errors_1.MissingDefinitionError(missingDefinitions);
|
|
64
|
+
}
|
|
65
|
+
const disabledNames = disabledDefinitions?.map((def) => def.name) || [];
|
|
66
|
+
const valuesWithDisabledDefinitions = customFieldsNames.filter((name) => disabledNames.includes(name));
|
|
67
|
+
if (valuesWithDisabledDefinitions?.length > 0) {
|
|
68
|
+
logger_1.default.warn(`custom-fields: trying to update disabled values: ${valuesWithDisabledDefinitions.join(', ')}`);
|
|
69
|
+
}
|
|
70
|
+
return models_1.CustomFieldEntries.upsert({
|
|
71
|
+
modelId,
|
|
72
|
+
entityId: fieldDefinitions[0].entityId,
|
|
73
|
+
modelType,
|
|
74
|
+
customFields,
|
|
75
|
+
}, options);
|
|
76
|
+
};
|
|
77
|
+
exports.updateEntries = updateEntries;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { Application } from 'express';
|
|
2
2
|
import type { Models } from '../../types';
|
|
3
|
-
export declare function commonTestHooks(app?: Application | null, models?: Models[]
|
|
3
|
+
export declare function commonTestHooks(app?: Application | null, models?: Models[], options?: {
|
|
4
|
+
useCustomFieldsEntries: boolean;
|
|
5
|
+
}): void;
|
|
@@ -31,7 +31,7 @@ const __1 = __importDefault(require("../.."));
|
|
|
31
31
|
const _1 = require(".");
|
|
32
32
|
const database_config_1 = __importDefault(require("./database-config"));
|
|
33
33
|
const db_1 = __importStar(require("../../utils/db"));
|
|
34
|
-
function commonTestHooks(app = null, models = [{ name: 'TestModel', scopeAttributes: ['fleetId'] }]) {
|
|
34
|
+
function commonTestHooks(app = null, models = [{ name: 'TestModel', scopeAttributes: ['fleetId'] }], options = { useCustomFieldsEntries: false }) {
|
|
35
35
|
let sequelize;
|
|
36
36
|
beforeAll(async () => {
|
|
37
37
|
sequelize = (0, db_1.default)(database_config_1.default);
|
|
@@ -41,11 +41,12 @@ function commonTestHooks(app = null, models = [{ name: 'TestModel', scopeAttribu
|
|
|
41
41
|
databaseConfig: database_config_1.default,
|
|
42
42
|
getUser: () => undefined,
|
|
43
43
|
sequelize,
|
|
44
|
+
useCustomFieldsEntries: options.useCustomFieldsEntries,
|
|
44
45
|
});
|
|
45
46
|
});
|
|
46
47
|
afterEach(async () => {
|
|
47
48
|
jest.clearAllMocks();
|
|
48
|
-
await (0, _1.cleanup)();
|
|
49
|
+
await (0, _1.cleanup)({ useCustomFieldsEntries: options.useCustomFieldsEntries });
|
|
49
50
|
});
|
|
50
51
|
afterAll(async () => {
|
|
51
52
|
await sequelize.close();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import type { CustomFieldOptions } from '../../types';
|
|
2
|
+
export declare const cleanup: (options?: Pick<CustomFieldOptions, 'useCustomFieldsEntries'>) => Promise<void>;
|
|
2
3
|
export declare const getModel: (name: string) => any;
|
|
3
4
|
export declare const getReplacementMapWithScopeValue: (conditions: Record<string, any>) => {
|
|
4
5
|
replacementsMap: Record<string, string>;
|
|
@@ -4,12 +4,15 @@ exports.getReplacementMapWithScopeValue = exports.getModel = exports.cleanup = v
|
|
|
4
4
|
const formatter_1 = require("@autofleet/sheilta/lib/formatter");
|
|
5
5
|
const models_1 = require("../../models");
|
|
6
6
|
// eslint-disable-next-line import/prefer-default-export
|
|
7
|
-
const cleanup = async () => {
|
|
7
|
+
const cleanup = async (options) => {
|
|
8
8
|
if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development') {
|
|
9
9
|
await models_1.CustomFieldDefinition.unscoped().destroy({ where: {} });
|
|
10
10
|
await models_1.TestModel.destroy({ where: {} });
|
|
11
11
|
await models_1.ContextAwareTestModel.destroy({ where: {} });
|
|
12
12
|
await models_1.ContextTestModel.destroy({ where: {} });
|
|
13
|
+
if (options?.useCustomFieldsEntries) {
|
|
14
|
+
await models_1.CustomFieldEntries.unscoped().destroy({ where: {} });
|
|
15
|
+
}
|
|
13
16
|
}
|
|
14
17
|
};
|
|
15
18
|
exports.cleanup = cleanup;
|
package/dist/utils/init.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ModelFetcher, Models } from '../types';
|
|
2
|
-
export declare const addHooks: (models: Models[], getModel: ModelFetcher
|
|
2
|
+
export declare const addHooks: (models: Models[], getModel: ModelFetcher, sadotOptions?: {
|
|
3
|
+
useCustomFieldsEntries: boolean;
|
|
4
|
+
}) => void;
|
|
3
5
|
export declare const removeHooks: (models: Models[], getModel: ModelFetcher) => void;
|
|
4
6
|
export declare const addScopes: (models: Models[], getModel: ModelFetcher) => void;
|
|
5
7
|
export declare const applyCustomAssociation: (models: Models[]) => void;
|
package/dist/utils/init.js
CHANGED
|
@@ -13,7 +13,7 @@ const scopes_1 = require("../scopes");
|
|
|
13
13
|
const logger_1 = __importDefault(require("./logger"));
|
|
14
14
|
const filter_1 = require("../scopes/filter");
|
|
15
15
|
const { CUSTOM_FIELDS_FILTER_SCOPE } = common_types_1.customFields;
|
|
16
|
-
const addHooks = (models, getModel) => {
|
|
16
|
+
const addHooks = (models, getModel, sadotOptions = { useCustomFieldsEntries: false }) => {
|
|
17
17
|
models.forEach(async ({ name, scopeAttributes, modelOptions, }) => {
|
|
18
18
|
try {
|
|
19
19
|
const model = getModel(name);
|
|
@@ -33,8 +33,8 @@ const addHooks = (models, getModel) => {
|
|
|
33
33
|
model.addHook('beforeFind', 'sadot-beforeFind', (0, hooks_1.beforeFind)(scopeAttributes));
|
|
34
34
|
model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', hooks_1.beforeBulkCreate);
|
|
35
35
|
model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', hooks_1.beforeBulkUpdate);
|
|
36
|
-
model.addHook('beforeCreate', 'sadot-beforeCreate', (0, hooks_1.beforeCreate)(scopeAttributes, modelOptions));
|
|
37
|
-
model.addHook('beforeUpdate', 'sadot-beforeUpdate', (0, hooks_1.beforeUpdate)(scopeAttributes, modelOptions));
|
|
36
|
+
model.addHook('beforeCreate', 'sadot-beforeCreate', (0, hooks_1.beforeCreate)(scopeAttributes, modelOptions, sadotOptions));
|
|
37
|
+
model.addHook('beforeUpdate', 'sadot-beforeUpdate', (0, hooks_1.beforeUpdate)(scopeAttributes, modelOptions, sadotOptions));
|
|
38
38
|
model.addHook('afterFind', 'sadot-afterFind', (0, hooks_1.enrichResults)(name, scopeAttributes, 'afterFind', modelOptions));
|
|
39
39
|
model.addHook('afterUpdate', 'sadot-afterUpdate', (0, hooks_1.enrichResults)(name, scopeAttributes, null, modelOptions));
|
|
40
40
|
model.addHook('afterCreate', 'sadot-afterCreate', (0, hooks_1.enrichResults)(name, scopeAttributes, null, modelOptions));
|
|
@@ -19,6 +19,9 @@ exports.validateValue = validateValue;
|
|
|
19
19
|
const validateInstanceCustomFieldEntries = (instance, definitionsByName) => {
|
|
20
20
|
const validationErrors = Object.entries(instance.customFields)
|
|
21
21
|
.map(([customFieldName, value]) => {
|
|
22
|
+
// Allow NULL values, just like we do in custom_field_values.
|
|
23
|
+
if (value === null)
|
|
24
|
+
return null;
|
|
22
25
|
const { validation, fieldType } = definitionsByName[customFieldName];
|
|
23
26
|
const result = (0, exports.validateValue)(value, fieldType, validation);
|
|
24
27
|
if (result?.error) {
|
package/package.json
CHANGED
package/src/errors/index.ts
CHANGED
|
@@ -55,7 +55,9 @@ export class InvalidEntriesError extends BadRequest {
|
|
|
55
55
|
constructor(modelId: string, validationErrors: EntriesValidationError[]) {
|
|
56
56
|
const errors = validationErrors.map((validationError) => new InvalidValueError(validationError.value, validationError.fieldDefinitionName, validationError.joiValidationError));
|
|
57
57
|
super(errors, null, null);
|
|
58
|
-
this.message = `Invalid entries on ${modelId}
|
|
58
|
+
this.message = `Invalid entries on ${modelId}\n${validationErrors.map((validationError) => (
|
|
59
|
+
`${validationError.fieldDefinitionName} - ${validationError.joiValidationError.message}`
|
|
60
|
+
)).join('\n')}`;
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
|
package/src/hooks/create.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { WhereOptions } from 'sequelize';
|
|
2
2
|
import logger from '../utils/logger';
|
|
3
|
-
import * as ValueRepo from '../repository/value';
|
|
4
3
|
import * as DefinitionRepo from '../repository/definition';
|
|
5
4
|
import { MissingRequiredCustomFieldError } from '../errors';
|
|
6
|
-
import type { ModelOptions } from '../types';
|
|
5
|
+
import type { CustomFieldOptions, ModelOptions } from '../types';
|
|
7
6
|
import applyScopeToInstance from '../utils/scopeAttributes';
|
|
7
|
+
import updateInstanceValues from './utils/updateInstanceValues';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A hook to create the custom fields when updating a model (more then one instance).
|
|
@@ -18,7 +18,11 @@ export const beforeBulkCreate = (options): void => {
|
|
|
18
18
|
* A hook to create the custom fields when updating a model instance.
|
|
19
19
|
* TODO - cleanup if update fail
|
|
20
20
|
*/
|
|
21
|
-
export const beforeCreate = (
|
|
21
|
+
export const beforeCreate = (
|
|
22
|
+
scopeAttributes: string[],
|
|
23
|
+
modelOptions: ModelOptions = {},
|
|
24
|
+
sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false },
|
|
25
|
+
) => async (
|
|
22
26
|
instance,
|
|
23
27
|
options,
|
|
24
28
|
): Promise<void> => {
|
|
@@ -61,16 +65,17 @@ export const beforeCreate = (scopeAttributes: string[], modelOptions: ModelOptio
|
|
|
61
65
|
throw new MissingRequiredCustomFieldError(missingFields);
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
await
|
|
68
|
+
await updateInstanceValues({
|
|
69
|
+
modelId: instance.id,
|
|
65
70
|
modelType,
|
|
66
|
-
instance.id,
|
|
67
71
|
identifiers,
|
|
68
72
|
customFields,
|
|
69
|
-
{
|
|
73
|
+
options: {
|
|
74
|
+
useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,
|
|
70
75
|
transaction: options.transaction,
|
|
71
76
|
modelOptions,
|
|
72
77
|
},
|
|
73
|
-
);
|
|
78
|
+
});
|
|
74
79
|
|
|
75
80
|
// eslint-disable-next-line no-param-reassign
|
|
76
81
|
fields.splice(customFieldsIdx, 1);
|
package/src/hooks/update.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logger from '../utils/logger';
|
|
2
|
-
import
|
|
3
|
-
import type { ModelOptions } from '../types';
|
|
2
|
+
import type { CustomFieldOptions, ModelOptions } from '../types';
|
|
4
3
|
import applyScopeToInstance from '../utils/scopeAttributes';
|
|
4
|
+
import updateInstanceValues from './utils/updateInstanceValues';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* A hook to update the custom fields when updating a model (more then one instance).
|
|
@@ -16,7 +16,11 @@ export const beforeBulkUpdate = (options): void => {
|
|
|
16
16
|
* A hook to update the custom fields when updating a model instance.
|
|
17
17
|
* TODO - cleanup if update fail
|
|
18
18
|
*/
|
|
19
|
-
export const beforeUpdate = (
|
|
19
|
+
export const beforeUpdate = (
|
|
20
|
+
scopeAttributes: string[],
|
|
21
|
+
modelOptions: ModelOptions = {},
|
|
22
|
+
sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false },
|
|
23
|
+
) => async (
|
|
20
24
|
instance,
|
|
21
25
|
options,
|
|
22
26
|
): Promise<void> => {
|
|
@@ -28,14 +32,19 @@ export const beforeUpdate = (scopeAttributes: string[], modelOptions: ModelOptio
|
|
|
28
32
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
29
33
|
if (customFieldsIdx > -1) {
|
|
30
34
|
const { customFields } = instance;
|
|
31
|
-
|
|
35
|
+
|
|
36
|
+
await updateInstanceValues({
|
|
37
|
+
modelId: instance.id,
|
|
32
38
|
modelType,
|
|
33
|
-
instance.id,
|
|
34
39
|
identifiers,
|
|
35
40
|
customFields,
|
|
36
|
-
|
|
41
|
+
options: {
|
|
42
|
+
useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,
|
|
43
|
+
transaction: options.transaction,
|
|
44
|
+
modelOptions,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
37
47
|
|
|
38
|
-
);
|
|
39
48
|
// eslint-disable-next-line no-param-reassign
|
|
40
49
|
fields.splice(customFieldsIdx, 1);
|
|
41
50
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Transaction } from 'sequelize';
|
|
2
|
+
import * as ValueRepo from '../../repository/value';
|
|
3
|
+
import * as EntriesRepo from '../../repository/entries';
|
|
4
|
+
import type { ModelOptions } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface UpdateInstanceValuesParams {
|
|
7
|
+
modelId: string;
|
|
8
|
+
modelType: string;
|
|
9
|
+
identifiers: string[];
|
|
10
|
+
customFields: Record<string, any>;
|
|
11
|
+
options?: {
|
|
12
|
+
transaction?: Transaction;
|
|
13
|
+
modelOptions?: ModelOptions;
|
|
14
|
+
useCustomFieldsEntries?: boolean;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const updateInstanceValues = async ({
|
|
19
|
+
modelId,
|
|
20
|
+
modelType,
|
|
21
|
+
identifiers,
|
|
22
|
+
customFields,
|
|
23
|
+
options = {
|
|
24
|
+
modelOptions: {},
|
|
25
|
+
useCustomFieldsEntries: false,
|
|
26
|
+
},
|
|
27
|
+
}: UpdateInstanceValuesParams): Promise<void> => {
|
|
28
|
+
await ValueRepo.updateValues(
|
|
29
|
+
modelType,
|
|
30
|
+
modelId,
|
|
31
|
+
identifiers,
|
|
32
|
+
customFields,
|
|
33
|
+
{
|
|
34
|
+
...options,
|
|
35
|
+
modelOptions: options.modelOptions ?? {},
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
T.Y TODO - Once we're ready to switch from custom_field_values to custom_field_entries, we should remove the ValueRepo.updateValues call.
|
|
41
|
+
Currently, We're updating both tables to keep the data in sync, but not all microservices are using the new table yet.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
if (!options?.useCustomFieldsEntries) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { dataValues: { customFields: oldCustomFields } } = await EntriesRepo.findEntriesByModelId(modelId) ?? { dataValues: {} };
|
|
49
|
+
const newCustomFields = { ...oldCustomFields, ...customFields };
|
|
50
|
+
|
|
51
|
+
await EntriesRepo.updateEntries(
|
|
52
|
+
modelId,
|
|
53
|
+
modelType,
|
|
54
|
+
newCustomFields,
|
|
55
|
+
identifiers,
|
|
56
|
+
{
|
|
57
|
+
...options,
|
|
58
|
+
modelOptions: options.modelOptions ?? {},
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default updateInstanceValues;
|
package/src/index.ts
CHANGED
|
@@ -35,7 +35,7 @@ const useCustomFields = async (
|
|
|
35
35
|
await initTestModels(sequelize);
|
|
36
36
|
}
|
|
37
37
|
// The order is important
|
|
38
|
-
addHooks(models, getModel);
|
|
38
|
+
addHooks(models, getModel, { useCustomFieldsEntries });
|
|
39
39
|
await initTables(sequelize, options.getUser, { useCustomFieldsEntries });
|
|
40
40
|
addScopes(models, getModel);
|
|
41
41
|
applyCustomAssociation(models);
|
|
@@ -155,7 +155,13 @@ export const getCustomFieldDefinitionsDictionary = async (
|
|
|
155
155
|
});
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
-
const
|
|
158
|
+
const where: WhereOptions = {
|
|
159
|
+
modelType,
|
|
160
|
+
entityId: { [Op.in]: Array.from(entityIds) },
|
|
161
|
+
name: { [Op.in]: Array.from(customFields) },
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const definitions = await findAll(where, { ...options });
|
|
159
165
|
|
|
160
166
|
const matchedDefinitions = definitions.filter((def) => customFields.has(def.name));
|
|
161
167
|
const matchedDefinitionsByName = Object.fromEntries(matchedDefinitions.map((definition) => [definition.name, definition]));
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FindOptions,
|
|
3
|
+
Includeable,
|
|
4
|
+
Transaction,
|
|
5
|
+
WhereOptions,
|
|
6
|
+
} from 'sequelize';
|
|
7
|
+
import { CustomFieldEntries } from '../models';
|
|
8
|
+
import type { ModelOptions } from '../types';
|
|
9
|
+
import logger from '../utils/logger';
|
|
10
|
+
import { MissingDefinitionError } from '../errors';
|
|
11
|
+
import * as DefinitionRepo from './definition';
|
|
12
|
+
|
|
13
|
+
type CustomFieldEntriesModelOptions = ModelOptions & { include?: Includeable, transaction?: Transaction };
|
|
14
|
+
|
|
15
|
+
export const findEntriesByModelId = async (modelId: string) => CustomFieldEntries.findOne({ where: { modelId } });
|
|
16
|
+
|
|
17
|
+
export const findEntriesByModelIds = async (modelIds: string[], options: CustomFieldEntriesModelOptions = {}) => {
|
|
18
|
+
const { transaction } = options;
|
|
19
|
+
return CustomFieldEntries.findAll({
|
|
20
|
+
where: { modelId: modelIds },
|
|
21
|
+
transaction,
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const updateEntries = async (
|
|
26
|
+
modelId: string,
|
|
27
|
+
modelType: string,
|
|
28
|
+
customFields: Record<string, any>,
|
|
29
|
+
identifiers: string[],
|
|
30
|
+
options: FindOptions & { modelOptions?: ModelOptions } = {},
|
|
31
|
+
) => {
|
|
32
|
+
const customFieldsNames = Object.keys(customFields);
|
|
33
|
+
logger.debug(`custom-fields: updating entries for ${modelType} ${modelId}`, {
|
|
34
|
+
customFieldsNames,
|
|
35
|
+
optionsKeys: options ? Object.keys(options) : null,
|
|
36
|
+
customFields,
|
|
37
|
+
identifiers,
|
|
38
|
+
});
|
|
39
|
+
const { modelOptions, transaction } = options;
|
|
40
|
+
|
|
41
|
+
const where: WhereOptions = {
|
|
42
|
+
modelType,
|
|
43
|
+
name: customFieldsNames,
|
|
44
|
+
...(!options.modelOptions?.useEntityIdFromInclude && { entityId: identifiers }),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true, transaction, include: modelOptions.include?.(identifiers) }) ?? [];
|
|
48
|
+
|
|
49
|
+
const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
|
|
50
|
+
if (fieldDefinitions.length !== customFieldsNames.length) {
|
|
51
|
+
logger.warn(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names: customFieldsNames, fieldDefinitions });
|
|
52
|
+
const missingDefinitions = customFieldsNames.filter((name) => !fieldDefinitions.some((def) => def.name === name));
|
|
53
|
+
throw new MissingDefinitionError(missingDefinitions);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const disabledNames = disabledDefinitions?.map((def) => def.name) || [];
|
|
57
|
+
const valuesWithDisabledDefinitions = customFieldsNames.filter((name) => disabledNames.includes(name));
|
|
58
|
+
if (valuesWithDisabledDefinitions?.length > 0) {
|
|
59
|
+
logger.warn(`custom-fields: trying to update disabled values: ${valuesWithDisabledDefinitions.join(', ')}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return CustomFieldEntries.upsert(
|
|
63
|
+
{
|
|
64
|
+
modelId,
|
|
65
|
+
entityId: fieldDefinitions[0].entityId,
|
|
66
|
+
modelType,
|
|
67
|
+
customFields,
|
|
68
|
+
},
|
|
69
|
+
options,
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -6,7 +6,11 @@ import databaseConfig from './database-config';
|
|
|
6
6
|
import initDB, { createSequelizeMeta } from '../../utils/db';
|
|
7
7
|
import type { Models } from '../../types';
|
|
8
8
|
|
|
9
|
-
export function commonTestHooks(
|
|
9
|
+
export function commonTestHooks(
|
|
10
|
+
app: Application | null = null,
|
|
11
|
+
models: Models[] = [{ name: 'TestModel', scopeAttributes: ['fleetId'] }],
|
|
12
|
+
options: { useCustomFieldsEntries: boolean } = { useCustomFieldsEntries: false },
|
|
13
|
+
) {
|
|
10
14
|
let sequelize: Sequelize;
|
|
11
15
|
|
|
12
16
|
beforeAll(async () => {
|
|
@@ -17,12 +21,13 @@ export function commonTestHooks(app: Application | null = null, models: Models[]
|
|
|
17
21
|
databaseConfig,
|
|
18
22
|
getUser: () => undefined,
|
|
19
23
|
sequelize,
|
|
24
|
+
useCustomFieldsEntries: options.useCustomFieldsEntries,
|
|
20
25
|
});
|
|
21
26
|
});
|
|
22
27
|
|
|
23
28
|
afterEach(async () => {
|
|
24
29
|
jest.clearAllMocks();
|
|
25
|
-
await cleanup();
|
|
30
|
+
await cleanup({ useCustomFieldsEntries: options.useCustomFieldsEntries });
|
|
26
31
|
});
|
|
27
32
|
|
|
28
33
|
afterAll(async () => {
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { generateFilterReplacements } from '@autofleet/sheilta/lib/formatter';
|
|
2
2
|
import {
|
|
3
|
-
ContextAwareTestModel, ContextTestModel, CustomFieldDefinition, TestModel,
|
|
3
|
+
ContextAwareTestModel, ContextTestModel, CustomFieldDefinition, CustomFieldEntries, TestModel,
|
|
4
4
|
} from '../../models';
|
|
5
|
+
import type { CustomFieldOptions } from '../../types';
|
|
5
6
|
|
|
6
7
|
// eslint-disable-next-line import/prefer-default-export
|
|
7
|
-
export const cleanup = async (): Promise<void> => {
|
|
8
|
+
export const cleanup = async (options?: Pick<CustomFieldOptions, 'useCustomFieldsEntries'>): Promise<void> => {
|
|
8
9
|
if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development') {
|
|
9
10
|
await CustomFieldDefinition.unscoped().destroy({ where: {} });
|
|
10
11
|
await TestModel.destroy({ where: {} });
|
|
11
12
|
await ContextAwareTestModel.destroy({ where: {} });
|
|
12
13
|
await ContextTestModel.destroy({ where: {} });
|
|
14
|
+
|
|
15
|
+
if (options?.useCustomFieldsEntries) {
|
|
16
|
+
await CustomFieldEntries.unscoped().destroy({ where: {} });
|
|
17
|
+
}
|
|
13
18
|
}
|
|
14
19
|
};
|
|
15
20
|
|
package/src/utils/init.ts
CHANGED
|
@@ -21,7 +21,11 @@ import { customFieldsSortScope } from '../scopes/filter';
|
|
|
21
21
|
|
|
22
22
|
const { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;
|
|
23
23
|
|
|
24
|
-
export const addHooks = (
|
|
24
|
+
export const addHooks = (
|
|
25
|
+
models: Models[],
|
|
26
|
+
getModel: ModelFetcher,
|
|
27
|
+
sadotOptions: { useCustomFieldsEntries: boolean } = { useCustomFieldsEntries: false },
|
|
28
|
+
): void => {
|
|
25
29
|
models.forEach(async ({
|
|
26
30
|
name, scopeAttributes, modelOptions,
|
|
27
31
|
}) => {
|
|
@@ -43,8 +47,8 @@ export const addHooks = (models: Models[], getModel: ModelFetcher): void => {
|
|
|
43
47
|
model.addHook('beforeFind', 'sadot-beforeFind', beforeFind(scopeAttributes));
|
|
44
48
|
model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', beforeBulkCreate);
|
|
45
49
|
model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', beforeBulkUpdate);
|
|
46
|
-
model.addHook('beforeCreate', 'sadot-beforeCreate', beforeCreate(scopeAttributes, modelOptions));
|
|
47
|
-
model.addHook('beforeUpdate', 'sadot-beforeUpdate', beforeUpdate(scopeAttributes, modelOptions));
|
|
50
|
+
model.addHook('beforeCreate', 'sadot-beforeCreate', beforeCreate(scopeAttributes, modelOptions, sadotOptions));
|
|
51
|
+
model.addHook('beforeUpdate', 'sadot-beforeUpdate', beforeUpdate(scopeAttributes, modelOptions, sadotOptions));
|
|
48
52
|
model.addHook('afterFind', 'sadot-afterFind', enrichResults(name, scopeAttributes, 'afterFind', modelOptions));
|
|
49
53
|
model.addHook('afterUpdate', 'sadot-afterUpdate', enrichResults(name, scopeAttributes, null, modelOptions));
|
|
50
54
|
model.addHook('afterCreate', 'sadot-afterCreate', enrichResults(name, scopeAttributes, null, modelOptions));
|
|
@@ -24,6 +24,9 @@ export const validateValue = (
|
|
|
24
24
|
export const validateInstanceCustomFieldEntries = (instance: CustomFieldEntriesDTO, definitionsByName: { [defName: string]: CustomFieldDefinition; }) => {
|
|
25
25
|
const validationErrors = Object.entries(instance.customFields)
|
|
26
26
|
.map(([customFieldName, value]) => {
|
|
27
|
+
// Allow NULL values, just like we do in custom_field_values.
|
|
28
|
+
if (value === null) return null;
|
|
29
|
+
|
|
27
30
|
const { validation, fieldType } = definitionsByName[customFieldName];
|
|
28
31
|
const result = validateValue(value, fieldType, validation);
|
|
29
32
|
if (result?.error) {
|