@autofleet/sadot 0.13.1 → 0.13.2-beta.1

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.
@@ -36,6 +36,7 @@ const DefinitionRepo = __importStar(require("../repository/definition"));
36
36
  const errors_2 = require("../errors");
37
37
  const scopeAttributes_1 = __importDefault(require("../utils/scopeAttributes"));
38
38
  const updateInstanceValues_1 = __importDefault(require("./utils/updateInstanceValues"));
39
+ const constants_1 = require("../utils/constants");
39
40
  // Initialize Ajv with relaxed settings to avoid warnings
40
41
  const ajv = new ajv_1.default({
41
42
  allErrors: true,
@@ -195,16 +196,8 @@ const validateModel = async (instance, options, scopeAttributes, isCreate = fals
195
196
  }
196
197
  }
197
198
  };
198
- /**
199
- * Hook to handle validation and custom fields during creation
200
- */
201
- const beforeCreate = (scopeAttributes, modelOptions = {}, sadotOptions = { useCustomFieldsEntries: false }) => async (instance, options) => {
202
- logger_1.default.debug('sadot - before create hook');
203
- const { fields } = options;
199
+ const getFieldDefinitions = async ({ modelType, modelOptions, identifiers, options }) => {
204
200
  const { include, useEntityIdFromInclude } = modelOptions;
205
- const modelType = instance.constructor.name;
206
- const identifiers = (0, scopeAttributes_1.default)(instance, scopeAttributes);
207
- // Step 1: Handle custom fields default values and required fields
208
201
  const where = {
209
202
  modelType,
210
203
  disabled: false,
@@ -215,7 +208,32 @@ const beforeCreate = (scopeAttributes, modelOptions = {}, sadotOptions = { useCu
215
208
  transaction: options.transaction,
216
209
  include: include?.(identifiers),
217
210
  });
218
- const requiredFieldsNames = Array.from(new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)));
211
+ return fieldDefinitions;
212
+ };
213
+ const formatDates = (fieldDefinitions = [], instance) => {
214
+ fieldDefinitions.forEach((fieldDefinition) => {
215
+ const { fieldType, name } = fieldDefinition;
216
+ if ([constants_1.CustomFieldDefinitionType.DATE, constants_1.CustomFieldDefinitionType.DATETIME].includes(fieldType)) {
217
+ const value = instance.customFields?.[name];
218
+ if (value) {
219
+ // eslint-disable-next-line no-param-reassign
220
+ instance.customFields[name] = new Date(value).toISOString();
221
+ }
222
+ }
223
+ });
224
+ };
225
+ /**
226
+ * Hook to handle validation and custom fields during creation
227
+ */
228
+ const beforeCreate = (scopeAttributes, modelOptions = {}, sadotOptions = { useCustomFieldsEntries: false }) => async (instance, options) => {
229
+ logger_1.default.debug('sadot - before create hook');
230
+ const { fields } = options;
231
+ const modelType = instance.constructor.name;
232
+ const identifiers = (0, scopeAttributes_1.default)(instance, scopeAttributes);
233
+ // Step 1: Handle custom fields default values and required fields
234
+ const fieldDefinitions = await getFieldDefinitions({
235
+ modelType, modelOptions, identifiers, options
236
+ });
219
237
  // Apply default values
220
238
  const fieldsWithDefaultValue = fieldDefinitions.filter((def) => ![null, undefined].includes(def.defaultValue));
221
239
  if (fieldsWithDefaultValue.length) {
@@ -229,6 +247,7 @@ const beforeCreate = (scopeAttributes, modelOptions = {}, sadotOptions = { useCu
229
247
  });
230
248
  }
231
249
  // Check for required fields
250
+ const requiredFieldsNames = Array.from(new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)));
232
251
  const { customFields } = instance;
233
252
  const fieldsNames = Object.keys(customFields ?? {});
234
253
  const missingFields = requiredFieldsNames.filter((name) => !fieldsNames.includes(name));
