@autofleet/sadot 0.6.2 → 0.6.3-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.
Files changed (69) hide show
  1. package/dist/api/v1/definition/validations.d.ts +2 -2
  2. package/dist/api/v1/definition/validations.js +2 -2
  3. package/dist/api/v1/errors.js +1 -1
  4. package/dist/hooks/create.d.ts +2 -1
  5. package/dist/hooks/create.js +8 -4
  6. package/dist/hooks/enrich.d.ts +3 -1
  7. package/dist/hooks/enrich.js +22 -4
  8. package/dist/hooks/update.d.ts +2 -1
  9. package/dist/hooks/update.js +4 -3
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.js +5 -1
  12. package/dist/models/index.d.ts +3 -1
  13. package/dist/models/index.js +8 -2
  14. package/dist/models/tests/contextAwareModels/ContextAwareTestModel.d.ts +10 -0
  15. package/dist/models/tests/contextAwareModels/ContextAwareTestModel.js +55 -0
  16. package/dist/models/tests/contextAwareModels/ContextTestModel.d.ts +13 -0
  17. package/dist/models/tests/contextAwareModels/ContextTestModel.js +47 -0
  18. package/dist/repository/definition.d.ts +7 -4
  19. package/dist/repository/definition.js +27 -15
  20. package/dist/repository/value.d.ts +5 -1
  21. package/dist/repository/value.js +13 -3
  22. package/dist/scopes/filter.d.ts +9 -0
  23. package/dist/scopes/filter.js +37 -5
  24. package/dist/tests/functional/searching/index.d.ts +8 -0
  25. package/dist/tests/functional/searching/index.js +44 -0
  26. package/dist/tests/helpers/database-config.d.ts +1 -0
  27. package/dist/tests/helpers/database-config.js +1 -0
  28. package/dist/tests/helpers/index.js +2 -0
  29. package/dist/tests/mocks/definition.mock.d.ts +6 -0
  30. package/dist/tests/mocks/definition.mock.js +7 -1
  31. package/dist/tests/mocks/testModel.d.ts +1 -1
  32. package/dist/types/index.d.ts +20 -2
  33. package/dist/utils/constants/index.d.ts +4 -1
  34. package/dist/utils/constants/index.js +3 -1
  35. package/dist/utils/helpers/index.d.ts +25 -0
  36. package/dist/utils/helpers/index.js +34 -0
  37. package/dist/utils/init.d.ts +5 -4
  38. package/dist/utils/init.js +19 -9
  39. package/dist/utils/scopeAttributes.d.ts +2 -0
  40. package/dist/utils/scopeAttributes.js +11 -0
  41. package/dist/utils/validations/custom-fields.d.ts +2 -1
  42. package/dist/utils/validations/custom-fields.js +1 -1
  43. package/dist/utils/validations/type.js +1 -1
  44. package/package.json +8 -8
  45. package/src/api/v1/definition/index.ts +1 -1
  46. package/src/api/v1/definition/validations.ts +0 -13
  47. package/src/hooks/create.ts +1 -1
  48. package/src/hooks/enrich.ts +4 -4
  49. package/src/hooks/update.ts +1 -1
  50. package/src/index.ts +2 -2
  51. package/src/models/CustomFieldDefinition.ts +5 -9
  52. package/src/models/CustomFieldValue.ts +5 -1
  53. package/src/repository/definition.ts +5 -2
  54. package/src/repository/value.ts +2 -2
  55. package/src/scopes/filter.ts +26 -1
  56. package/src/tests/functional/searching/index.ts +1 -1
  57. package/src/tests/mocks/definition.mock.ts +8 -6
  58. package/src/types/index.ts +2 -2
  59. package/src/utils/helpers/index.ts +4 -4
  60. package/src/utils/init.ts +4 -1
  61. package/src/utils/validations/{schema/custom-fields.ts → custom-fields.ts} +1 -0
  62. package/src/utils/validations/custom.ts +39 -0
  63. package/src/utils/validations/index.ts +15 -18
  64. package/src/utils/validations/type.ts +32 -5
  65. package/src/utils/validations/validators.ts +34 -0
  66. package/.env +0 -3
  67. package/src/utils/validations/validators/index.ts +0 -29
  68. package/src/utils/validations/validators/select.validator.ts +0 -13
  69. package/src/utils/validations/validators/status.validator.ts +0 -20
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.scopeName = exports.customFieldsFilterScope = void 0;
3
+ exports.customFieldsSortScope = exports.scopeName = exports.customFieldsFilterScope = void 0;
4
4
  /* eslint-disable import/prefer-default-export */
