@autofleet/sadot 0.13.5 → 0.13.6

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.
@@ -31,6 +31,7 @@ const ajv_1 = __importDefault(require("ajv"));
31
31
  const joi_1 = __importDefault(require("joi"));
32
32
  const ajv_formats_1 = __importDefault(require("ajv-formats"));
33
33
  const errors_1 = require("@autofleet/errors");
34
+ const ajv_errors_1 = __importDefault(require("ajv-errors"));
34
35
  const logger_1 = __importDefault(require("../utils/logger"));
35
36
  const ValidatorRepo = __importStar(require("../repository/validator"));
36
37
  const DefinitionRepo = __importStar(require("../repository/definition"));
@@ -48,6 +49,7 @@ const ajv = new ajv_1.default({
48
49
  $data: true, // Enable $data references
49
50
  });
50
51
  (0, ajv_formats_1.default)(ajv);
52
+ (0, ajv_errors_1.default)(ajv);
51
53
  /**
52
54
  * Helper function to manually copy object properties
53
55
  * This is more efficient for large objects and avoids excessive object creation
@@ -88,10 +90,22 @@ const getCompleteCustomFields = async (instance, options) => {
88
90
  }
89
91
  return instance.customFields || {};
90
92
  };
93
+ const formatAjvErrors = (errors) => errors.reduce((acc, err) => {
94
+ const basePath = (err.instancePath || '')
95
+ .split('/')
96
+ .filter(Boolean)
97
+ .join('.')
98
+ .replace(/^after\./, '');
99
+ const missingProp = err.keyword === 'required' ? `.${err.params?.missingProperty}` : '';
100
+ const key = (basePath + missingProp).replace(/^\./, '') || 'root';
101
+ const message = err.message || 'Invalid value';
102
+ acc[key] = message;
103
+ return acc;
104
+ }, {});
91
105
  /**
92
106
  * Validates the model using custom validators
93
107
  */
94
- const validateModel = async (instance, options, scopeAttributes, isCreate = false) => {
108
+ const validateModel = async (instance, options, scopeAttributes, modelOptions = {}, isCreate = false) => {
95
109
  var _a;
96
110
  const modelType = instance.constructor.name;
97
111
  logger_1.default.debug('sadot - validating model', { isCreate, modelType });
@@ -121,6 +135,9 @@ const validateModel = async (instance, options, scopeAttributes, isCreate = fals
121
135
  validatorsPromise = ValidatorRepo.findAllByModelType(modelType, entityId, {
122
136
  transaction: options.transaction,
123
137
  attributes: CUSTOM_VALIDATOR_ATTRIBUTES_TO_PULL,
138
+ ...(modelOptions.include && {
139
+ include: modelOptions.include?.(entityId),
140
+ }),
124
141
  raw: true,
125
142
  });
126
143
  if (options.transaction) {
@@ -173,7 +190,10 @@ const validateModel = async (instance, options, scopeAttributes, isCreate = fals
173
190
  })));
174
191
  if (!isValid) {
175
192
  const errorDetails = validateSchema.errors?.map((err) => `${err.instancePath || ''} ${err.message || 'Invalid value'}`).join(', ');
176
- throw new errors_1.BadRequest([new Error(`Validation failed for ${modelType}: ${errorDetails}`)]);
193
+ const formattedErrors = formatAjvErrors(validateSchema.errors);
194
+ throw new errors_1.BadRequest([new Error(`Validation failed for ${modelType}: ${errorDetails}`)], undefined, {
195
+ customError: formattedErrors,
196
+ });
177
197
  }
178
198
  }
179
199
  }
@@ -202,7 +222,10 @@ const validateModel = async (instance, options, scopeAttributes, isCreate = fals
202
222
  const errorDetails = validateSchema
203
223
  .errors
204
224
  ?.map((err) => `${err.instancePath || ''} ${err.message || 'Invalid value'}`).join(', ');
205
- throw new errors_1.BadRequest([new Error(`Validation failed for ${modelType}: ${errorDetails}`)]);
225
+ const formattedErrors = formatAjvErrors(validateSchema.errors);
226
+ throw new errors_1.BadRequest([new Error(`Validation failed for ${modelType}: ${errorDetails}`)], undefined, {
227
+ customError: formattedErrors,
228
+ });
206
229
  }
