@autofleet/sadot 0.13.0-beta.12 → 0.13.0-beta.3
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/validator/index.js +3 -14
- package/dist/hooks/hooks.js +19 -90
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -1
- package/dist/models/CustomValidator.js +0 -1
- package/dist/models/index.js +0 -15
- package/dist/repository/validator.d.ts +1 -1
- package/dist/repository/validator.js +7 -17
- package/dist/tests/helpers/database-config.js +1 -1
- package/dist/utils/validations/schema/validator-schema.js +0 -1
- package/package.json +1 -1
- package/src/api/v1/validator/index.ts +3 -17
- package/src/hooks/hooks.ts +20 -110
- package/src/index.ts +5 -0
- package/src/models/CustomValidator.ts +0 -2
- package/src/models/index.ts +0 -16
- package/src/repository/validator.ts +9 -17
- package/src/tests/helpers/database-config.ts +1 -1
- package/src/utils/validations/schema/validator-schema.ts +0 -1
- package/validator-test.js +0 -79
|
@@ -80,9 +80,8 @@ router.get('/', async (req, res) => {
|
|
|
80
80
|
*/
|
|
81
81
|
router.get('/:validatorId', async (req, res) => {
|
|
82
82
|
try {
|
|
83
|
-
const { validatorId
|
|
84
|
-
|
|
85
|
-
const validators = await ValidatorRepo.findAll({ id: validatorId, modelType: modelName }, { withDisabled: true });
|
|
83
|
+
const { validatorId } = req.params;
|
|
84
|
+
const validators = await ValidatorRepo.findAll({ id: validatorId });
|
|
86
85
|
if (!validators.length) {
|
|
87
86
|
throw new errors_1.ResourceNotFoundError('Validator not found');
|
|
88
87
|
}
|
|
@@ -104,11 +103,6 @@ router.patch('/:validatorId', async (req, res) => {
|
|
|
104
103
|
if (validatedPayload.schema) {
|
|
105
104
|
(0, validator_schema_1.validateValidatorSchema)(validatedPayload.schema);
|
|
106
105
|
}
|
|
107
|
-
// First verify the validator exists, including disabled ones
|
|
108
|
-
const existingValidators = await ValidatorRepo.findAll({ id: validatorId }, { withDisabled: true });
|
|
109
|
-
if (!existingValidators.length) {
|
|
110
|
-
throw new errors_1.ResourceNotFoundError('Validator not found');
|
|
111
|
-
}
|
|
112
106
|
const [count, validators] = await ValidatorRepo.update(validatorId, validatedPayload);
|
|
113
107
|
if (!count) {
|
|
114
108
|
throw new errors_1.ResourceNotFoundError('Validator not found');
|
|
@@ -125,14 +119,9 @@ router.patch('/:validatorId', async (req, res) => {
|
|
|
125
119
|
router.delete('/:validatorId', async (req, res) => {
|
|
126
120
|
try {
|
|
127
121
|
const { validatorId } = req.params;
|
|
128
|
-
// First verify the validator exists, including disabled ones
|
|
129
|
-
const existingValidators = await ValidatorRepo.findAll({ id: validatorId }, { withDisabled: true });
|
|
130
|
-
if (!existingValidators.length) {
|
|
131
|
-
throw new errors_1.ResourceNotFoundError('Validator not found');
|
|
132
|
-
}
|
|
133
122
|
const [count] = await ValidatorRepo.disable(validatorId);
|
|
134
123
|
if (!count) {
|
|
135
|
-
throw new errors_1.ResourceNotFoundError('Validator
|
|
124
|
+
throw new errors_1.ResourceNotFoundError('Validator not found');
|
|
136
125
|
}
|
|
137
126
|
return res.status(http_status_codes_1.StatusCodes.NO_CONTENT).send();
|
|
138
127
|
}
|
package/dist/hooks/hooks.js
CHANGED
|
@@ -41,63 +41,8 @@ const ajv = new ajv_1.default({
|
|
|
41
41
|
allErrors: true,
|
|
42
42
|
strict: false, // Disable strict mode to avoid warnings
|
|
43
43
|
strictTypes: false, // Disable strict type checking
|
|
44
|
-
$data: true, // Enable $data references
|
|
45
44
|
});
|
|
46
45
|
(0, ajv_formats_1.default)(ajv);
|
|
47
|
-
/**
|
|
48
|
-
* Helper function to manually copy object properties
|
|
49
|
-
* This is more efficient for large objects and avoids excessive object creation
|
|
50
|
-
*/
|
|
51
|
-
const manualObjectCopy = (sourceObj, additionalProps) => {
|
|
52
|
-
const resultObj = Object.create(null);
|
|
53
|
-
// Copy properties from source object
|
|
54
|
-
if (sourceObj) {
|
|
55
|
-
Object.keys(sourceObj).forEach((key) => {
|
|
56
|
-
resultObj[key] = sourceObj[key];
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
// Add additional properties if provided
|
|
60
|
-
if (additionalProps) {
|
|
61
|
-
Object.keys(additionalProps).forEach((key) => {
|
|
62
|
-
resultObj[key] = additionalProps[key];
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
return resultObj;
|
|
66
|
-
};
|
|
67
|
-
/**
|
|
68
|
-
* Fetches complete custom fields for an instance by merging DB values with update values
|
|
69
|
-
* This is needed for partial updates to ensure all related fields are available for validation
|
|
70
|
-
*/
|
|
71
|
-
const getCompleteCustomFields = async (instance, options) => {
|
|
72
|
-
// If we don't have an instance id or no custom fields being updated, return original fields
|
|
73
|
-
if (!instance.id || !instance.customFields || Object.keys(instance.customFields).length === 0) {
|
|
74
|
-
return instance.customFields || {};
|
|
75
|
-
}
|
|
76
|
-
try {
|
|
77
|
-
const ModelClass = instance.constructor;
|
|
78
|
-
// Only select the customFields column to minimize data transfer
|
|
79
|
-
const currentCustomFields = await ModelClass.findOne({
|
|
80
|
-
where: { id: instance.id },
|
|
81
|
-
attributes: ['customFields'],
|
|
82
|
-
transaction: options.transaction,
|
|
83
|
-
raw: true, // Get plain object instead of model instance for better performance
|
|
84
|
-
});
|
|
85
|
-
if (currentCustomFields?.customFields) {
|
|
86
|
-
// Merge existing fields with update fields using our helper function
|
|
87
|
-
const completeFields = manualObjectCopy(currentCustomFields.customFields, instance.customFields);
|
|
88
|
-
logger_1.default.debug('sadot - fetched complete custom fields for validation', {
|
|
89
|
-
fieldsCount: Object.keys(completeFields).length,
|
|
90
|
-
updateFieldsCount: Object.keys(instance.customFields).length,
|
|
91
|
-
});
|
|
92
|
-
return completeFields;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch (error) {
|
|
96
|
-
logger_1.default.error('sadot - error fetching complete model for validation', { error });
|
|
97
|
-
// Continue with partial data if we can't fetch the complete model
|
|
98
|
-
}
|
|
99
|
-
return instance.customFields || {};
|
|
100
|
-
};
|
|
101
46
|
/**
|
|
102
47
|
* Validates the model using custom validators
|
|
103
48
|
*/
|
|
@@ -125,26 +70,21 @@ const validateModel = async (instance, options, scopeAttributes, isCreate = fals
|
|
|
125
70
|
return;
|
|
126
71
|
}
|
|
127
72
|
// For updates, get the previous values
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
// Get complete custom fields by merging DB values with update values
|
|
136
|
-
// This is especially important for partial updates to ensure all related fields are available
|
|
137
|
-
const completeCustomFields = !isCreate
|
|
138
|
-
? await getCompleteCustomFields(instance, options)
|
|
139
|
-
: instance.customFields || {};
|
|
73
|
+
const originalValues = isCreate
|
|
74
|
+
? null
|
|
75
|
+
: {
|
|
76
|
+
...instance.previous(),
|
|
77
|
+
customFields: instance.previous('customFields') || {},
|
|
78
|
+
};
|
|
140
79
|
// For debugging in case of update
|
|
141
80
|
if (!isCreate) {
|
|
142
|
-
// Create after object for logging
|
|
143
|
-
const logAfterObj = manualObjectCopy(instance.dataValues, { customFields: completeCustomFields });
|
|
144
81
|
logger_1.default.debug('sadot - validate with values', {
|
|
145
82
|
before: originalValues,
|
|
146
|
-
after:
|
|
147
|
-
|
|
83
|
+
after: {
|
|
84
|
+
...instance.dataValues,
|
|
85
|
+
...instance.customFields,
|
|
86
|
+
},
|
|
87
|
+
schema: JSON.stringify(validators[0].schema),
|
|
148
88
|
});
|
|
149
89
|
}
|
|
150
90
|
// eslint-disable-next-line no-restricted-syntax
|
|
@@ -152,7 +92,7 @@ const validateModel = async (instance, options, scopeAttributes, isCreate = fals
|
|
|
152
92
|
const { schema } = validator;
|
|
153
93
|
const typedSchema = schema;
|
|
154
94
|
logger_1.default.debug('sadot - validating with schema', {
|
|
155
|
-
schema,
|
|
95
|
+
schema: JSON.stringify(schema),
|
|
156
96
|
hasAfterProps: !!typedSchema.properties?.after,
|
|
157
97
|
hasBeforeProps: !!typedSchema.properties?.before,
|
|
158
98
|
});
|
|
@@ -166,12 +106,12 @@ const validateModel = async (instance, options, scopeAttributes, isCreate = fals
|
|
|
166
106
|
after: typedSchema.properties.after,
|
|
167
107
|
},
|
|
168
108
|
});
|
|
169
|
-
const isValid = validateSchema(
|
|
109
|
+
const isValid = validateSchema({
|
|
170
110
|
after: {
|
|
171
111
|
...instance.dataValues,
|
|
172
|
-
customFields
|
|
112
|
+
...instance.customFields,
|
|
173
113
|
},
|
|
174
|
-
})
|
|
114
|
+
});
|
|
175
115
|
if (!isValid) {
|
|
176
116
|
const errorDetails = validateSchema.errors?.map((err) => `${err.instancePath || ''} ${err.message || 'Invalid value'}`).join(', ');
|
|
177
117
|
throw new errors_1.BadRequest([new Error(`Validation failed for ${modelType}: ${errorDetails}`)]);
|
|
@@ -181,22 +121,11 @@ const validateModel = async (instance, options, scopeAttributes, isCreate = fals
|
|
|
181
121
|
else {
|
|
182
122
|
// For update operations, we need both before and after
|
|
183
123
|
const validateSchema = ajv.compile(typedSchema);
|
|
184
|
-
|
|
185
|
-
const afterObj = manualObjectCopy(instance.dataValues);
|
|
186
|
-
// Add complete custom fields
|
|
187
|
-
afterObj.customFields = completeCustomFields;
|
|
188
|
-
// Create validation payload
|
|
189
|
-
const payload = {
|
|
124
|
+
const isValid = validateSchema({
|
|
190
125
|
before: originalValues,
|
|
191
|
-
after:
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const isValid = validateSchema(JSON.parse(JSON.stringify(payload)));
|
|
195
|
-
logger_1.default.info('sadot - validation result', {
|
|
196
|
-
isValid,
|
|
197
|
-
test: {
|
|
198
|
-
before: originalValues,
|
|
199
|
-
after: afterObj,
|
|
126
|
+
after: {
|
|
127
|
+
...instance.dataValues,
|
|
128
|
+
...instance.customFields,
|
|
200
129
|
},
|
|
201
130
|
});
|
|
202
131
|
if (!isValid) {
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ import type { CustomFieldOptions, ModelFetcher, Models } from './types';
|
|
|
4
4
|
export * from './utils/validations/schema/custom-fields';
|
|
5
5
|
export * from './utils/constants';
|
|
6
6
|
export * from './utils/helpers';
|
|
7
|
+
export * as DefinitionRepo from './repository/definition';
|
|
8
|
+
export * as ValueRepo from './repository/value';
|
|
9
|
+
export * as ValidatorRepo from './repository/validator';
|
|
7
10
|
/**
|
|
8
11
|
* Adding custom fields enrichment to the models inside the MODELS_FILE_NAME json file
|
|
9
12
|
* @see {@link 'custom-fields/config'} for configurations
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
29
29
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
30
30
|
};
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.disableCustomFields = void 0;
|
|
32
|
+
exports.disableCustomFields = exports.ValidatorRepo = exports.ValueRepo = exports.DefinitionRepo = void 0;
|
|
33
33
|
const models_1 = require("./models");
|
|
34
34
|
const api_1 = __importDefault(require("./api"));
|
|
35
35
|
const db_1 = __importDefault(require("./utils/db"));
|
|
@@ -38,6 +38,10 @@ const init_1 = require("./utils/init");
|
|
|
38
38
|
__exportStar(require("./utils/validations/schema/custom-fields"), exports);
|
|
39
39
|
__exportStar(require("./utils/constants"), exports);
|
|
40
40
|
__exportStar(require("./utils/helpers"), exports);
|
|
41
|
+
// Export repositories
|
|
42
|
+
exports.DefinitionRepo = __importStar(require("./repository/definition"));
|
|
43
|
+
exports.ValueRepo = __importStar(require("./repository/value"));
|
|
44
|
+
exports.ValidatorRepo = __importStar(require("./repository/validator"));
|
|
41
45
|
/**
|
|
42
46
|
* Adding custom fields enrichment to the models inside the MODELS_FILE_NAME json file
|
|
43
47
|
* @see {@link 'custom-fields/config'} for configurations
|
|
@@ -90,7 +90,6 @@ __decorate([
|
|
|
90
90
|
__metadata("design:returntype", void 0)
|
|
91
91
|
], CustomValidator, "afterSaveHandler", null);
|
|
92
92
|
CustomValidator = __decorate([
|
|
93
|
-
(0, sequelize_typescript_1.DefaultScope)(() => ({ where: { disabled: false } })),
|
|
94
93
|
(0, sequelize_typescript_1.Table)({
|
|
95
94
|
timestamps: true,
|
|
96
95
|
})
|
package/dist/models/index.js
CHANGED
|
@@ -58,21 +58,6 @@ const initTables = async (sequelize, getUser, { schemaPrefix, schemaVersion, use
|
|
|
58
58
|
},
|
|
59
59
|
};
|
|
60
60
|
});
|
|
61
|
-
CustomValidator_1.default.addScope('userScope', () => {
|
|
62
|
-
const user = getUser();
|
|
63
|
-
if (!user?.permissions) {
|
|
64
|
-
return {};
|
|
65
|
-
}
|
|
66
|
-
return {
|
|
67
|
-
where: {
|
|
68
|
-
entityId: [
|
|
69
|
-
...Object.keys(user.permissions.fleets),
|
|
70
|
-
...Object.keys(user.permissions.businessModels),
|
|
71
|
-
...Object.keys(user.permissions.demandSources),
|
|
72
|
-
],
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
});
|
|
76
61
|
logger_1.default.info('custom-fields: models added');
|
|
77
62
|
const SequelizeMeta = sequelize.define('SequelizeMeta', {
|
|
78
63
|
name: {
|
|
@@ -16,23 +16,13 @@ exports.create = create;
|
|
|
16
16
|
const findAll = async (where = {}, options = { withDisabled: false }) => {
|
|
17
17
|
logger_1.default.debug('custom-validator - find all validators');
|
|
18
18
|
const { transaction, withDisabled } = options;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
// Use defaultScope and userScope to filter both disabled and by permissions
|
|
30
|
-
// The defaultScope keeps only non-disabled validators
|
|
31
|
-
validators = await models_1.CustomValidator.scope(['defaultScope', 'userScope']).findAll({
|
|
32
|
-
where,
|
|
33
|
-
transaction,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
19
|
+
const fullWhere = withDisabled
|
|
20
|
+
? where
|
|
21
|
+
: { ...where, disabled: false };
|
|
22
|
+
const validators = await models_1.CustomValidator.findAll({
|
|
23
|
+
where: fullWhere,
|
|
24
|
+
transaction,
|
|
25
|
+
});
|
|
36
26
|
return validators;
|
|
37
27
|
};
|
|
38
28
|
exports.findAll = findAll;
|
|
@@ -4,7 +4,7 @@ exports.default = {
|
|
|
4
4
|
test: {
|
|
5
5
|
username: process.env.DB_USERNAME || '',
|
|
6
6
|
password: process.env.DB_PASSWORD || null,
|
|
7
|
-
database: process.env.DB_NAME || '
|
|
7
|
+
database: process.env.DB_NAME || 'sadot_package_test',
|
|
8
8
|
host: process.env.DB_HOST || '127.0.0.1',
|
|
9
9
|
port: process.env.DB_PORT || 5432,
|
|
10
10
|
dialect: process.env.DB_TYPE || 'postgres',
|
package/package.json
CHANGED
|
@@ -66,9 +66,8 @@ router.get<
|
|
|
66
66
|
*/
|
|
67
67
|
router.get<{ modelName: string; validatorId: string }, CustomValidator>('/:validatorId', async (req, res) => {
|
|
68
68
|
try {
|
|
69
|
-
const { validatorId
|
|
70
|
-
|
|
71
|
-
const validators = await ValidatorRepo.findAll({ id: validatorId, modelType: modelName }, { withDisabled: true });
|
|
69
|
+
const { validatorId } = req.params;
|
|
70
|
+
const validators = await ValidatorRepo.findAll({ id: validatorId });
|
|
72
71
|
|
|
73
72
|
if (!validators.length) {
|
|
74
73
|
throw new ResourceNotFoundError('Validator not found');
|
|
@@ -95,12 +94,6 @@ router.patch<{ modelName: string; validatorId: string }, CustomValidator>('/:val
|
|
|
95
94
|
validateValidatorSchema(validatedPayload.schema);
|
|
96
95
|
}
|
|
97
96
|
|
|
98
|
-
// First verify the validator exists, including disabled ones
|
|
99
|
-
const existingValidators = await ValidatorRepo.findAll({ id: validatorId }, { withDisabled: true });
|
|
100
|
-
if (!existingValidators.length) {
|
|
101
|
-
throw new ResourceNotFoundError('Validator not found');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
97
|
const [count, validators] = await ValidatorRepo.update(validatorId, validatedPayload);
|
|
105
98
|
|
|
106
99
|
if (!count) {
|
|
@@ -119,17 +112,10 @@ router.patch<{ modelName: string; validatorId: string }, CustomValidator>('/:val
|
|
|
119
112
|
router.delete<{ modelName: string; validatorId: string }>('/:validatorId', async (req, res) => {
|
|
120
113
|
try {
|
|
121
114
|
const { validatorId } = req.params;
|
|
122
|
-
|
|
123
|
-
// First verify the validator exists, including disabled ones
|
|
124
|
-
const existingValidators = await ValidatorRepo.findAll({ id: validatorId }, { withDisabled: true });
|
|
125
|
-
if (!existingValidators.length) {
|
|
126
|
-
throw new ResourceNotFoundError('Validator not found');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
115
|
const [count] = await ValidatorRepo.disable(validatorId);
|
|
130
116
|
|
|
131
117
|
if (!count) {
|
|
132
|
-
throw new ResourceNotFoundError('Validator
|
|
118
|
+
throw new ResourceNotFoundError('Validator not found');
|
|
133
119
|
}
|
|
134
120
|
|
|
135
121
|
return res.status(StatusCodes.NO_CONTENT).send();
|
package/src/hooks/hooks.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { WhereOptions } from 'sequelize';
|
|
2
2
|
import Ajv from 'ajv';
|
|
3
|
-
import addFormats from
|
|
3
|
+
import addFormats from "ajv-formats";
|
|
4
4
|
import { BadRequest } from '@autofleet/errors';
|
|
5
5
|
import logger from '../utils/logger';
|
|
6
6
|
import * as ValidatorRepo from '../repository/validator';
|
|
@@ -15,77 +15,10 @@ const ajv = new Ajv({
|
|
|
15
15
|
allErrors: true,
|
|
16
16
|
strict: false, // Disable strict mode to avoid warnings
|
|
17
17
|
strictTypes: false, // Disable strict type checking
|
|
18
|
-
$data: true, // Enable $data references
|
|
19
18
|
});
|
|
20
19
|
|
|
21
20
|
addFormats(ajv);
|
|
22
21
|
|
|
23
|
-
/**
|
|
24
|
-
* Helper function to manually copy object properties
|
|
25
|
-
* This is more efficient for large objects and avoids excessive object creation
|
|
26
|
-
*/
|
|
27
|
-
const manualObjectCopy = (sourceObj: Record<string, any>, additionalProps?: Record<string, any>): Record<string, any> => {
|
|
28
|
-
const resultObj = Object.create(null);
|
|
29
|
-
|
|
30
|
-
// Copy properties from source object
|
|
31
|
-
if (sourceObj) {
|
|
32
|
-
Object.keys(sourceObj).forEach((key) => {
|
|
33
|
-
resultObj[key] = sourceObj[key];
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Add additional properties if provided
|
|
38
|
-
if (additionalProps) {
|
|
39
|
-
Object.keys(additionalProps).forEach((key) => {
|
|
40
|
-
resultObj[key] = additionalProps[key];
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return resultObj;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Fetches complete custom fields for an instance by merging DB values with update values
|
|
49
|
-
* This is needed for partial updates to ensure all related fields are available for validation
|
|
50
|
-
*/
|
|
51
|
-
const getCompleteCustomFields = async (instance, options): Promise<Record<string, any>> => {
|
|
52
|
-
// If we don't have an instance id or no custom fields being updated, return original fields
|
|
53
|
-
if (!instance.id || !instance.customFields || Object.keys(instance.customFields).length === 0) {
|
|
54
|
-
return instance.customFields || {};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
const ModelClass = instance.constructor;
|
|
59
|
-
// Only select the customFields column to minimize data transfer
|
|
60
|
-
const currentCustomFields = await ModelClass.findOne({
|
|
61
|
-
where: { id: instance.id },
|
|
62
|
-
attributes: ['customFields'],
|
|
63
|
-
transaction: options.transaction,
|
|
64
|
-
raw: true, // Get plain object instead of model instance for better performance
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
if (currentCustomFields?.customFields) {
|
|
68
|
-
// Merge existing fields with update fields using our helper function
|
|
69
|
-
const completeFields = manualObjectCopy(
|
|
70
|
-
currentCustomFields.customFields,
|
|
71
|
-
instance.customFields,
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
logger.debug('sadot - fetched complete custom fields for validation', {
|
|
75
|
-
fieldsCount: Object.keys(completeFields).length,
|
|
76
|
-
updateFieldsCount: Object.keys(instance.customFields).length,
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
return completeFields;
|
|
80
|
-
}
|
|
81
|
-
} catch (error) {
|
|
82
|
-
logger.error('sadot - error fetching complete model for validation', { error });
|
|
83
|
-
// Continue with partial data if we can't fetch the complete model
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return instance.customFields || {};
|
|
87
|
-
};
|
|
88
|
-
|
|
89
22
|
/**
|
|
90
23
|
* Validates the model using custom validators
|
|
91
24
|
*/
|
|
@@ -132,30 +65,22 @@ const validateModel = async (
|
|
|
132
65
|
}
|
|
133
66
|
|
|
134
67
|
// For updates, get the previous values
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
originalValues.customFields = instance.previous('customFields') || {};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Get complete custom fields by merging DB values with update values
|
|
145
|
-
// This is especially important for partial updates to ensure all related fields are available
|
|
146
|
-
const completeCustomFields = !isCreate
|
|
147
|
-
? await getCompleteCustomFields(instance, options)
|
|
148
|
-
: instance.customFields || {};
|
|
68
|
+
const originalValues = isCreate
|
|
69
|
+
? null
|
|
70
|
+
: {
|
|
71
|
+
...instance.previous(),
|
|
72
|
+
customFields: instance.previous('customFields') || {},
|
|
73
|
+
};
|
|
149
74
|
|
|
150
75
|
// For debugging in case of update
|
|
151
76
|
if (!isCreate) {
|
|
152
|
-
// Create after object for logging
|
|
153
|
-
const logAfterObj = manualObjectCopy(instance.dataValues, { customFields: completeCustomFields });
|
|
154
|
-
|
|
155
77
|
logger.debug('sadot - validate with values', {
|
|
156
78
|
before: originalValues,
|
|
157
|
-
after:
|
|
158
|
-
|
|
79
|
+
after: {
|
|
80
|
+
...instance.dataValues,
|
|
81
|
+
...instance.customFields,
|
|
82
|
+
},
|
|
83
|
+
schema: JSON.stringify(validators[0].schema),
|
|
159
84
|
});
|
|
160
85
|
}
|
|
161
86
|
|
|
@@ -165,7 +90,7 @@ const validateModel = async (
|
|
|
165
90
|
const typedSchema = schema as Record<string, any>;
|
|
166
91
|
|
|
167
92
|
logger.debug('sadot - validating with schema', {
|
|
168
|
-
schema,
|
|
93
|
+
schema: JSON.stringify(schema),
|
|
169
94
|
hasAfterProps: !!typedSchema.properties?.after,
|
|
170
95
|
hasBeforeProps: !!typedSchema.properties?.before,
|
|
171
96
|
});
|
|
@@ -181,12 +106,12 @@ const validateModel = async (
|
|
|
181
106
|
},
|
|
182
107
|
});
|
|
183
108
|
|
|
184
|
-
const isValid = validateSchema(
|
|
109
|
+
const isValid = validateSchema({
|
|
185
110
|
after: {
|
|
186
111
|
...instance.dataValues,
|
|
187
|
-
customFields
|
|
112
|
+
...instance.customFields,
|
|
188
113
|
},
|
|
189
|
-
})
|
|
114
|
+
});
|
|
190
115
|
|
|
191
116
|
if (!isValid) {
|
|
192
117
|
const errorDetails = validateSchema.errors?.map((err) =>
|
|
@@ -199,26 +124,11 @@ const validateModel = async (
|
|
|
199
124
|
// For update operations, we need both before and after
|
|
200
125
|
const validateSchema = ajv.compile(typedSchema);
|
|
201
126
|
|
|
202
|
-
|
|
203
|
-
const afterObj = manualObjectCopy(instance.dataValues);
|
|
204
|
-
|
|
205
|
-
// Add complete custom fields
|
|
206
|
-
afterObj.customFields = completeCustomFields;
|
|
207
|
-
|
|
208
|
-
// Create validation payload
|
|
209
|
-
const payload = {
|
|
127
|
+
const isValid = validateSchema({
|
|
210
128
|
before: originalValues,
|
|
211
|
-
after:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
// Validate
|
|
215
|
-
const isValid = validateSchema(JSON.parse(JSON.stringify(payload)));
|
|
216
|
-
|
|
217
|
-
logger.info('sadot - validation result', {
|
|
218
|
-
isValid,
|
|
219
|
-
test: {
|
|
220
|
-
before: originalValues,
|
|
221
|
-
after: afterObj,
|
|
129
|
+
after: {
|
|
130
|
+
...instance.dataValues,
|
|
131
|
+
...instance.customFields,
|
|
222
132
|
},
|
|
223
133
|
});
|
|
224
134
|
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,11 @@ export * from './utils/constants';
|
|
|
17
17
|
|
|
18
18
|
export * from './utils/helpers';
|
|
19
19
|
|
|
20
|
+
// Export repositories
|
|
21
|
+
export * as DefinitionRepo from './repository/definition';
|
|
22
|
+
export * as ValueRepo from './repository/value';
|
|
23
|
+
export * as ValidatorRepo from './repository/validator';
|
|
24
|
+
|
|
20
25
|
/**
|
|
21
26
|
* Adding custom fields enrichment to the models inside the MODELS_FILE_NAME json file
|
|
22
27
|
* @see {@link 'custom-fields/config'} for configurations
|
|
@@ -6,12 +6,10 @@ import {
|
|
|
6
6
|
DataType,
|
|
7
7
|
Default,
|
|
8
8
|
AfterUpsert,
|
|
9
|
-
DefaultScope,
|
|
10
9
|
} from 'sequelize-typescript';
|
|
11
10
|
import { randomUUID } from 'node:crypto';
|
|
12
11
|
import { sendDimEvent } from '../events';
|
|
13
12
|
|
|
14
|
-
@DefaultScope(() => ({ where: { disabled: false } }))
|
|
15
13
|
@Table({
|
|
16
14
|
timestamps: true,
|
|
17
15
|
})
|
package/src/models/index.ts
CHANGED
|
@@ -68,22 +68,6 @@ const initTables = async (
|
|
|
68
68
|
};
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
CustomValidator.addScope('userScope', () => {
|
|
72
|
-
const user = getUser();
|
|
73
|
-
if (!user?.permissions) {
|
|
74
|
-
return {};
|
|
75
|
-
}
|
|
76
|
-
return {
|
|
77
|
-
where: {
|
|
78
|
-
entityId: [
|
|
79
|
-
...Object.keys(user.permissions.fleets),
|
|
80
|
-
...Object.keys(user.permissions.businessModels),
|
|
81
|
-
...Object.keys(user.permissions.demandSources),
|
|
82
|
-
],
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
});
|
|
86
|
-
|
|
87
71
|
logger.info('custom-fields: models added');
|
|
88
72
|
|
|
89
73
|
const SequelizeMeta = sequelize.define(
|
|
@@ -11,7 +11,7 @@ export interface ValidatorAttributes {
|
|
|
11
11
|
entityId: string;
|
|
12
12
|
entityType: string;
|
|
13
13
|
modelType: string;
|
|
14
|
-
schema:
|
|
14
|
+
schema: Record<string, unknown>;
|
|
15
15
|
disabled?: boolean;
|
|
16
16
|
[key: string]: unknown; // Add index signature for Sequelize compatibility
|
|
17
17
|
}
|
|
@@ -36,22 +36,14 @@ export const findAll = async (
|
|
|
36
36
|
|
|
37
37
|
const { transaction, withDisabled } = options;
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
} else {
|
|
48
|
-
// Use defaultScope and userScope to filter both disabled and by permissions
|
|
49
|
-
// The defaultScope keeps only non-disabled validators
|
|
50
|
-
validators = await CustomValidator.scope(['defaultScope', 'userScope']).findAll({
|
|
51
|
-
where,
|
|
52
|
-
transaction,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
39
|
+
const fullWhere = withDisabled
|
|
40
|
+
? where
|
|
41
|
+
: { ...where, disabled: false };
|
|
42
|
+
|
|
43
|
+
const validators = await CustomValidator.findAll({
|
|
44
|
+
where: fullWhere,
|
|
45
|
+
transaction,
|
|
46
|
+
});
|
|
55
47
|
|
|
56
48
|
return validators;
|
|
57
49
|
};
|
|
@@ -2,7 +2,7 @@ export default {
|
|
|
2
2
|
test: {
|
|
3
3
|
username: process.env.DB_USERNAME || '',
|
|
4
4
|
password: process.env.DB_PASSWORD || null,
|
|
5
|
-
database: process.env.DB_NAME || '
|
|
5
|
+
database: process.env.DB_NAME || 'sadot_package_test',
|
|
6
6
|
host: process.env.DB_HOST || '127.0.0.1',
|
|
7
7
|
port: process.env.DB_PORT || 5432,
|
|
8
8
|
dialect: process.env.DB_TYPE || 'postgres',
|
package/validator-test.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
const Ajv = require("ajv");
|
|
2
|
-
const addFormats = require("ajv-formats");
|
|
3
|
-
const ajv = new Ajv({
|
|
4
|
-
allErrors: true,
|
|
5
|
-
strict: false, // Disable strict mode to avoid warnings
|
|
6
|
-
strictTypes: false, // Disable strict type checking
|
|
7
|
-
$data: true, // Enable $data references
|
|
8
|
-
}); // options can be passed, e.g. {allErrors: true}
|
|
9
|
-
addFormats(ajv);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const schema = {
|
|
13
|
-
"type": "object",
|
|
14
|
-
"properties": {
|
|
15
|
-
"after": {
|
|
16
|
-
"type": "object",
|
|
17
|
-
"properties": {
|
|
18
|
-
"customFields": {
|
|
19
|
-
"type": "object",
|
|
20
|
-
"properties": {
|
|
21
|
-
"actualStartTime": {
|
|
22
|
-
"type": "string",
|
|
23
|
-
"format": "date-time"
|
|
24
|
-
},
|
|
25
|
-
"actualEndTime": {
|
|
26
|
-
"type": "string",
|
|
27
|
-
"format": "date-time",
|
|
28
|
-
"formatMinimum": {
|
|
29
|
-
"$data": "/after/customFields/actualStartTime"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const validate = ajv.compile(schema);
|
|
40
|
-
|
|
41
|
-
const data = {
|
|
42
|
-
"id": "c9c87cec-28c7-4a1f-9376-4d8a4ba0f49c",
|
|
43
|
-
"typeId": "4fcd7096-4c90-46a2-a20e-91bb5bff3ced",
|
|
44
|
-
"priorityId": null,
|
|
45
|
-
"statusId": "51d28a37-c042-429f-a9cd-fbc81bba2b39",
|
|
46
|
-
"subjectId": null,
|
|
47
|
-
"subjectType": null,
|
|
48
|
-
"title": "ff",
|
|
49
|
-
"description": null,
|
|
50
|
-
"dueTime": null,
|
|
51
|
-
"startTime": null,
|
|
52
|
-
"endTime": null,
|
|
53
|
-
"driverId": null,
|
|
54
|
-
"vendorId": null,
|
|
55
|
-
"userId": null,
|
|
56
|
-
"businessModelId": "72ecf740-42a1-46f4-81ca-8132b08c4f04",
|
|
57
|
-
"createdAt": "2025-03-12T12:58:05.529Z",
|
|
58
|
-
"updatedAt": "2025-03-12T12:58:05.529Z",
|
|
59
|
-
"deletedAt": null,
|
|
60
|
-
"actions": [],
|
|
61
|
-
"customFields": {
|
|
62
|
-
"actualStartTime": "2025-03-14T12:48:01.373Z",
|
|
63
|
-
"actualEndTime": "2025-03-01T12:51:46.685Z"
|
|
64
|
-
},
|
|
65
|
-
"isBlocker": false
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const valid = validate({after: data});
|
|
69
|
-
|
|
70
|
-
if (!valid) {
|
|
71
|
-
console.log("invalid, the errors is: ");
|
|
72
|
-
validate.errors.forEach((error) =>
|
|
73
|
-
console.log(error.instancePath, error.message)
|
|
74
|
-
);
|
|
75
|
-
} else {
|
|
76
|
-
console.log("valid");
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
console.log("--------------------");
|