@autofleet/sadot 0.9.1-beta-8053a0cc.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/api/v1/definition/index.js +4 -4
- package/dist/api/v1/definition/validations.js +9 -9
- package/dist/api/v1/errors.d.ts +3 -1
- package/dist/api/v1/errors.js +1 -4
- package/dist/errors/index.js +1 -1
- package/dist/events/index.js +4 -0
- 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/models/CustomFieldEntries.d.ts +4 -4
- package/dist/models/CustomFieldEntries.js +15 -20
- package/dist/models/index.d.ts +8 -4
- package/dist/models/index.js +22 -11
- package/dist/repository/definition.d.ts +4 -5
- package/dist/repository/definition.js +11 -13
- package/dist/repository/entries.d.ts +13 -0
- package/dist/repository/entries.js +77 -0
- package/dist/scopes/filter.js +0 -3
- 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/tests/mocks/definition.mock.js +1 -0
- package/dist/tests/mocks/events.mock.d.ts +1 -0
- package/dist/tests/mocks/events.mock.js +3 -1
- package/dist/types/entries/index.d.ts +13 -3
- 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 +2 -2
- package/src/api/v1/definition/index.ts +13 -14
- package/src/api/v1/definition/validations.ts +11 -13
- package/src/api/v1/errors.ts +4 -7
- package/src/errors/index.ts +3 -1
- package/src/events/index.ts +4 -0
- 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/models/CustomFieldEntries.ts +15 -27
- package/src/models/index.ts +31 -15
- package/src/repository/definition.ts +13 -16
- package/src/repository/entries.ts +71 -0
- package/src/scopes/filter.ts +0 -3
- package/src/tests/helpers/commonHooks.ts +7 -2
- package/src/tests/helpers/index.ts +7 -2
- package/src/tests/mocks/definition.mock.ts +11 -10
- package/src/tests/mocks/events.mock.ts +3 -0
- package/src/types/entries/index.ts +13 -3
- package/src/utils/init.ts +7 -3
- package/src/utils/validations/index.ts +3 -0
- package/.env +0 -4
|
@@ -51,7 +51,7 @@ router.post('/', async (req, res) => {
|
|
|
51
51
|
}
|
|
52
52
|
catch (err) {
|
|
53
53
|
logger_1.default.error('Failed to create custom field definition', err);
|
|
54
|
-
return (0, errors_2.default)(err, res, { message: `Error in create ${ENTITY} request` });
|
|
54
|
+
return (0, errors_2.default)(err, res, { logger: logger_1.default, message: `Error in create ${ENTITY} request` });
|
|
55
55
|
}
|
|
56
56
|
});
|
|
57
57
|
/**
|
|
@@ -68,7 +68,7 @@ router.get('/:customFieldDefinitionId', async (req, res) => {
|
|
|
68
68
|
}
|
|
69
69
|
catch (err) {
|
|
70
70
|
logger_1.default.error('Failed to fetch custom field definition', err);
|
|
71
|
-
return (0, errors_2.default)(err, res, { message: `Error in get ${ENTITY} request` });
|
|
71
|
+
return (0, errors_2.default)(err, res, { logger: logger_1.default, message: `Error in get ${ENTITY} request` });
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
/**
|
|
@@ -87,7 +87,7 @@ router.get('/', async (req, res) => {
|
|
|
87
87
|
}
|
|
88
88
|
catch (err) {
|
|
89
89
|
logger_1.default.error('Failed to fetch custom field definitions', err);
|
|
90
|
-
return (0, errors_2.default)(err, res, { message: `Error in get all ${ENTITY} request` });
|
|
90
|
+
return (0, errors_2.default)(err, res, { logger: logger_1.default, message: `Error in get all ${ENTITY} request` });
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
93
|
/**
|
|
@@ -110,7 +110,7 @@ router.patch('/:customFieldDefinitionId', async (req, res) => {
|
|
|
110
110
|
}
|
|
111
111
|
catch (err) {
|
|
112
112
|
logger_1.default.error('Failed to patch custom field definition', err);
|
|
113
|
-
return (0, errors_2.default)(err, res, { message: `Error in update ${ENTITY} request` });
|
|
113
|
+
return (0, errors_2.default)(err, res, { logger: logger_1.default, message: `Error in update ${ENTITY} request` });
|
|
114
114
|
}
|
|
115
115
|
});
|
|
116
116
|
exports.default = router;
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.validateCustomFieldDefinitionUpdate = exports.validateCustomFieldDefinitionCreation = void 0;
|
|
7
|
+
/* eslint-disable newline-per-chained-call */
|
|
7
8
|
const joi_1 = __importDefault(require("joi"));
|
|
8
9
|
const constants_1 = require("../../../utils/constants");
|
|
9
10
|
const FileValidationSchema = joi_1.default.object({
|
|
@@ -16,7 +17,6 @@ const statusValidationObject = joi_1.default.object({
|
|
|
16
17
|
value: joi_1.default.string().required(),
|
|
17
18
|
color: joi_1.default.string().required(),
|
|
18
19
|
});
|
|
19
|
-
const statusValidationObjectSchema = joi_1.default.array().items(statusValidationObject).min(1).unique('value');
|
|
20
20
|
/**
|
|
21
21
|
* Schema for the validation of custom field definition
|
|
22
22
|
* The only custom validation is for
|
|
@@ -28,12 +28,12 @@ const statusValidationObjectSchema = joi_1.default.array().items(statusValidatio
|
|
|
28
28
|
*/
|
|
29
29
|
const ValidationSchema = joi_1.default.when('fieldType', {
|
|
30
30
|
is: constants_1.CustomFieldDefinitionType.SELECT,
|
|
31
|
-
then: joi_1.default.array().items(joi_1.default.string()).min(1).unique(),
|
|
32
|
-
otherwise: joi_1.default.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
then: joi_1.default.array().required().items(joi_1.default.string()).min(1).unique(),
|
|
32
|
+
otherwise: joi_1.default.when('fieldType', {
|
|
33
|
+
is: constants_1.CustomFieldDefinitionType.STATUS,
|
|
34
|
+
then: joi_1.default.array().required().items(statusValidationObject).min(1).unique('value'),
|
|
35
|
+
otherwise: joi_1.default.forbidden(),
|
|
36
|
+
}),
|
|
37
37
|
});
|
|
38
38
|
const DefaultValueSchema = joi_1.default.when('fieldType', {
|
|
39
39
|
switch: [
|
|
@@ -60,7 +60,7 @@ const CustomFieldDefinitionCreationSchema = joi_1.default.object({
|
|
|
60
60
|
required: joi_1.default.boolean(),
|
|
61
61
|
disabled: joi_1.default.boolean(),
|
|
62
62
|
blockEditingFromUI: joi_1.default.boolean(),
|
|
63
|
-
});
|
|
63
|
+
}).oxor('required', 'blockEditingFromUI', { isPresent: (value) => value === true });
|
|
64
64
|
const CustomFieldDefinitionUpdateSchema = joi_1.default.object({
|
|
65
65
|
displayName: joi_1.default.string(),
|
|
66
66
|
validation: ValidationSchema,
|
|
@@ -70,7 +70,7 @@ const CustomFieldDefinitionUpdateSchema = joi_1.default.object({
|
|
|
70
70
|
required: joi_1.default.boolean(),
|
|
71
71
|
disabled: joi_1.default.boolean(),
|
|
72
72
|
blockEditingFromUI: joi_1.default.boolean(),
|
|
73
|
-
});
|
|
73
|
+
}).oxor('required', 'blockEditingFromUI', { isPresent: (value) => value === true });
|
|
74
74
|
const validateCustomFieldDefinitionCreation = (payload) => CustomFieldDefinitionCreationSchema.validateAsync(payload, { abortEarly: false });
|
|
75
75
|
exports.validateCustomFieldDefinitionCreation = validateCustomFieldDefinitionCreation;
|
|
76
76
|
const validateCustomFieldDefinitionUpdate = (payload) => CustomFieldDefinitionUpdateSchema.validateAsync(payload, { abortEarly: false });
|
package/dist/api/v1/errors.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Response } from 'express';
|
|
2
|
+
import { type LogPayload } from '@autofleet/errors';
|
|
3
|
+
declare const _default: (err: any, res: Response, additionalData?: LogPayload) => Promise<any>;
|
|
2
4
|
export default _default;
|
package/dist/api/v1/errors.js
CHANGED
|
@@ -5,10 +5,7 @@ const joi_1 = require("joi");
|
|
|
5
5
|
const sequelize_1 = require("sequelize");
|
|
6
6
|
exports.default = (err, res, additionalData = undefined) => {
|
|
7
7
|
let error = err;
|
|
8
|
-
if (err instanceof
|
|
9
|
-
error = new errors_1.BadRequest([err], null);
|
|
10
|
-
}
|
|
11
|
-
if (err instanceof sequelize_1.ValidationError) {
|
|
8
|
+
if ([joi_1.ValidationError, sequelize_1.ValidationError].some((ErrClass) => err instanceof ErrClass)) {
|
|
12
9
|
error = new errors_1.BadRequest([err], null);
|
|
13
10
|
}
|
|
14
11
|
return (0, errors_1.handleError)(error, res, additionalData);
|
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/events/index.js
CHANGED
|
@@ -27,6 +27,10 @@ const modelTableMapping = {
|
|
|
27
27
|
tableName: 'dim_custom_field_value',
|
|
28
28
|
eventVersion: '1',
|
|
29
29
|
},
|
|
30
|
+
CustomFieldEntries: {
|
|
31
|
+
tableName: 'dim_custom_field_entries',
|
|
32
|
+
eventVersion: '1',
|
|
33
|
+
},
|
|
30
34
|
};
|
|
31
35
|
const sendDimEvent = (instance) => {
|
|
32
36
|
const mapping = modelTableMapping[instance.constructor.name];
|
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);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Model } from 'sequelize-typescript';
|
|
2
2
|
declare class CustomFieldEntries extends Model {
|
|
3
|
-
/** The ID of the
|
|
3
|
+
/** The ID of the model of which this row hold the custom field entries of, e.g. vehicleId / stopPointId / etc. */
|
|
4
4
|
modelId: string;
|
|
5
|
+
/** The ID of the entity of which this row hold the custom field entries of, e.g. fleetId / etc. */
|
|
6
|
+
entityId: string;
|
|
5
7
|
/** A dictionary of customFields and values with the following structure: `{ customFieldName: 'CustomFieldValue' }` */
|
|
6
|
-
customFields:
|
|
7
|
-
[customFieldName: string]: any;
|
|
8
|
-
};
|
|
8
|
+
customFields: Record<string, any>;
|
|
9
9
|
/** The type of model which this custom field entry represents. e.g. Vehicle / StopPoint / etc. */
|
|
10
10
|
modelType: string;
|
|
11
11
|
createdAt?: Date;
|
|
@@ -31,15 +31,11 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
31
31
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
32
32
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
33
33
|
};
|
|
34
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
-
};
|
|
37
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
35
|
const sequelize_typescript_1 = require("sequelize-typescript");
|
|
39
36
|
const events_1 = require("../events");
|
|
40
37
|
const CustomFieldDefinitionRepo = __importStar(require("../repository/definition"));
|
|
41
38
|
const validations_1 = require("../utils/validations");
|
|
42
|
-
const logger_1 = __importDefault(require("../utils/logger"));
|
|
43
39
|
let CustomFieldEntries = class CustomFieldEntries extends sequelize_typescript_1.Model {
|
|
44
40
|
static afterSaveHandler(instance, options) {
|
|
45
41
|
if (options.transaction) {
|
|
@@ -56,10 +52,19 @@ __decorate([
|
|
|
56
52
|
type: sequelize_typescript_1.DataType.UUID,
|
|
57
53
|
allowNull: false,
|
|
58
54
|
})
|
|
59
|
-
/** The ID of the
|
|
55
|
+
/** The ID of the model of which this row hold the custom field entries of, e.g. vehicleId / stopPointId / etc. */
|
|
60
56
|
,
|
|
61
57
|
__metadata("design:type", String)
|
|
62
58
|
], CustomFieldEntries.prototype, "modelId", void 0);
|
|
59
|
+
__decorate([
|
|
60
|
+
(0, sequelize_typescript_1.Column)({
|
|
61
|
+
type: sequelize_typescript_1.DataType.UUID,
|
|
62
|
+
allowNull: false,
|
|
63
|
+
})
|
|
64
|
+
/** The ID of the entity of which this row hold the custom field entries of, e.g. fleetId / etc. */
|
|
65
|
+
,
|
|
66
|
+
__metadata("design:type", String)
|
|
67
|
+
], CustomFieldEntries.prototype, "entityId", void 0);
|
|
63
68
|
__decorate([
|
|
64
69
|
(0, sequelize_typescript_1.Column)({
|
|
65
70
|
type: sequelize_typescript_1.DataType.JSONB,
|
|
@@ -88,11 +93,6 @@ __decorate([
|
|
|
88
93
|
__metadata("design:type", Date)
|
|
89
94
|
], CustomFieldEntries.prototype, "updatedAt", void 0);
|
|
90
95
|
__decorate([
|
|
91
|
-
sequelize_typescript_1.BeforeBulkCreate,
|
|
92
|
-
sequelize_typescript_1.BeforeBulkUpdate,
|
|
93
|
-
sequelize_typescript_1.BeforeUpdate,
|
|
94
|
-
sequelize_typescript_1.BeforeCreate,
|
|
95
|
-
sequelize_typescript_1.BeforeUpsert,
|
|
96
96
|
sequelize_typescript_1.AfterUpsert,
|
|
97
97
|
__metadata("design:type", Function),
|
|
98
98
|
__metadata("design:paramtypes", [CustomFieldEntries, Object]),
|
|
@@ -110,17 +110,12 @@ CustomFieldEntries = __decorate([
|
|
|
110
110
|
},
|
|
111
111
|
],
|
|
112
112
|
validate: {
|
|
113
|
-
validationByType() {
|
|
114
|
-
if (Object.keys(this.customFields ?? {}).length) {
|
|
115
|
-
|
|
116
|
-
.then((definitionsByName) => {
|
|
117
|
-
(0, validations_1.validateInstanceCustomFieldEntries)(this, definitionsByName);
|
|
118
|
-
})
|
|
119
|
-
.catch((error) => {
|
|
120
|
-
logger_1.default.error('Validation error on custom_field_entries', error);
|
|
121
|
-
throw error;
|
|
122
|
-
});
|
|
113
|
+
async validationByType() {
|
|
114
|
+
if (!Object.keys(this.customFields ?? {}).length) {
|
|
115
|
+
return;
|
|
123
116
|
}
|
|
117
|
+
const definitionsByName = await CustomFieldDefinitionRepo.getCustomFieldDefinitionsDictionary([this]);
|
|
118
|
+
(0, validations_1.validateInstanceCustomFieldEntries)(this, definitionsByName);
|
|
124
119
|
},
|
|
125
120
|
},
|
|
126
121
|
})
|
package/dist/models/index.d.ts
CHANGED
|
@@ -6,8 +6,12 @@ import ContextAwareTestModel from './tests/contextAwareModels/ContextAwareTestMo
|
|
|
6
6
|
import ContextTestModel from './tests/contextAwareModels/ContextTestModel';
|
|
7
7
|
import AssociatedTestModel from './tests/AssociatedTestModel';
|
|
8
8
|
import type { CustomFieldOptions } from '../types';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
import CustomFieldEntries from './CustomFieldEntries';
|
|
10
|
+
interface InitTablesOptions {
|
|
11
|
+
schemaPrefix?: string;
|
|
12
|
+
schemaVersion?: string;
|
|
13
|
+
useCustomFieldsEntries?: boolean;
|
|
14
|
+
}
|
|
15
|
+
declare const initTables: (sequelize: Sequelize, getUser: CustomFieldOptions['getUser'], { schemaPrefix, schemaVersion, useCustomFieldsEntries, }?: InitTablesOptions) => Promise<void>;
|
|
12
16
|
declare const initTestModels: (sequelize: Sequelize) => Promise<void>;
|
|
13
|
-
export { CustomFieldValue, CustomFieldDefinition, TestModel, AssociatedTestModel, ContextAwareTestModel, ContextTestModel, initTables, initTestModels, };
|
|
17
|
+
export { CustomFieldValue, CustomFieldDefinition, CustomFieldEntries, TestModel, AssociatedTestModel, ContextAwareTestModel, ContextTestModel, 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.ContextTestModel = exports.ContextAwareTestModel = exports.AssociatedTestModel = exports.TestModel = exports.CustomFieldDefinition = exports.CustomFieldValue = void 0;
|
|
6
|
+
exports.initTestModels = exports.initTables = exports.ContextTestModel = exports.ContextAwareTestModel = exports.AssociatedTestModel = exports.TestModel = exports.CustomFieldEntries = 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"));
|
|
@@ -20,21 +20,23 @@ exports.ContextTestModel = ContextTestModel_1.default;
|
|
|
20
20
|
const AssociatedTestModel_1 = __importDefault(require("./tests/AssociatedTestModel"));
|
|
21
21
|
exports.AssociatedTestModel = AssociatedTestModel_1.default;
|
|
22
22
|
const CustomFieldEntries_1 = __importDefault(require("./CustomFieldEntries"));
|
|
23
|
+
exports.CustomFieldEntries = CustomFieldEntries_1.default;
|
|
23
24
|
const productionModels = [CustomFieldDefinition_1.default, CustomFieldValue_1.default];
|
|
24
25
|
const testModels = [TestModel_1.default, AssociatedTestModel_1.default, ContextAwareTestModel_1.default, ContextTestModel_1.default];
|
|
25
26
|
const SADOT_MIGRATION_PREFIX = 'sadot-migration';
|
|
26
27
|
const SCHEMA_VERSION = 'fb0fa867-1241-4816-b08d-5ed9060c7ae5';
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const initTables = async (sequelize, getUser, { schemaPrefix, schemaVersion, useCustomFieldsEntries, } = {
|
|
29
|
+
schemaPrefix: SADOT_MIGRATION_PREFIX,
|
|
30
|
+
schemaVersion: SCHEMA_VERSION,
|
|
31
|
+
useCustomFieldsEntries: false,
|
|
32
|
+
}) => {
|
|
33
|
+
const CUSTOM_FIELDS_SCHEMA_VERSION = `${schemaPrefix}_${schemaVersion}${useCustomFieldsEntries ? '_withEntries' : ''}`;
|
|
31
34
|
logger_1.default.info('custom-fields: initialize custom-fields tables');
|
|
32
35
|
// Detect models and import them to the orm
|
|
33
36
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
34
37
|
if (!sequelize.addModels) {
|
|
35
38
|
throw new Error('sequelize instance must have addModels function');
|
|
36
39
|
}
|
|
37
|
-
const schemaVersion = useCustomFieldsEntries ? CUSTOM_FIELDS_SCHEMA_VERSION_WITH_ENTRIES : CUSTOM_FIELDS_SCHEMA_VERSION;
|
|
38
40
|
if (useCustomFieldsEntries) {
|
|
39
41
|
productionModels.push(CustomFieldEntries_1.default);
|
|
40
42
|
}
|
|
@@ -68,17 +70,26 @@ const initTables = async (sequelize, getUser, options) => {
|
|
|
68
70
|
timestamps: false,
|
|
69
71
|
schema: 'public',
|
|
70
72
|
});
|
|
71
|
-
const migrations = await SequelizeMeta.findAll({ raw: true });
|
|
72
|
-
const currentSadotSchemaVersion = migrations.
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
const migrations = await SequelizeMeta.findAll({ where: { name: { [sequelize_1.Op.like]: `${schemaPrefix}%` } }, raw: true });
|
|
74
|
+
const currentSadotSchemaVersion = migrations.at(-1);
|
|
75
|
+
const expectedSchemaVersionIndex = migrations.findIndex((m) => m.name === CUSTOM_FIELDS_SCHEMA_VERSION);
|
|
76
|
+
if (!currentSadotSchemaVersion || currentSadotSchemaVersion.name !== CUSTOM_FIELDS_SCHEMA_VERSION) {
|
|
75
77
|
await CustomFieldDefinition_1.default.sync({ alter: true });
|
|
76
78
|
await CustomFieldValue_1.default.sync({ alter: true });
|
|
79
|
+
// T.Y TODO: Remove the if statement once we're ready to add the new entries table for all MS
|
|
77
80
|
if (useCustomFieldsEntries) {
|
|
78
81
|
await CustomFieldEntries_1.default.sync({ alter: true });
|
|
79
82
|
}
|
|
80
|
-
|
|
83
|
+
if (expectedSchemaVersionIndex === -1) {
|
|
84
|
+
await SequelizeMeta.create({ name: CUSTOM_FIELDS_SCHEMA_VERSION });
|
|
85
|
+
}
|
|
81
86
|
logger_1.default.info('custom-fields: models synced');
|
|
87
|
+
if (migrations.length && expectedSchemaVersionIndex !== -1 && expectedSchemaVersionIndex < migrations.length - 1) {
|
|
88
|
+
// We have existing migrations, and we are calling `sync`.
|
|
89
|
+
// This means we are in a `down` migration, and hence we should delete newer migrations to ensure we can reapply them.
|
|
90
|
+
const migrationsToDelete = migrations.slice(expectedSchemaVersionIndex + 1);
|
|
91
|
+
await SequelizeMeta.destroy({ where: { name: { [sequelize_1.Op.in]: migrationsToDelete.map((m) => m.name) } } });
|
|
92
|
+
}
|
|
82
93
|
}
|
|
83
94
|
};
|
|
84
95
|
exports.initTables = initTables;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type Includeable, type Transaction, type FindOptions, type WhereOptions } from 'sequelize';
|
|
2
|
-
import { CustomFieldDefinition } from '../models';
|
|
2
|
+
import { CustomFieldDefinition, type CustomFieldEntries } from '../models';
|
|
3
3
|
import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../types/definition';
|
|
4
4
|
import type { ModelOptions } from '../types';
|
|
5
|
-
import type { CustomFieldEntriesDTO } from '../types/entries';
|
|
6
5
|
export declare const create: (data: CreateCustomFieldDefinition) => Promise<CustomFieldDefinition>;
|
|
7
6
|
interface SadotFindOptions {
|
|
8
7
|
withDisabled?: boolean;
|
|
@@ -28,10 +27,10 @@ export declare const destroy: (id: string) => Promise<number>;
|
|
|
28
27
|
*/
|
|
29
28
|
export declare const getRequiredFields: (modelType: string, modelId: string | string[], entityId: string | string[], modelOptions?: ModelOptions) => Promise<string[]>;
|
|
30
29
|
/**
|
|
31
|
-
* @returns A dictionary of custom field definitions by name.
|
|
32
|
-
* @throws A MissingDefinitionError if any of the custom fields doesn't have a definition.
|
|
30
|
+
* @returns A promise resolving with a dictionary of custom field definitions by name.
|
|
31
|
+
* @throws A {@link MissingDefinitionError} if any of the custom fields doesn't have a definition.
|
|
33
32
|
*/
|
|
34
|
-
export declare const getCustomFieldDefinitionsDictionary: (instances:
|
|
33
|
+
export declare const getCustomFieldDefinitionsDictionary: (instances: CustomFieldEntries[], options?: SadotGetDefinitionsByEntityIdsOptions) => Promise<{
|
|
35
34
|
[definitionName: string]: CustomFieldDefinition;
|
|
36
35
|
}>;
|
|
37
36
|
export {};
|
|
@@ -88,30 +88,28 @@ const getRequiredFields = async (modelType, modelId, entityId, modelOptions = {}
|
|
|
88
88
|
};
|
|
89
89
|
exports.getRequiredFields = getRequiredFields;
|
|
90
90
|
/**
|
|
91
|
-
* @returns A dictionary of custom field definitions by name.
|
|
92
|
-
* @throws A MissingDefinitionError if any of the custom fields doesn't have a definition.
|
|
91
|
+
* @returns A promise resolving with a dictionary of custom field definitions by name.
|
|
92
|
+
* @throws A {@link MissingDefinitionError} if any of the custom fields doesn't have a definition.
|
|
93
93
|
*/
|
|
94
94
|
const getCustomFieldDefinitionsDictionary = async (instances, options = { withDisabled: false, modelOptions: {} }) => {
|
|
95
|
-
const { modelType } = instances[0];
|
|
95
|
+
const { modelType } = instances[0]?.dataValues ?? {};
|
|
96
96
|
const customFields = new Set();
|
|
97
97
|
const modelIds = [];
|
|
98
|
+
const entityIds = new Set();
|
|
98
99
|
instances.forEach((instance) => {
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
const { dataValues: { modelId, entityId, customFields: instanceCustomFields } } = instance;
|
|
101
|
+
modelIds.push(modelId);
|
|
102
|
+
entityIds.add(entityId);
|
|
103
|
+
Object.keys(instanceCustomFields ?? {}).forEach((fieldName) => {
|
|
101
104
|
customFields.add(fieldName);
|
|
102
105
|
});
|
|
103
106
|
});
|
|
104
|
-
const { include, useEntityIdFromInclude } = options.modelOptions;
|
|
105
107
|
const where = {
|
|
106
108
|
modelType,
|
|
107
|
-
|
|
109
|
+
entityId: { [sequelize_1.Op.in]: Array.from(entityIds) },
|
|
110
|
+
name: { [sequelize_1.Op.in]: Array.from(customFields) },
|
|
108
111
|
};
|
|
109
|
-
const definitions = await (0, exports.findAll)({
|
|
110
|
-
where,
|
|
111
|
-
transaction: options.transaction,
|
|
112
|
-
include: include?.(modelIds),
|
|
113
|
-
raw: true,
|
|
114
|
-
}, options);
|
|
112
|
+
const definitions = await (0, exports.findAll)(where, { ...options });
|
|
115
113
|
const matchedDefinitions = definitions.filter((def) => customFields.has(def.name));
|
|
116
114
|
const matchedDefinitionsByName = Object.fromEntries(matchedDefinitions.map((definition) => [definition.name, definition]));
|
|
117
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 {};
|