207
230
  }
208
231
  }
@@ -270,7 +293,7 @@ const beforeCreate = (scopeAttributes, modelOptions = {}, sadotOptions = { useCu
270
293
  throw new errors_2.MissingRequiredCustomFieldError(missingFields);
271
294
  }
272
295
  // Step 2: Validate the model data (including custom fields)
273
- await validateModel(instance, options, scopeAttributes, true);
296
+ await validateModel(instance, options, scopeAttributes, modelOptions, true);
274
297
  // format date and datetime fields
275
298
  formatDates(fieldDefinitions, instance);
276
299
  // Step 3: Save custom field values if they exist
@@ -308,7 +331,7 @@ const beforeUpdate = (scopeAttributes, modelOptions = {}, sadotOptions = { useCu
308
331
  modelType, modelOptions, identifiers, options,
309
332
  });
310
333
  // Step 1: Validate the model data (including custom fields)
311
- await validateModel(instance, options, scopeAttributes, false);
334
+ await validateModel(instance, options, scopeAttributes, modelOptions, false);
312
335
  // format date and datetime fields
313
336
  formatDates(fieldDefinitions, instance);
314
337
  // Step 2: Update custom field values if they exist
@@ -1,9 +1,10 @@
1
- import type { Transactionable } from 'sequelize';
1
+ import type { IncludeOptions, Transactionable } from 'sequelize';
2
2
  import { CustomValidator } from '../models';
3
3
  export interface FindValidatorOptions extends Transactionable {
4
4
  withDisabled?: boolean;
5
5
  attributes?: string[];
6
6
  raw?: boolean;
7
+ include?: IncludeOptions[];
7
8
  }
8
9
  export interface ValidatorAttributes {
9
10
  entityId: string;
@@ -40,7 +40,9 @@ const findAllByModelType = async (modelType, entityId, options = { withDisabled:
40
40
  logger_1.default.debug('custom-validator - find all validators by model type');
41
41
  return (0, exports.findAll)({
42
42
  modelType,
43
- entityId,
43
+ ...(!options.include && {
44
+ entityId,
45
+ }),
44
46
  }, options);
45
47
  };
46
48
  exports.findAllByModelType = findAllByModelType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/sadot",
3
- "version": "0.13.5",
3
+ "version": "0.13.6",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -31,6 +31,7 @@
31
31
  "@autofleet/common-types": "^4.4.0",
32
32
  "@autofleet/events": "^4.0.0",
33
33
  "ajv": "^8.12.0",
34
+ "ajv-errors": "^3.0.0",
34
35
  "ajv-formats": "^3.0.1",
35
36
  "http-status-codes": "^2.3.0",
36
37
  "joi": "^17.7.0",
@@ -3,6 +3,7 @@ import Ajv from 'ajv';
3
3
  import Joi from 'joi';
4
4
  import addFormats from 'ajv-formats';
5
5
  import { BadRequest } from '@autofleet/errors';
6
+ import ajvErrors from 'ajv-errors';
6
7
  import logger from '../utils/logger';
7
8
  import * as ValidatorRepo from '../repository/validator';
8
9
  import * as DefinitionRepo from '../repository/definition';
@@ -25,6 +26,7 @@ const ajv = new Ajv({
25
26
  });
26
27
 
27
28
  addFormats(ajv);
29
+ ajvErrors(ajv);
28
30
 
29
31
  /**
30
32
  * Helper function to manually copy object properties
@@ -76,6 +78,29 @@ const getCompleteCustomFields = async (instance, options): Promise<Record<string
76
78
  return instance.customFields || {};
77
79
  };
78
80
 
81
+ const formatAjvErrors = (
82
+ errors: {
83
+ instancePath?: string;
84
+ keyword: string;
85
+ message?: string;
86
+ params?: Record<string, any>;
87
+ }[],
88
+ ): Record<string, string> => errors.reduce((acc, err) => {
89
+ const basePath = (err.instancePath || '')
90
+ .split('/')
91
+ .filter(Boolean)
92
+ .join('.')
93
+ .replace(/^after\./, '');
94
+
95
+ const missingProp = err.keyword === 'required' ? `.${err.params?.missingProperty}` : '';
96
+ const key = (basePath + missingProp).replace(/^\./, '') || 'root';
97
+
98
+ const message = err.message || 'Invalid value';
99
+ acc[key] = message;
100
+
101
+ return acc;
102
+ }, {} as Record<string, string>);
103
+
79
104
  /**
80
105
  * Validates the model using custom validators
81
106
  */
@@ -83,6 +108,7 @@ const validateModel = async (
83
108
  instance,
84
109
  options,
85
110
  scopeAttributes: string[],
111
+ modelOptions: ModelOptions = {},
86
112
  isCreate = false,
87
113
  ): Promise<void> => {
88
114
  const modelType = instance.constructor.name;
@@ -124,6 +150,9 @@ const validateModel = async (
124
150
  {
125
151
  transaction: options.transaction,
126
152
  attributes: CUSTOM_VALIDATOR_ATTRIBUTES_TO_PULL,
153
+ ...(modelOptions.include && {
154
+ include: modelOptions.include?.(entityId),
155
+ }),
127
156
  raw: true,
128
157
  },
129
158
  );
@@ -189,7 +218,14 @@ const validateModel = async (
189
218
  const errorDetails = validateSchema.errors?.map((err) =>
190
219
  `${(err as any).instancePath || ''} ${(err as any).message || 'Invalid value'}`).join(', ');
191
220
 
192
- throw new BadRequest([new Error(`Validation failed for ${modelType}: ${errorDetails}`)]);
221
+ const formattedErrors = formatAjvErrors(validateSchema.errors);
222
+ throw new BadRequest(
223
+ [new Error(`Validation failed for ${modelType}: ${errorDetails}`)],
224
+ undefined,
225
+ {
226
+ customError: formattedErrors,
227
+ },
228
+ );
193
229
  }
194
230
  }
195
231
  } else {
@@ -224,7 +260,14 @@ const validateModel = async (
224
260
  .errors
225
261
  ?.map((err) => `${(err as any).instancePath || ''} ${(err as any).message || 'Invalid value'}`).join(', ');
226
262
 
227
- throw new BadRequest([new Error(`Validation failed for ${modelType}: ${errorDetails}`)]);
263
+ const formattedErrors = formatAjvErrors(validateSchema.errors);
264
+ throw new BadRequest(
265
+ [new Error(`Validation failed for ${modelType}: ${errorDetails}`)],
266
+ undefined,
267
+ {
268
+ customError: formattedErrors,
269
+ },
270
+ );
228
271
  }
229
272
  }
230
273
  }
@@ -321,7 +364,7 @@ export const beforeCreate = (
321
364
  }
322
365
 
323
366
  // Step 2: Validate the model data (including custom fields)
324
- await validateModel(instance, options, scopeAttributes, true);
367
+ await validateModel(instance, options, scopeAttributes, modelOptions, true);
325
368
 
326
369
  // format date and datetime fields
327
370
  formatDates(fieldDefinitions, instance);
@@ -372,7 +415,7 @@ export const beforeUpdate = (
372
415
  });
373
416
 
374
417
  // Step 1: Validate the model data (including custom fields)
375
- await validateModel(instance, options, scopeAttributes, false);
418
+ await validateModel(instance, options, scopeAttributes, modelOptions, false);
376
419
 
377
420
  // format date and datetime fields
378
421
  formatDates(fieldDefinitions, instance);
@@ -1,4 +1,4 @@
1
- import type { Transactionable } from 'sequelize';
1
+ import type { IncludeOptions, Transactionable } from 'sequelize';
2
2
  import logger from '../utils/logger';
3
3
  import { CustomValidator } from '../models';
4
4
 
@@ -6,6 +6,7 @@ export interface FindValidatorOptions extends Transactionable {
6
6
  withDisabled?: boolean;
7
7
  attributes?: string[];
8
8
  raw?: boolean;
9
+ include?: IncludeOptions[];
9
10
  }
10
11
 
11
12
  // Make sure this interface is compatible with the Sequelize model
@@ -68,7 +69,9 @@ export const findAllByModelType = async (
68
69
  return findAll(
69
70
  {
70
71
  modelType,
71
- entityId,
72
+ ...(!options.include && {
73
+ entityId,
74
+ }),
72
75
  },
73
76
  options,
74
77
  );