5
5
  const sequelize_1 = require("sequelize");
6
6
  const sequelize_typescript_1 = require("sequelize-typescript");
7
- const constants_1 = require("../utils/constants");
7
+ const common_types_1 = require("@autofleet/common-types");
8
+ const { CUSTOM_FIELDS_FILTER_SCOPE } = common_types_1.customFields;
8
9
  /**
9
10
  * A Sequelize scope for filtering models by custom fields.
10
11
  * This scope builds a WHERE clause to be applied on the main query.
@@ -13,16 +14,27 @@ const constants_1 = require("../utils/constants");
13
14
  * @returns {Function} - A function that takes conditions and returns the Sequelize options object.
14
15
  */
15
16
  const customFieldsFilterScope = (name) => (conditions) => {
17
+ if (!conditions || Object.keys(conditions).length === 0) {
18
+ return {};
19
+ }
16
20
  // Build the WHERE clause for custom field filtering
17
- const customFieldConditions = Object.entries(conditions)
21
+ const conditionsStrings = Object.entries(conditions)
18
22
  .map(([key, value]) => {
19
23
  if (Array.isArray(value)) {
24
+ if (value.length === 0) {
25
+ // if empty array, the condition is ignored
26
+ return false;
27
+ }
20
28
  const values = value.map((v) => `'${v}'`).join(',');
21
29
  return `custom_fields->>'${key}' IN (${values})`;
22
30
  }
23
31
  return `custom_fields->>'${key}' = '${value}'`;
24
32
  })
25
- .join(' AND ');
33
+ .filter(Boolean);
34
+ if (conditionsStrings.length === 0) {
35
+ return {};
36
+ }
37
+ const customFieldConditions = conditionsStrings.join(' AND ');
26
38
  const subQuery = `${'SELECT model_id FROM ('
27
39
  + 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
28
40
  + 'FROM custom_field_values AS cv '
@@ -40,4 +52,24 @@ const customFieldsFilterScope = (name) => (conditions) => {
40
52
  };
41
53
  };
42
54
  exports.customFieldsFilterScope = customFieldsFilterScope;
43
- exports.scopeName = constants_1.CUSTOM_FIELDS_FILTER_SCOPE;
55
+ exports.scopeName = CUSTOM_FIELDS_FILTER_SCOPE;
56
+ const customFieldsSortScope = (name) => (sort) => {
57
+ if (!sort || sort.length === 0) {
58
+ return {};
59
+ }
60
+ const incluedes = Object.entries(sort).map(([key]) => sequelize_typescript_1.Sequelize.literal(`(select CustomFieldAggregation.custom_fields->>'${key}'
61
+ from (SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields
62
+ FROM custom_field_values AS cv
63
+ INNER JOIN custom_field_definitions AS cd
64
+ ON cv.custom_field_definition_id = cd.id AND cd.model_type = 'Task'
65
+ where cv.model_id = "${name}"."id"
66
+ GROUP BY cv.model_id) AS CustomFieldAggregation) as "customFields.${key}"`));
67
+ const orders = Object.entries(sort).map(([key, value]) => sequelize_typescript_1.Sequelize.literal(`"customFields.${key}" ${value}`));
68
+ return {
69
+ attributes: {
70
+ include: incluedes,
71
+ },
72
+ order: orders,
73
+ };
74
+ };
75
+ exports.customFieldsSortScope = customFieldsSortScope;
@@ -0,0 +1,8 @@
1
+ interface CustomFieldsSearchTestFlowInput {
2
+ fieldType: string;
3
+ fieldValue: string | number;
4
+ searchTerm: string;
5
+ expectedNumberOfQueryResults: number;
6
+ }
7
+ declare const customFieldsSearchTestFlow: ({ fieldType, fieldValue, searchTerm, expectedNumberOfQueryResults, }: CustomFieldsSearchTestFlowInput) => Promise<void>;
8
+ export default customFieldsSearchTestFlow;
@@ -0,0 +1,44 @@
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 index_1 = require("../../../index");
27
+ const DefinitionRepo = __importStar(require("../../../repository/definition"));
28
+ const definition_mock_1 = require("../../mocks/definition.mock");
29
+ const testModel_1 = require("../../mocks/testModel");
30
+ const models_1 = require("../../../models");
31
+ const customFieldsSearchTestFlow = async ({ fieldType, fieldValue, searchTerm, expectedNumberOfQueryResults, }) => {
32
+ const definition = (0, definition_mock_1.createDefinition)({ fieldType, name: 'coolDefinition' });
33
+ await DefinitionRepo.create({ ...definition });
34
+ const [testModel1] = await (0, testModel_1.createTestModels)(definition.entityId, 2);
35
+ await testModel1.update({ customFields: { [definition.name]: fieldValue } });
36
+ const models = await models_1.TestModel.findAndCountAll({
37
+ ...(0, index_1.generateCustomFieldSearchQueryPayload)(searchTerm, models_1.TestModel, definition.entityId),
38
+ });
39
+ expect(models.count).toBe(expectedNumberOfQueryResults);
40
+ if (expectedNumberOfQueryResults > 0) {
41
+ expect(models.rows[0].dataValues.id).toBe(testModel1.id);
42
+ }
43
+ };
44
+ exports.default = customFieldsSearchTestFlow;
@@ -4,6 +4,7 @@ declare const _default: {
4
4
  password: string;
5
5
  database: string;
6
6
  host: string;
7
+ port: string | number;
7
8
  dialect: string;
8
9
  define: {
9
10
  underscored: boolean;
@@ -6,6 +6,7 @@ exports.default = {
6
6
  password: process.env.DB_PASSWORD || null,
7
7
  database: process.env.DB_NAME || 'sadot_package_test',
8
8
  host: process.env.DB_HOST || '127.0.0.1',
9
+ port: process.env.DB_PORT || 5432,
9
10
  dialect: process.env.DB_TYPE || 'postgres',
10
11
  define: {
11
12
  underscored: true,
@@ -7,6 +7,8 @@ const cleanup = async () => {
7
7
  if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development') {
8
8
  await models_1.CustomFieldDefinition.unscoped().destroy({ where: {} });
9
9
  await models_1.TestModel.destroy({ where: {} });
10
+ await models_1.ContextAwareTestModel.destroy({ where: {} });
11
+ await models_1.ContextTestModel.destroy({ where: {} });
10
12
  }
11
13
  };
12
14
  exports.cleanup = cleanup;
@@ -1,4 +1,10 @@
1
1
  import { CreateCustomFieldDefinition, CustomFieldDefinitionDTO } from '../../types/definition';
2
+ export declare const contextAwareFieldDefinition: {
3
+ name: string;
4
+ modelType: string;
5
+ fieldType: string;
6
+ entityType: string;
7
+ };
2
8
  export declare const coolFieldDefinition: CreateCustomFieldDefinition;
3
9
  export declare const coolFieldDefinition2: {
4
10
  name: string;
@@ -1,7 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createDefinitions = exports.createDefinition = exports.rangeField = exports.enumField = exports.booleanField = exports.coolFieldDefinition3 = exports.coolFieldDefinition2 = exports.coolFieldDefinition = void 0;
3
+ exports.createDefinitions = exports.createDefinition = exports.rangeField = exports.enumField = exports.booleanField = exports.coolFieldDefinition3 = exports.coolFieldDefinition2 = exports.coolFieldDefinition = exports.contextAwareFieldDefinition = void 0;
4
4
  const uuid_1 = require("uuid");
5
+ exports.contextAwareFieldDefinition = {
6
+ name: 'cool field',
7
+ modelType: 'ContextAwareTestModel',
8
+ fieldType: 'number',
9
+ entityType: 'fleetId',
10
+ };
5
11
  exports.coolFieldDefinition = {
6
12
  name: 'cool field',
7
13
  modelType: 'TestModel',
@@ -3,7 +3,7 @@ export declare const createTestModel: (payload?: {}) => Promise<TestModel>;
3
3
  export declare const createTestModels: (fleetId: any, total: number) => Promise<TestModel[]>;
4
4
  export declare const upsertTestModel: (payload: any) => Promise<[TestModel, boolean]>;
5
5
  export declare const destroyTestModels: () => Promise<number>;
6
- export declare const getTestModel: (id: string, options?: {}) => Promise<TestModel>;
6
+ export declare const getTestModel: (id: string, options?: {}) => Promise<TestModel | null>;
7
7
  export declare const getSomeTestModels: (options?: {
8
8
  limit: number;
9
9
  }) => Promise<TestModel[]>;
@@ -1,13 +1,31 @@
1
+ import { IncludeOptions } from 'sequelize';
2
+ import { ModelCtor } from 'sequelize-typescript';
1
3
  export type ModelFetcher = (name: string) => any;
2
4
  export type ModelOptions = {
5
+ /**
6
+ * Include options for the model
7
+ */
8
+ include?: (entityIds: string[]) => IncludeOptions[];
9
+ /**
10
+ * Custom association for the model
11
+ * @param model - The model to associate with
12
+ */
13
+ customAssociation?: (model: ModelCtor) => void;
14
+ /**
15
+ * Whether to use the entity id from the instance per scope attribute
16
+ */
17
+ useEntityIdFromInclude?: boolean;
18
+ };
19
+ export type Models = {
3
20
  name: string;
4
- scopeAttributes: string[];
21
+ scopeAttributes: any[];
22
+ modelOptions?: ModelOptions;
5
23
  creationWebhookHandler?: (instance: any) => any;
6
24
  updateWebhookHandler?: (instance: any) => any;
7
25
  deletionWebhookHandler?: (instance: any) => any;
8
26
  };
9
27
  export type CustomFieldOptions = {
10
- models: ModelOptions[];
28
+ models: Models[];
11
29
  databaseConfig: any;
12
30
  getUser: () => any;
13
31
  };
@@ -1,2 +1,5 @@
1
+ declare const CUSTOM_FIELDS_FILTER_SCOPE: string;
1
2
  export declare const supportedEntities: string[];
2
- export declare const CUSTOM_FIELDS_FILTER_SCOPE = "filterByCustomFields";
3
+ export {
4
+ /** @deprecated Use the value from `@autofleet/common-types` instead */
5
+ CUSTOM_FIELDS_FILTER_SCOPE, };
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CUSTOM_FIELDS_FILTER_SCOPE = exports.supportedEntities = void 0;
4
+ const common_types_1 = require("@autofleet/common-types");
5
+ const { CUSTOM_FIELDS_FILTER_SCOPE } = common_types_1.customFields;
6
+ exports.CUSTOM_FIELDS_FILTER_SCOPE = CUSTOM_FIELDS_FILTER_SCOPE;
4
7
  // eslint-disable-next-line import/prefer-default-export
5
8
  exports.supportedEntities = ['businessModelId', 'fleetId', 'demandSourceId'];
6
- exports.CUSTOM_FIELDS_FILTER_SCOPE = 'filterByCustomFields';
@@ -0,0 +1,25 @@
1
+ import { WhereOptions, BindOrReplacements } from 'sequelize';
2
+ import { ModelStatic } from 'sequelize-typescript';
3
+ import { CustomFieldDefinitionType } from '../validations/type';
4
+ /**
5
+ * Builds a WHERE clause and replacements for free-text search by custom fields.
6
+ *
7
+ * This function constructs a WHERE clause and replacement bindings that allow searching
8
+ * for a given term within custom fields associated with a specific model type and entity ID.
9
+ * The WHERE clause and replacements are designed to be added to the main query.
10
+ *
11
+ * @param {string} searchTerm - The term to search for within custom fields.
12
+ * @param {ModelStatic} model - The Sequelize model representing the entity type to search for.
13
+ * @param {string} entityId - The entity ID to filter the custom fields by.
14
+ * @param {CustomFieldDefinitionType[]} excludedCustomFieldsTypes - An array of custom field types
15
+ * to exclude from the search
16
+ *
17
+ * @returns {CustomFieldsSearchPayload} - An object containing the WHERE clause and replacements
18
+ * for Sequelize.
19
+ */
20
+ interface CustomFieldsSearchPayload {
21
+ where: WhereOptions;
22
+ replacements: BindOrReplacements;
23
+ }
24
+ export declare const generateCustomFieldSearchQueryPayload: (searchTerm: string, model: ModelStatic, entityId: string, customFieldsTypesToExclude?: CustomFieldDefinitionType[]) => CustomFieldsSearchPayload;
25
+ export {};
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateCustomFieldSearchQueryPayload = void 0;
4
+ /* eslint-disable import/prefer-default-export */
5
+ const sequelize_1 = require("sequelize");
6
+ const sequelize_typescript_1 = require("sequelize-typescript");
7
+ const type_1 = require("../validations/type");
8
+ const generateCustomFieldSearchQueryPayload = (searchTerm, model, entityId, customFieldsTypesToExclude = [
9
+ type_1.CustomFieldDefinitionType.DATETIME,
10
+ type_1.CustomFieldDefinitionType.DATE,
11
+ ]) => {
12
+ const excludedTypesString = customFieldsTypesToExclude.map((type) => `'${type}'`).join(',');
13
+ const subQuery = 'EXISTS ('
14
+ + ' SELECT 1'
15
+ + ' FROM "custom_field_values" AS "cv"'
16
+ + ' INNER JOIN custom_field_definitions AS cd '
17
+ + ` ON cd.entity_id = '${entityId}'`
18
+ + ' AND cv.custom_field_definition_id = cd.id'
19
+ + ` AND cd.model_type = '${model.name}'`
20
+ + ` ${excludedTypesString ? `AND cd.field_type NOT IN (${excludedTypesString})` : ''}`
21
+ + ' WHERE'
22
+ + ' "cv"."deleted_at" IS NULL'
23
+ + ` AND "cv"."model_id" = "${model.name}"."id"`
24
+ + ' AND CAST("cv"."value" AS TEXT) ILIKE :searchTerm)';
25
+ return {
26
+ where: {
27
+ [sequelize_1.Op.or]: [
28
+ sequelize_typescript_1.Sequelize.where(sequelize_typescript_1.Sequelize.literal(subQuery), true),
29
+ ],
30
+ },
31
+ replacements: { searchTerm: `%${searchTerm}%` },
32
+ };
33
+ };
34
+ exports.generateCustomFieldSearchQueryPayload = generateCustomFieldSearchQueryPayload;
@@ -1,4 +1,5 @@
1
- import type { ModelFetcher, ModelOptions } from '../types';
2
- export declare const addHooks: (models: ModelOptions[], getModel: ModelFetcher) => void;
3
- export declare const removeHooks: (models: ModelOptions[], getModel: ModelFetcher) => void;
4
- export declare const addScopes: (models: ModelOptions[], getModel: ModelFetcher) => void;
1
+ import type { ModelFetcher, Models } from '../types';
2
+ export declare const addHooks: (models: Models[], getModel: ModelFetcher) => void;
3
+ export declare const removeHooks: (models: Models[], getModel: ModelFetcher) => void;
4
+ export declare const addScopes: (models: Models[], getModel: ModelFetcher) => void;
5
+ export declare const applyCustomAssociation: (models: Models[]) => void;
@@ -3,15 +3,18 @@ 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.addScopes = exports.removeHooks = exports.addHooks = void 0;
6
+ exports.applyCustomAssociation = exports.addScopes = exports.removeHooks = exports.addHooks = void 0;
7
7
  const sequelize_1 = require("sequelize");
8
+ const common_types_1 = require("@autofleet/common-types");
9
+ const custom_fields_1 = require("@autofleet/common-types/lib/custom-fields");
8
10
  const models_1 = require("../models");
9
11
  const hooks_1 = require("../hooks");
10
12
  const scopes_1 = require("../scopes");
11
13
  const logger_1 = __importDefault(require("./logger"));
12
- const constants_1 = require("./constants");
14
+ const filter_1 = require("../scopes/filter");
15
+ const { CUSTOM_FIELDS_FILTER_SCOPE } = common_types_1.customFields;
13
16
  const addHooks = (models, getModel) => {
14
- models.forEach(async ({ name, scopeAttributes }) => {
17
+ models.forEach(async ({ name, scopeAttributes, modelOptions, }) => {
15
18
  try {
16
19
  const model = getModel(name);
17
20
  if (!model) {
@@ -30,11 +33,11 @@ const addHooks = (models, getModel) => {
30
33
  model.addHook('beforeFind', 'sadot-beforeFind', (0, hooks_1.beforeFind)(scopeAttributes));
31
34
  model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', hooks_1.beforeBulkCreate);
32
35
  model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', hooks_1.beforeBulkUpdate);
33
- model.addHook('beforeCreate', 'sadot-beforeCreate', (0, hooks_1.beforeCreate)(scopeAttributes));
34
- model.addHook('beforeUpdate', 'sadot-beforeUpdate', (0, hooks_1.beforeUpdate)(scopeAttributes));
35
- model.addHook('afterFind', 'sadot-afterFind', (0, hooks_1.enrichResults)(name, scopeAttributes));
36
- model.addHook('afterUpdate', 'sadot-afterUpdate', (0, hooks_1.enrichResults)(name, scopeAttributes));
37
- model.addHook('afterCreate', 'sadot-afterCreate', (0, hooks_1.enrichResults)(name, scopeAttributes));
36
+ model.addHook('beforeCreate', 'sadot-beforeCreate', (0, hooks_1.beforeCreate)(scopeAttributes, modelOptions));
37
+ model.addHook('beforeUpdate', 'sadot-beforeUpdate', (0, hooks_1.beforeUpdate)(scopeAttributes, modelOptions));
38
+ model.addHook('afterFind', 'sadot-afterFind', (0, hooks_1.enrichResults)(name, scopeAttributes, 'afterFind', modelOptions));
39
+ model.addHook('afterUpdate', 'sadot-afterUpdate', (0, hooks_1.enrichResults)(name, scopeAttributes, null, modelOptions));
40
+ model.addHook('afterCreate', 'sadot-afterCreate', (0, hooks_1.enrichResults)(name, scopeAttributes, null, modelOptions));
38
41
  }
39
42
  catch (e) {
40
43
  logger_1.default.error(`Could not add custom fields hook to model ${name}. `, e);
@@ -89,7 +92,8 @@ const addScopes = (models, getModel) => {
89
92
  // Necessary associations for the filtering scope
90
93
  addAssociations(model, name);
91
94
  // Add filter scope
92
- model.addScope(constants_1.CUSTOM_FIELDS_FILTER_SCOPE, (0, scopes_1.customFieldsFilterScope)(name));
95
+ model.addScope(CUSTOM_FIELDS_FILTER_SCOPE, (0, scopes_1.customFieldsFilterScope)(name));
96
+ model.addScope(custom_fields_1.CUSTOM_FIELDS_SORT_SCOPE, (0, filter_1.customFieldsSortScope)(name));
93
97
  }
94
98
  catch (e) {
95
99
  logger_1.default.error(`Could not add custom fields scopes to model ${name}. `, e);
@@ -97,3 +101,9 @@ const addScopes = (models, getModel) => {
97
101
  });
98
102
  };
99
103
  exports.addScopes = addScopes;
104
+ const applyCustomAssociation = (models) => {
105
+ models.forEach(({ modelOptions }) => {
106
+ modelOptions?.customAssociation?.(models_1.CustomFieldDefinition);
107
+ });
108
+ };
109
+ exports.applyCustomAssociation = applyCustomAssociation;
@@ -0,0 +1,2 @@
1
+ declare const applyScopeToInstance: (instance: any, scopeAttributes: string[]) => any[];
2
+ export default applyScopeToInstance;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const mapAttributeToInstance = (scopeAttributes, instance) => scopeAttributes.map((attr) => instance[attr]);
4
+ const applyScopeToInstance = (instance, scopeAttributes) => {
5
+ const uniqueAttributes = Array.from(new Set(scopeAttributes));
6
+ if (Array.isArray(instance)) {
7
+ return instance.flatMap((ins) => mapAttributeToInstance(uniqueAttributes, ins));
8
+ }
9
+ return mapAttributeToInstance(uniqueAttributes, instance);
10
+ };
11
+ exports.default = applyScopeToInstance;
@@ -1,2 +1,3 @@
1
- declare const CustomFieldsSchema: any;
1
+ import Joi from 'joi';
2
+ declare const CustomFieldsSchema: Joi.ObjectSchema<any>;
2
3
  export { CustomFieldsSchema };
@@ -5,6 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.CustomFieldsSchema = void 0;
7
7
  /* eslint-disable import/prefer-default-export */
8
- const joi_1 = __importDefault(require("@hapi/joi"));
8
+ const joi_1 = __importDefault(require("joi"));
9
9
  const CustomFieldsSchema = joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.any());
10
10
  exports.CustomFieldsSchema = CustomFieldsSchema;
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.CustomFieldDefinitionType = void 0;
7
- const joi_1 = __importDefault(require("@hapi/joi"));
7
+ const joi_1 = __importDefault(require("joi"));
8
8
  /**
9
9
  * Supported custom field types
10
10
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/sadot",
3
- "version": "0.6.2",
3
+ "version": "0.6.3-beta.1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -9,7 +9,7 @@
9
9
  "linter": "./node_modules/.bin/eslint .",
10
10
  "test": "jest --forceExit --runInBand",
11
11
  "coverage": "jest --coverage --forceExit --runInBand && rm -rf ./coverage",
12
- "build-to-local-repo": "npm run build && cp -r dist/* ../$REPO/node_modules/$npm_package_name/dist",
12
+ "build-to-local-repo": "npm run build && cp -r dist/* ../task-ms/node_modules/$npm_package_name/dist",
13
13
  "dev": "nodemon",
14
14
  "watch": "npm-watch build-to-local-repo"
15
15
  },
@@ -45,16 +45,16 @@
45
45
  "@types/express": "^4.17.17",
46
46
  "@types/jest": "^27.0.9",
47
47
  "@types/uuid": "^9.0.2",
48
- "@typescript-eslint/eslint-plugin": "^6.4.0",
49
- "@typescript-eslint/parser": "^6.4.0",
50
- "eslint": "^7.32.0",
48
+ "@typescript-eslint/eslint-plugin": "^4.8.1",
49
+ "@typescript-eslint/parser": "^4.33.0",
50
+ "eslint": "^7.13.0",
51
51
  "eslint-config-airbnb-typescript": "^12.0.0",
52
52
  "eslint-plugin-import": "^2.22.1",
53
- "jest": "^29.7.0",
54
- "ts-jest": "^29.1.2",
53
+ "jest": "^27.0.5",
55
54
  "npm-watch": "^0.11.0",
55
+ "ts-jest": "^27.0.3",
56
56
  "ts-node": "^8.6.2",
57
- "typescript": "^5.3.3",
57
+ "typescript": "^4.9.5",
58
58
  "typescript-eslint": "^0.0.1-alpha.0"
59
59
  },
60
60
  "author": "Autofleet",
@@ -3,7 +3,7 @@ import { Router } from 'express';
3
3
  import type { Request, Response } from 'express';
4
4
  import handleError from '../errors';
5
5
  import * as DefinitionRepo from '../../../repository/definition';
6
- import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../../../types/definition';
6
+ import { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../../../types/definition';
7
7
  import { validateCustomFieldDefinitionCreation, validateCustomFieldDefinitionUpdate } from './validations';
8
8
  import logger from '../../../utils/logger';
9
9
 
@@ -1,23 +1,10 @@
1
1
  import Joi from 'joi';
2
2
  import { CustomFieldDefinitionType } from '../../../utils/validations/type';
3
3
 
4
- /**
5
- * Schema for the validation of custom field definition
6
- * The only custom validation is for
7
- * {@link CustomFieldDefinitionType.SELECT SELECT}
8
- * and
9
- * {@link CustomFieldDefinitionType.STATUS STATUS}
10
- * field types.
11
- * The rest of the field types are validated by Joi
12
- */
13
4
  const ValidationSchema = Joi.when('fieldType', {
14
5
  is: CustomFieldDefinitionType.SELECT,
15
6
  then: Joi.array().items(Joi.string()).min(1).unique(),
16
7
  otherwise: Joi.any(),
17
- }).when('fieldType', {
18
- is: CustomFieldDefinitionType.STATUS,
19
- then: Joi.string().min(1).required(),
20
- otherwise: Joi.any(),
21
8
  });
22
9
 
23
10
  const CustomFieldDefinitionCreationSchema = Joi.object({
@@ -2,7 +2,7 @@ import logger from '../utils/logger';
2
2
  import * as ValueRepo from '../repository/value';
3
3
  import * as DefinitionRepo from '../repository/definition';
4
4
  import { MissingRequiredCustomFieldError } from '../errors';
5
- import type { ModelOptions } from '../types';
5
+ import { ModelOptions } from '../types';
6
6
  import applyScopeToInstance from '../utils/scopeAttributes';
7
7
 
8
8
  /**
@@ -1,10 +1,10 @@
1
1
  /* eslint-disable no-param-reassign */
2
2
  import * as ValueRepo from '../repository/value';
3
3
  import * as DefinitionRepo from '../repository/definition';
4
- import type CustomFieldValue from '../models/CustomFieldValue';
5
- import type CustomFieldDefinition from '../models/CustomFieldDefinition';
6
- import type { SerializedCustomFields } from '../types/definition';
7
- import type { ModelOptions } from '../types';
4
+ import CustomFieldValue from '../models/CustomFieldValue';
5
+ import CustomFieldDefinition from '../models/CustomFieldDefinition';
6
+ import { SerializedCustomFields } from '../types/definition';
7
+ import { ModelOptions } from '../types';
8
8
  import applyScopeToInstance from '../utils/scopeAttributes';
9
9
 
10
10
  type SupportedHookTypes = 'afterFind' | 'afterCreate' | 'afterUpdate';
@@ -1,6 +1,6 @@
1
1
  import logger from '../utils/logger';
2
2
  import * as ValueRepo from '../repository/value';
3
- import type { ModelOptions } from '../types';
3
+ import { ModelOptions } from '../types';
4
4
  import applyScopeToInstance from '../utils/scopeAttributes';
5
5
 
6
6
  /**
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Application } from 'express';
2
- import type { Sequelize } from 'sequelize-typescript';
2
+ import { Sequelize } from 'sequelize-typescript';
3
3
  import {
4
4
  initTables, initTestModels,
5
5
  } from './models';
@@ -11,7 +11,7 @@ import {
11
11
  addHooks, addScopes, applyCustomAssociation, removeHooks,
12
12
  } from './utils/init';
13
13
 
14
- export * from './utils/validations/schema/custom-fields';
14
+ export * from './utils/validations/custom-fields';
15
15
 
16
16
  export * from './utils/constants';
17
17
 
@@ -1,3 +1,5 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ /* eslint-disable indent */
1
3
  import {
2
4
  Table,
3
5
  Column,
@@ -10,13 +12,11 @@ import {
10
12
  AfterSave,
11
13
  Is,
12
14
  } from 'sequelize-typescript';
13
-
15
+ import { validateValidation } from '../utils/validations/custom';
14
16
  import { CustomFieldDefinitionType } from '../utils/validations/type';
15
- import { CustomValidationTypes } from '../utils/validations/validators';
16
17
  import { CustomFieldValue } from '.';
17
18
  import { sendDimEvent } from '../events';
18
19
  import { UnsupportedCustomFieldTypeError, UnsupportedCustomValidationError } from '../errors';
19
- import logger from '../utils/logger';
20
20
 
21
21
  @DefaultScope(() => ({ where: { disabled: false } }))
22
22
  @Table({
@@ -30,12 +30,8 @@ import logger from '../utils/logger';
30
30
  timestamps: true,
31
31
  validate: {
32
32
  validationByType(this: CustomFieldDefinition) {
33
- // Verify the validation is provided if needed
34
- if (!this.validation) {
35
- if (CustomValidationTypes[this.fieldType]) {
36
- logger.error(`No custom validation for custom field type ${this.fieldType} found`);
37
- throw new UnsupportedCustomValidationError(`Validation provided for "${this.fieldType}" is not supported`);
38
- }
33
+ if (!validateValidation(this.fieldType, this.validation)) {
34
+ throw new UnsupportedCustomValidationError(`Validation provided for "${this.fieldType}" is not supported`);
39
35
  }
40
36
  },
41
37
  },
@@ -1,3 +1,5 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ /* eslint-disable indent */
1
3
  import {
2
4
  Table,
3
5
  Column,
@@ -12,11 +14,13 @@ import {
12
14
  BeforeUpdate,
13
15
  BeforeBulkCreate,
14
16
  BeforeBulkUpdate,
17
+ Scopes,
15
18
  } from 'sequelize-typescript';
16
19
  import { sendDimEvent } from '../events';
17
20
  import { CustomFieldDefinition } from '.';
18
- import { validateValue } from '../utils/validations';
21
+ import validateValue from '../utils/validations';
19
22
  import * as CustomFieldDefinitionRepo from '../repository/definition';
23
+ import logger from '../utils/logger';
20
24
  import { InvalidValueError } from '../errors';
21
25
 
22
26
  @Table({
@@ -1,7 +1,10 @@
1
- import { Op, type FindOptions, type WhereOptions } from 'sequelize';
1
+ import {
2
+ FindOptions,
3
+ Op, WhereOptions,
4
+ } from 'sequelize';
2
5
  import { CustomFieldDefinition } from '../models';
3
6
  import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../types/definition';
4
- import type { ModelOptions } from '../types';
7
+ import { ModelOptions } from '../types';
5
8
 
6
9
  export const create = (data: CreateCustomFieldDefinition): Promise<CustomFieldDefinition> =>
7
10
  CustomFieldDefinition.create(data);
@@ -2,10 +2,10 @@
2
2
  import type { FindOptions, WhereOptions } from 'sequelize';
3
3
  import { CustomFieldValue, CustomFieldDefinition } from '../models';
4
4
  import * as DefinitionRepo from './definition';
5
- import type { CreateCustomFieldValue, ValuesToUpdate } from '../types/value';
5
+ import { CreateCustomFieldValue, ValuesToUpdate } from '../types/value';
6
6
  import logger from '../utils/logger';
7
7
  import { MissingDefinitionError } from '../errors';
8
- import type { ModelOptions } from '../types';
8
+ import { ModelOptions } from '../types';
9
9
 
10
10
  export const findByModelIdAndDefinition = async (modelId: string, customFieldDefinitionId: string) =>
11
11
  CustomFieldValue.findAll({ where: { modelId, customFieldDefinitionId }, include: [CustomFieldDefinition] });