@@ -237,6 +256,8 @@ const beforeCreate = (scopeAttributes, modelOptions = {}, sadotOptions = { useCu
237
256
  }
238
257
  // Step 2: Validate the model data (including custom fields)
239
258
  await validateModel(instance, options, scopeAttributes, true);
259
+ // format date and datetime fields
260
+ formatDates(fieldDefinitions, instance);
240
261
  // Step 3: Save custom field values if they exist
241
262
  const customFieldsIdx = fields.indexOf('customFields');
242
263
  if (customFieldsIdx === -1 || !customFields || !Object.keys(customFields).length) {
@@ -268,8 +289,13 @@ const beforeUpdate = (scopeAttributes, modelOptions = {}, sadotOptions = { useCu
268
289
  const { fields } = options;
269
290
  const modelType = instance.constructor.name;
270
291
  const identifiers = (0, scopeAttributes_1.default)(instance, scopeAttributes);
292
+ const fieldDefinitions = await getFieldDefinitions({
293
+ modelType, modelOptions, identifiers, options
294
+ });
271
295
  // Step 1: Validate the model data (including custom fields)
272
296
  await validateModel(instance, options, scopeAttributes, false);
297
+ // format date and datetime fields
298
+ formatDates(fieldDefinitions, instance);
273
299
  // Step 2: Update custom field values if they exist
274
300
  const customFieldsIdx = fields.indexOf('customFields');
275
301
  if (customFieldsIdx > -1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/sadot",
3
- "version": "0.13.1",
3
+ "version": "0.13.2-beta.1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -8,6 +8,7 @@
8
8
  "build": "rm -rf dist && tsc",
9
9
  "linter": "eslint .",
10
10
  "test": "jest --runInBand",
11
+ "test-debug": "node --inspect-brk node_modules/.bin/jest --testTimeout=10000000",
11
12
  "coverage": "jest --coverage --runInBand",
12
13
  "build-to-local-repo": "node --run build && cp -r dist/* ../$REPO/node_modules/$npm_package_name/dist",
13
14
  "dev": "nodemon",
@@ -1,14 +1,17 @@
1
1
  import type { WhereOptions } from 'sequelize';
2
2
  import Ajv from 'ajv';
3
+ import Joi from 'joi';
3
4
  import addFormats from 'ajv-formats';
4
5
  import { BadRequest } from '@autofleet/errors';
5
6
  import logger from '../utils/logger';
6
7
  import * as ValidatorRepo from '../repository/validator';
7
8
  import * as DefinitionRepo from '../repository/definition';
8
- import { MissingRequiredCustomFieldError } from '../errors';
9
+ import { InvalidValueError, MissingRequiredCustomFieldError } from '../errors';
9
10
  import type { CustomFieldOptions, ModelOptions } from '../types';
10
11
  import applyScopeToInstance from '../utils/scopeAttributes';
11
12
  import updateInstanceValues from './utils/updateInstanceValues';
13
+ import { CustomFieldDefinitionType } from '../utils/constants';
14
+ import type { CustomFieldDefinition } from '../models';
12
15
 
13
16
  // Initialize Ajv with relaxed settings to avoid warnings
14
17
  const ajv = new Ajv({
@@ -218,25 +221,18 @@ const validateModel = async (
218
221
  }
219
222
  };
220
223
 
221
- /**
222
- * Hook to handle validation and custom fields during creation
223
- */
224
- export const beforeCreate = (
225
- scopeAttributes: string[],
226
- modelOptions: ModelOptions = {},
227
- sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false },
228
- ) => async (
229
- instance,
224
+ const getFieldDefinitions = async ({
225
+ modelType,
226
+ modelOptions,
227
+ identifiers,
230
228
  options,
231
- ): Promise<void> => {
232
- logger.debug('sadot - before create hook');
233
- const { fields } = options;
229
+ }: {
230
+ modelType: any,
231
+ modelOptions: ModelOptions,
232
+ identifiers: any[],
233
+ options: any
234
+ }) => {
234
235
  const { include, useEntityIdFromInclude } = modelOptions;
235
- const modelType = instance.constructor.name;
236
-
237
- const identifiers = applyScopeToInstance(instance, scopeAttributes);
238
-
239
- // Step 1: Handle custom fields default values and required fields
240
236
  const where: WhereOptions = {
241
237
  modelType,
242
238
  disabled: false,
@@ -248,64 +244,33 @@ export const beforeCreate = (
248
244
  transaction: options.transaction,
249
245
  include: include?.(identifiers),
250
246
  });
247
+ return fieldDefinitions;
248
+ };
251
249
 
252
- const requiredFieldsNames = Array.from(
253
- new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)),
254
- );
255
-
256
- // Apply default values
257
- const fieldsWithDefaultValue = fieldDefinitions.filter((def) => ![null, undefined].includes(def.defaultValue));
258
- if (fieldsWithDefaultValue.length) {
259
- // eslint-disable-next-line no-param-reassign
260
- instance.customFields ||= {};
261
- fieldsWithDefaultValue
262
- .filter((def) => (instance.customFields?.[def.name] === undefined))
263
- .forEach(({ name, defaultValue }) => {
250
+ const formatDates = (fieldDefinitions: CustomFieldDefinition[], instance: any) => {
251
+ (fieldDefinitions || []).forEach((fieldDefinition) => {
252
+ const { fieldType, name } = fieldDefinition;
253
+ if ([CustomFieldDefinitionType.DATE, CustomFieldDefinitionType.DATETIME].includes(fieldType)) {
254
+ const value = instance.customFields?.[name];
255
+ if (value) {
256
+ const { value: joiValue, error: validationError } = Joi.date().validate(value);
257
+ if (validationError) {
258
+ throw new InvalidValueError(value, name, validationError);
259
+ }
264
260
  // eslint-disable-next-line no-param-reassign
265
- instance.customFields[name] = defaultValue;
266
- });
267
- }
268
-
269
- // Check for required fields
270
- const { customFields } = instance;
271
- const fieldsNames = Object.keys(customFields ?? {});
272
- const missingFields = requiredFieldsNames.filter((name) => !fieldsNames.includes(name));
273
- if (missingFields?.length) {
274
- throw new MissingRequiredCustomFieldError(missingFields);
275
- }
276
-
277
- // Step 2: Validate the model data (including custom fields)
278
- await validateModel(instance, options, scopeAttributes, true);
279
-
280
- // Step 3: Save custom field values if they exist
281
- const customFieldsIdx = fields.indexOf('customFields');
282
- if (customFieldsIdx === -1 || !customFields || !Object.keys(customFields).length) {
283
- // No custom fields to update
284
- return;
285
- }
286
-
287
- // Save custom field values
288
- await updateInstanceValues({
289
- modelId: instance.id,
290
- modelType,
291
- identifiers,
292
- customFields,
293
- options: {
294
- useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,
295
- transaction: options.transaction,
296
- modelOptions,
297
- },
261
+ const typeofjoi = typeof joiValue;
262
+ logger.info('sadot - formatting date', { name, value, joiValue, type: typeof joiValue });
263
+ instance.customFields[name] = joiValue.toISOString();
264
+ new Date().toString
265
+ }
266
+ }
298
267
  });
299
-
300
- // Remove customFields from fields array after handling
301
- // eslint-disable-next-line no-param-reassign
302
- fields.splice(customFieldsIdx, 1);
303
268
  };
304
269
 
305
270
  /**
306
- * Hook to handle validation and custom fields during update
271
+ * Hook to handle validation and custom fields during creation
307
272
  */
308
- export const beforeUpdate = (
273
+ export const beforeCreate = (
309
274
  scopeAttributes: string[],
310
275
  modelOptions: ModelOptions = {},
311
276
  sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false },
@@ -313,20 +278,52 @@ export const beforeUpdate = (
313
278
  instance,
314
279
  options,
315
280
  ): Promise<void> => {
316
- logger.debug('sadot - before update hook');
317
- const { fields } = options;
318
- const modelType = instance.constructor.name;
319
- const identifiers = applyScopeToInstance(instance, scopeAttributes);
281
+ logger.debug('sadot - before create hook');
282
+ const { fields } = options;
283
+ const modelType = instance.constructor.name;
284
+
285
+ const identifiers = applyScopeToInstance(instance, scopeAttributes);
286
+
287
+ // Step 1: Handle custom fields default values and required fields
288
+
289
+ const fieldDefinitions = await getFieldDefinitions({
290
+ modelType, modelOptions, identifiers, options,
291
+ });
320
292
 
321
- // Step 1: Validate the model data (including custom fields)
322
- await validateModel(instance, options, scopeAttributes, false);
293
+ // Apply default values
294
+ const fieldsWithDefaultValue = fieldDefinitions.filter((def) => ![null, undefined].includes(def.defaultValue));
295
+ if (fieldsWithDefaultValue.length) {
296
+ // eslint-disable-next-line no-param-reassign
297
+ instance.customFields ||= {};
298
+ fieldsWithDefaultValue
299
+ .filter((def) => (instance.customFields?.[def.name] === undefined))
300
+ .forEach(({ name, defaultValue }) => {
301
+ // eslint-disable-next-line no-param-reassign
302
+ instance.customFields[name] = defaultValue;
303
+ });
304
+ }
323
305
 
324
- // Step 2: Update custom field values if they exist
325
- const customFieldsIdx = fields.indexOf('customFields');
326
- if (customFieldsIdx > -1) {
306
+ // Check for required fields
307
+ const requiredFieldsNames = Array.from(
308
+ new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)),
309
+ );
327
310
  const { customFields } = instance;
311
+ const fieldsNames = Object.keys(customFields ?? {});
312
+ const missingFields = requiredFieldsNames.filter((name) => !fieldsNames.includes(name));
313
+ if (missingFields?.length) {
314
+ throw new MissingRequiredCustomFieldError(missingFields);
315
+ }
316
+
317
+ // Step 2: Validate the model data (including custom fields)
318
+ await validateModel(instance, options, scopeAttributes, true);
319
+
320
+ // format date and datetime fields
321
+ formatDates(fieldDefinitions, instance);
328
322
 
329
- if (!Object.keys(customFields).length) {
323
+ // Step 3: Save custom field values if they exist
324
+ const customFieldsIdx = fields.indexOf('customFields');
325
+ if (customFieldsIdx === -1 || !customFields || !Object.keys(customFields).length) {
326
+ // No custom fields to update
330
327
  return;
331
328
  }
332
329
 
@@ -346,8 +343,61 @@ export const beforeUpdate = (
346
343
  // Remove customFields from fields array after handling
347
344
  // eslint-disable-next-line no-param-reassign
348
345
  fields.splice(customFieldsIdx, 1);
349
- }
350
- };
346
+ };
347
+
348
+ /**
349
+ * Hook to handle validation and custom fields during update
350
+ */
351
+ export const beforeUpdate = (
352
+ scopeAttributes: string[],
353
+ modelOptions: ModelOptions = {},
354
+ sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false },
355
+ ) => async (
356
+ instance,
357
+ options,
358
+ ): Promise<void> => {
359
+ logger.debug('sadot - before update hook');
360
+ const { fields } = options;
361
+ const modelType = instance.constructor.name;
362
+ const identifiers = applyScopeToInstance(instance, scopeAttributes);
363
+
364
+ const fieldDefinitions = await getFieldDefinitions({
365
+ modelType, modelOptions, identifiers, options,
366
+ });
367
+
368
+ // Step 1: Validate the model data (including custom fields)
369
+ await validateModel(instance, options, scopeAttributes, false);
370
+
371
+ // format date and datetime fields
372
+ formatDates(fieldDefinitions, instance);
373
+
374
+ // Step 2: Update custom field values if they exist
375
+ const customFieldsIdx = fields.indexOf('customFields');
376
+ if (customFieldsIdx > -1) {
377
+ const { customFields } = instance;
378
+
379
+ if (!Object.keys(customFields).length) {
380
+ return;
381
+ }
382
+
383
+ // Save custom field values
384
+ await updateInstanceValues({
385
+ modelId: instance.id,
386
+ modelType,
387
+ identifiers,
388
+ customFields,
389
+ options: {
390
+ useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,
391
+ transaction: options.transaction,
392
+ modelOptions,
393
+ },
394
+ });
395
+
396
+ // Remove customFields from fields array after handling
397
+ // eslint-disable-next-line no-param-reassign
398
+ fields.splice(customFieldsIdx, 1);
399
+ }
400
+ };
351
401
 
352
402
  /**
353
403
  * Hook to enable individual hooks for bulk create operations
@@ -23,7 +23,7 @@ const productionModels: ProductionModel[] = [CustomFieldDefinition, CustomFieldV
23
23
  const testModels = [TestModel, AssociatedTestModel, ContextAwareTestModel, ContextTestModel];
24
24
 
25
25
  const SADOT_MIGRATION_PREFIX = 'sadot-migration';
26
- const SCHEMA_VERSION = 'fb0fa867-1241-4816-b08d-5ed9060c7ae5';
26
+ const SCHEMA_VERSION = '49c9dd1d-b1cc-445b-a911-fd349d783110';
27
27
 
28
28
  const initTables = async (
29
29
  sequelize: Sequelize,