@autofleet/sadot 1.0.0-beta.1 → 1.0.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.
Files changed (135) hide show
  1. package/README.md +5 -0
  2. package/dist/api/index.d.ts +2 -1
  3. package/dist/api/index.js +3 -2
  4. package/dist/api/v1/definition/index.d.ts +2 -1
  5. package/dist/api/v1/definition/index.js +12 -14
  6. package/dist/api/v1/definition/validations.js +14 -12
  7. package/dist/api/v1/errors.d.ts +3 -1
  8. package/dist/api/v1/errors.js +1 -4
  9. package/dist/api/v1/index.d.ts +2 -1
  10. package/dist/api/v1/index.js +5 -2
  11. package/dist/api/v1/validator/index.d.ts +3 -0
  12. package/dist/api/v1/validator/index.js +143 -0
  13. package/dist/api/v1/validator/validations.d.ts +6 -0
  14. package/dist/api/v1/validator/validations.js +40 -0
  15. package/dist/errors/index.d.ts +9 -1
  16. package/dist/errors/index.js +25 -4
  17. package/dist/events/index.d.ts +2 -1
  18. package/dist/events/index.js +17 -11
  19. package/dist/hooks/create.d.ts +2 -2
  20. package/dist/hooks/create.js +40 -19
  21. package/dist/hooks/enrich.d.ts +20 -2
  22. package/dist/hooks/enrich.js +88 -16
  23. package/dist/hooks/hooks.d.ts +17 -0
  24. package/dist/hooks/hooks.js +379 -0
  25. package/dist/hooks/index.d.ts +2 -3
  26. package/dist/hooks/index.js +6 -7
  27. package/dist/hooks/update.d.ts +2 -2
  28. package/dist/hooks/update.js +16 -26
  29. package/dist/hooks/utils/updateInstanceValues.d.ts +15 -0
  30. package/dist/hooks/utils/updateInstanceValues.js +50 -0
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +19 -6
  33. package/dist/models/CustomFieldDefinition.d.ts +1 -0
  34. package/dist/models/CustomFieldDefinition.js +10 -2
  35. package/dist/models/CustomFieldEntries.d.ts +15 -0
  36. package/dist/models/CustomFieldEntries.js +123 -0
  37. package/dist/models/CustomFieldValue.d.ts +3 -2
  38. package/dist/models/CustomFieldValue.js +22 -14
  39. package/dist/models/CustomValidator.d.ts +17 -0
  40. package/dist/models/CustomValidator.js +98 -0
  41. package/dist/models/index.d.ts +10 -2
  42. package/dist/models/index.js +60 -22
  43. package/dist/repository/definition.d.ts +23 -7
  44. package/dist/repository/definition.js +36 -7
  45. package/dist/repository/entries.d.ts +13 -0
  46. package/dist/repository/entries.js +92 -0
  47. package/dist/repository/utils/formatValues.d.ts +3 -0
  48. package/dist/repository/utils/formatValues.js +16 -0
  49. package/dist/repository/validator.d.ts +21 -0
  50. package/dist/repository/validator.js +62 -0
  51. package/dist/repository/value.d.ts +1 -1
  52. package/dist/repository/value.js +7 -32
  53. package/dist/scopes/filter.d.ts +5 -22
  54. package/dist/scopes/filter.js +18 -65
  55. package/dist/scopes/helpers/filter.helpers.d.ts +42 -0
  56. package/dist/scopes/helpers/filter.helpers.js +204 -0
  57. package/dist/tests/api/test-api.js +6 -24
  58. package/dist/tests/helpers/commonHooks.d.ts +6 -0
  59. package/dist/tests/helpers/commonHooks.js +62 -0
  60. package/dist/tests/helpers/database-config.js +1 -1
  61. package/dist/tests/helpers/index.d.ts +6 -1
  62. package/dist/tests/helpers/index.js +15 -2
  63. package/dist/tests/mocks/definition.mock.d.ts +5 -2
  64. package/dist/tests/mocks/definition.mock.js +10 -1
  65. package/dist/tests/mocks/events.mock.d.ts +1 -0
  66. package/dist/tests/mocks/events.mock.js +4 -2
  67. package/dist/types/definition/index.d.ts +1 -0
  68. package/dist/types/entries/index.d.ts +25 -0
  69. package/dist/types/entries/index.js +2 -0
  70. package/dist/types/index.d.ts +19 -3
  71. package/dist/utils/constants/index.d.ts +1 -1
  72. package/dist/utils/helpers/index.d.ts +4 -3
  73. package/dist/utils/helpers/index.js +22 -30
  74. package/dist/utils/init.d.ts +5 -3
  75. package/dist/utils/init.js +13 -11
  76. package/dist/utils/logger/index.d.ts +1 -0
  77. package/dist/utils/logger/index.js +34 -0
  78. package/dist/utils/validations/index.d.ts +7 -1
  79. package/dist/utils/validations/index.js +28 -7
  80. package/dist/utils/validations/schema/validator-schema.d.ts +9 -0
  81. package/dist/utils/validations/schema/validator-schema.js +95 -0
  82. package/dist/utils/validations/type.d.ts +2 -1
  83. package/dist/utils/validations/validators/index.js +9 -9
  84. package/dist/utils/validations/validators/select.validator.js +5 -2
  85. package/dist/utils/validations/validators/status.validator.js +8 -2
  86. package/package.json +28 -12
  87. package/src/api/index.ts +3 -2
  88. package/src/api/v1/definition/index.ts +20 -23
  89. package/src/api/v1/definition/validations.ts +16 -16
  90. package/src/api/v1/errors.ts +4 -7
  91. package/src/api/v1/index.ts +5 -3
  92. package/src/api/v1/validator/index.ts +141 -0
  93. package/src/api/v1/validator/validations.ts +39 -0
  94. package/src/errors/index.ts +31 -3
  95. package/src/events/index.ts +25 -13
  96. package/src/hooks/create.ts +50 -28
  97. package/src/hooks/enrich.ts +137 -28
  98. package/src/hooks/hooks.ts +467 -0
  99. package/src/hooks/index.ts +10 -5
  100. package/src/hooks/update.ts +20 -7
  101. package/src/hooks/utils/updateInstanceValues.ts +63 -0
  102. package/src/index.ts +10 -8
  103. package/src/models/CustomFieldDefinition.ts +9 -2
  104. package/src/models/CustomFieldEntries.ts +81 -0
  105. package/src/models/CustomFieldValue.ts +25 -17
  106. package/src/models/CustomValidator.ts +78 -0
  107. package/src/models/index.ts +83 -25
  108. package/src/repository/definition.ts +62 -14
  109. package/src/repository/entries.ts +88 -0
  110. package/src/repository/utils/formatValues.ts +14 -0
  111. package/src/repository/validator.ts +104 -0
  112. package/src/repository/value.ts +5 -35
  113. package/src/scopes/filter.ts +33 -106
  114. package/src/scopes/helpers/filter.helpers.ts +227 -0
  115. package/src/tests/api/test-api.ts +4 -2
  116. package/src/tests/helpers/commonHooks.ts +43 -0
  117. package/src/tests/helpers/database-config.ts +1 -1
  118. package/src/tests/helpers/index.ts +18 -2
  119. package/src/tests/mocks/definition.mock.ts +18 -9
  120. package/src/tests/mocks/events.mock.ts +4 -1
  121. package/src/types/definition/index.ts +1 -0
  122. package/src/types/entries/index.ts +27 -0
  123. package/src/types/index.ts +20 -3
  124. package/src/utils/helpers/index.ts +28 -35
  125. package/src/utils/init.ts +17 -12
  126. package/src/utils/logger/index.ts +9 -0
  127. package/src/utils/validations/index.ts +30 -6
  128. package/src/utils/validations/schema/README.md +93 -0
  129. package/src/utils/validations/schema/validator-schema.ts +106 -0
  130. package/src/utils/validations/type.ts +2 -1
  131. package/src/utils/validations/validators/index.ts +9 -9
  132. package/src/utils/validations/validators/select.validator.ts +3 -2
  133. package/src/utils/validations/validators/status.validator.ts +6 -2
  134. package/tsconfig.build.json +7 -0
  135. package/tsconfig.json +1 -1
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # V1 migration
2
+
3
+ The breaking change in this version is the change of the minimum required version of `zehut` from `^3` to `^4`.
4
+
5
+ Additionally, the minimum node version is now 18, due to the minimum version of node defined in `zehut`.
@@ -1,2 +1,3 @@
1
- declare const router: import("express-serve-static-core").Router;
1
+ /// <reference types="express" />
2
+ declare const router: import("express").Router;
2
3
  export default router;
package/dist/api/index.js CHANGED
@@ -4,8 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  // export the api object
7
- const express_1 = require("express");
7
+ const node_common_1 = require("@autofleet/node-common");
8
8
  const v1_1 = __importDefault(require("./v1"));
9
- const router = (0, express_1.Router)({ mergeParams: true });
9
+ const logger_1 = __importDefault(require("../utils/logger"));
10
+ const router = (0, node_common_1.Router)({ logger: logger_1.default });
10
11
  router.use('/v1', v1_1.default);
11
12
  exports.default = router;
@@ -1,2 +1,3 @@
1
- declare const router: import("express-serve-static-core").Router;
1
+ /// <reference types="express" />
2
+ declare const router: import("express").Router;
2
3
  export default router;
@@ -27,12 +27,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const errors_1 = require("@autofleet/errors");
30
- const express_1 = require("express");
30
+ const node_common_1 = require("@autofleet/node-common");
31
31
  const errors_2 = __importDefault(require("../errors"));
32
32
  const DefinitionRepo = __importStar(require("../../../repository/definition"));
33
33
  const validations_1 = require("./validations");
34
34
  const logger_1 = __importDefault(require("../../../utils/logger"));
35
- const router = (0, express_1.Router)({ mergeParams: true });
35
+ const router = (0, node_common_1.Router)({ logger: logger_1.default });
36
36
  const ENTITY = 'CustomFieldDefinition';
37
37
  const toPascalCase = (str) => str.replace(/(^\w|-\w)/g, (subStr) => subStr.replace(/-/, '').toUpperCase());
38
38
  /**
@@ -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,27 +68,26 @@ 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
  /**
75
75
  * Get all
76
76
  */
77
77
  router.get('/', async (req, res) => {
78
- const { modelName } = req.params;
79
- const { entityIds } = req.query;
78
+ const { params: { modelName }, query: { entityIds } } = req;
80
79
  const modelType = toPascalCase(modelName);
81
80
  try {
82
- const where = { modelType };
83
- if (entityIds?.length > 0) {
84
- where.entityId = entityIds;
85
- }
86
- const customFieldDefinitions = await DefinitionRepo.findAll({ ...where }, { withDisabled: true });
81
+ const where = {
82
+ modelType,
83
+ ...(entityIds?.length > 0 && { entityId: entityIds }),
84
+ };
85
+ const customFieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true });
87
86
  return res.json(customFieldDefinitions);
88
87
  }
89
88
  catch (err) {
90
89
  logger_1.default.error('Failed to fetch custom field definitions', err);
91
- 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` });
92
91
  }
93
92
  });
94
93
  /**
@@ -98,7 +97,6 @@ router.patch('/:customFieldDefinitionId', async (req, res) => {
98
97
  const { customFieldDefinitionId, modelName } = req.params;
99
98
  const modelType = toPascalCase(modelName);
100
99
  try {
101
- // eslint-disable-next-line max-len
102
100
  const validatedPayload = await (0, validations_1.validateCustomFieldDefinitionUpdate)(req.body);
103
101
  const customFieldDefinition = await DefinitionRepo.findByWhere({
104
102
  id: customFieldDefinitionId,
@@ -112,7 +110,7 @@ router.patch('/:customFieldDefinitionId', async (req, res) => {
112
110
  }
113
111
  catch (err) {
114
112
  logger_1.default.error('Failed to patch custom field definition', err);
115
- 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` });
116
114
  }
117
115
  });
118
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,23 +28,23 @@ 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.any(),
33
- }).when('fieldType', {
34
- is: constants_1.CustomFieldDefinitionType.STATUS,
35
- then: statusValidationObjectSchema,
36
- otherwise: joi_1.default.any(),
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: [
40
40
  { is: constants_1.CustomFieldDefinitionType.BOOLEAN, then: joi_1.default.boolean().allow(null) },
41
41
  { is: constants_1.CustomFieldDefinitionType.DATE, then: joi_1.default.date().allow(null) },
42
42
  { is: constants_1.CustomFieldDefinitionType.DATETIME, then: joi_1.default.date().allow(null) },
43
- { is: constants_1.CustomFieldDefinitionType.FILE, then: FileValidationSchema },
44
- { is: constants_1.CustomFieldDefinitionType.IMAGE, then: joi_1.default.string().uri().allow(null) },
43
+ { is: constants_1.CustomFieldDefinitionType.FILE, then: joi_1.default.array().items(FileValidationSchema).allow(null) },
44
+ { is: constants_1.CustomFieldDefinitionType.IMAGE, then: joi_1.default.array().items(joi_1.default.string().uri()).allow(null) },
45
45
  { is: constants_1.CustomFieldDefinitionType.NUMBER, then: joi_1.default.number().allow(null) },
46
46
  { is: constants_1.CustomFieldDefinitionType.SELECT, then: joi_1.default.string().allow(null) },
47
- { is: constants_1.CustomFieldDefinitionType.STATUS, then: statusValidationObject.allow(null) },
47
+ { is: constants_1.CustomFieldDefinitionType.STATUS, then: joi_1.default.string().allow(null) },
48
48
  { is: constants_1.CustomFieldDefinitionType.TEXT, then: joi_1.default.string().allow(null) },
49
49
  ],
50
50
  });
@@ -59,7 +59,8 @@ const CustomFieldDefinitionCreationSchema = joi_1.default.object({
59
59
  description: joi_1.default.string(),
60
60
  required: joi_1.default.boolean(),
61
61
  disabled: joi_1.default.boolean(),
62
- });
62
+ blockEditingFromUI: joi_1.default.boolean(),
63
+ }).oxor('required', 'blockEditingFromUI', { isPresent: (value) => value === true });
63
64
  const CustomFieldDefinitionUpdateSchema = joi_1.default.object({
64
65
  displayName: joi_1.default.string(),
65
66
  validation: ValidationSchema,
@@ -68,7 +69,8 @@ const CustomFieldDefinitionUpdateSchema = joi_1.default.object({
68
69
  description: joi_1.default.string().allow(null),
69
70
  required: joi_1.default.boolean(),
70
71
  disabled: joi_1.default.boolean(),
71
- });
72
+ blockEditingFromUI: joi_1.default.boolean(),
73
+ }).oxor('required', 'blockEditingFromUI', { isPresent: (value) => value === true });
72
74
  const validateCustomFieldDefinitionCreation = (payload) => CustomFieldDefinitionCreationSchema.validateAsync(payload, { abortEarly: false });
73
75
  exports.validateCustomFieldDefinitionCreation = validateCustomFieldDefinitionCreation;
74
76
  const validateCustomFieldDefinitionUpdate = (payload) => CustomFieldDefinitionUpdateSchema.validateAsync(payload, { abortEarly: false });
@@ -1,2 +1,4 @@
1
- declare const _default: (err: any, res: any, additionalData?: any) => Promise<any>;
1
+ import type { Response } from 'express';
2
+ import { type LogPayload } from '@autofleet/errors';
3
+ declare const _default: (err: any, res: Response, additionalData?: LogPayload) => void;
2
4
  export default _default;
@@ -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 joi_1.ValidationError) {
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);
@@ -1,2 +1,3 @@
1
- declare const router: import("express-serve-static-core").Router;
1
+ /// <reference types="express" />
2
+ declare const router: import("express").Router;
2
3
  export default router;
@@ -3,8 +3,11 @@ 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
- const express_1 = require("express");
6
+ const node_common_1 = require("@autofleet/node-common");
7
+ const logger_1 = __importDefault(require("../../utils/logger"));
7
8
  const definition_1 = __importDefault(require("./definition"));
8
- const router = (0, express_1.Router)({ mergeParams: true });
9
+ const validator_1 = __importDefault(require("./validator"));
10
+ const router = (0, node_common_1.Router)({ logger: logger_1.default });
9
11
  router.use('/custom-field-definitions/:modelName', definition_1.default);
12
+ router.use('/custom-validators/:modelName', validator_1.default);
10
13
  exports.default = router;
@@ -0,0 +1,3 @@
1
+ /// <reference types="express" />
2
+ declare const router: import("express").Router;
3
+ export default router;
@@ -0,0 +1,143 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const errors_1 = require("@autofleet/errors");
30
+ const node_common_1 = require("@autofleet/node-common");
31
+ const http_status_codes_1 = require("http-status-codes");
32
+ const errors_2 = __importDefault(require("../errors"));
33
+ const ValidatorRepo = __importStar(require("../../../repository/validator"));
34
+ const validations_1 = __importDefault(require("./validations"));
35
+ const logger_1 = __importDefault(require("../../../utils/logger"));
36
+ const validator_schema_1 = require("../../../utils/validations/schema/validator-schema");
37
+ const router = (0, node_common_1.Router)({ logger: logger_1.default });
38
+ const ENTITY = 'CustomValidator';
39
+ /**
40
+ * Create
41
+ */
42
+ router.post('/', async (req, res) => {
43
+ const { modelName } = req.params;
44
+ try {
45
+ // Validate the request body
46
+ const validatedPayload = await validations_1.default.create.validateAsync(req.body);
47
+ // Validate that the schema is a valid AJV schema
48
+ (0, validator_schema_1.validateValidatorSchema)(validatedPayload.schema);
49
+ const validator = await ValidatorRepo.create({
50
+ ...validatedPayload,
51
+ modelType: modelName,
52
+ });
53
+ return res.status(http_status_codes_1.StatusCodes.CREATED).json(validator);
54
+ }
55
+ catch (err) {
56
+ return (0, errors_2.default)(err, res, { logger: logger_1.default, message: `Error in create ${ENTITY} request` });
57
+ }
58
+ });
59
+ /**
60
+ * Get all
61
+ */
62
+ router.get('/', async (req, res) => {
63
+ try {
64
+ const { modelName } = req.params;
65
+ const { entityId, entityType } = req.query;
66
+ const where = {
67
+ modelType: modelName,
68
+ ...(entityId && { entityId }),
69
+ ...(entityType && { entityType }),
70
+ };
71
+ const validators = await ValidatorRepo.findAll(where);
72
+ return res.status(http_status_codes_1.StatusCodes.OK).json({ validators });
73
+ }
74
+ catch (err) {
75
+ return (0, errors_2.default)(err, res, { logger: logger_1.default, message: `Error in get all ${ENTITY} request` });
76
+ }
77
+ });
78
+ /**
79
+ * Get by id
80
+ */
81
+ router.get('/:validatorId', async (req, res) => {
82
+ try {
83
+ const { validatorId, modelName } = req.params;
84
+ // Include disabled validators when fetching by ID
85
+ const validators = await ValidatorRepo.findAll({ id: validatorId, modelType: modelName }, { withDisabled: true });
86
+ if (!validators.length) {
87
+ throw new errors_1.ResourceNotFoundError('Validator not found');
88
+ }
89
+ return res.status(http_status_codes_1.StatusCodes.OK).json(validators[0]);
90
+ }
91
+ catch (err) {
92
+ return (0, errors_2.default)(err, res, { logger: logger_1.default, message: `Error in get ${ENTITY} request` });
93
+ }
94
+ });
95
+ /**
96
+ * Update
97
+ */
98
+ router.patch('/:validatorId', async (req, res) => {
99
+ try {
100
+ const { validatorId } = req.params;
101
+ // Validate the request body
102
+ const validatedPayload = await validations_1.default.update.validateAsync(req.body);
103
+ // If schema is included in the update, validate that it's a valid AJV schema
104
+ if (validatedPayload.schema) {
105
+ (0, validator_schema_1.validateValidatorSchema)(validatedPayload.schema);
106
+ }
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
+ const [count, validators] = await ValidatorRepo.update(validatorId, validatedPayload);
113
+ if (!count) {
114
+ throw new errors_1.ResourceNotFoundError('Validator not found');
115
+ }
116
+ return res.status(http_status_codes_1.StatusCodes.OK).json(validators[0]);
117
+ }
118
+ catch (err) {
119
+ return (0, errors_2.default)(err, res, { logger: logger_1.default, message: `Error in update ${ENTITY} request` });
120
+ }
121
+ });
122
+ /**
123
+ * Delete (disable)
124
+ */
125
+ router.delete('/:validatorId', async (req, res) => {
126
+ try {
127
+ 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
+ const [count] = await ValidatorRepo.disable(validatorId);
134
+ if (!count) {
135
+ throw new errors_1.ResourceNotFoundError('Validator failed to be disabled');
136
+ }
137
+ return res.status(http_status_codes_1.StatusCodes.NO_CONTENT).send();
138
+ }
139
+ catch (err) {
140
+ return (0, errors_2.default)(err, res, { logger: logger_1.default, message: `Error in delete ${ENTITY} request` });
141
+ }
142
+ });
143
+ exports.default = router;
@@ -0,0 +1,6 @@
1
+ import Joi from 'joi';
2
+ declare const validationSchemas: {
3
+ create: Joi.ObjectSchema<any>;
4
+ update: Joi.ObjectSchema<any>;
5
+ };
6
+ export default validationSchemas;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const joi_1 = __importDefault(require("joi"));
7
+ // Schema for validating JSON Schema objects
8
+ const jsonSchemaValidation = joi_1.default.object().unknown(true);
9
+ const validationSchemas = {
10
+ create: joi_1.default.object({
11
+ entityId: joi_1.default.string().uuid().required(),
12
+ entityType: joi_1.default.string().required(),
13
+ schema: joi_1.default.object({
14
+ type: joi_1.default.string().valid('object').required(),
15
+ properties: joi_1.default.object({
16
+ before: jsonSchemaValidation,
17
+ after: jsonSchemaValidation,
18
+ }).required(),
19
+ if: joi_1.default.object().optional(),
20
+ then: joi_1.default.object().optional(),
21
+ else: joi_1.default.object().optional(),
22
+ }).required(),
23
+ }),
24
+ update: joi_1.default.object({
25
+ entityId: joi_1.default.string().uuid(),
26
+ entityType: joi_1.default.string(),
27
+ schema: joi_1.default.object({
28
+ type: joi_1.default.string().valid('object'),
29
+ properties: joi_1.default.object({
30
+ before: jsonSchemaValidation,
31
+ after: jsonSchemaValidation,
32
+ }),
33
+ if: joi_1.default.object().optional(),
34
+ then: joi_1.default.object().optional(),
35
+ else: joi_1.default.object().optional(),
36
+ }),
37
+ disabled: joi_1.default.boolean(),
38
+ }).min(1),
39
+ };
40
+ exports.default = validationSchemas;
@@ -1,4 +1,6 @@
1
1
  import { BadRequest } from '@autofleet/errors';
2
+ import type { ValidationError } from 'joi';
3
+ import type { EntriesValidationError } from '../types/entries';
2
4
  export declare class MissingRequiredCustomFieldError extends BadRequest {
3
5
  constructor(missingFields: string[]);
4
6
  }
@@ -8,8 +10,14 @@ export declare class UnsupportedCustomFieldTypeError extends BadRequest {
8
10
  export declare class UnsupportedCustomValidationError extends BadRequest {
9
11
  constructor(fieldType: string);
10
12
  }
13
+ export declare class InvalidFieldTypeError extends BadRequest {
14
+ constructor(fieldType: string);
15
+ }
11
16
  export declare class InvalidValueError extends BadRequest {
12
- constructor(value: any, fieldType: string);
17
+ constructor(value: any, fieldDefinitionName: string, joiValidationError: ValidationError);
18
+ }
19
+ export declare class InvalidEntriesError extends BadRequest {
20
+ constructor(modelId: string, validationErrors: EntriesValidationError[]);
13
21
  }
14
22
  export declare class MissingDefinitionError extends BadRequest {
15
23
  constructor(fieldNames: string[]);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MissingDefinitionError = exports.InvalidValueError = exports.UnsupportedCustomValidationError = exports.UnsupportedCustomFieldTypeError = exports.MissingRequiredCustomFieldError = void 0;
3
+ exports.MissingDefinitionError = exports.InvalidEntriesError = exports.InvalidValueError = exports.InvalidFieldTypeError = exports.UnsupportedCustomValidationError = exports.UnsupportedCustomFieldTypeError = exports.MissingRequiredCustomFieldError = void 0;
4
4
  /* eslint-disable max-classes-per-file */
5
5
  const errors_1 = require("@autofleet/errors");
6
6
  class MissingRequiredCustomFieldError extends errors_1.BadRequest {
@@ -27,14 +27,35 @@ class UnsupportedCustomValidationError extends errors_1.BadRequest {
27
27
  }
28
28
  }
29
29
  exports.UnsupportedCustomValidationError = UnsupportedCustomValidationError;
30
+ class InvalidFieldTypeError extends errors_1.BadRequest {
31
+ constructor(fieldType) {
32
+ const err = new Error(`Invalid field type ${fieldType}`);
33
+ super([err], null, null);
34
+ this.message = 'INVALID_FIELD_TYPE';
35
+ }
36
+ }
37
+ exports.InvalidFieldTypeError = InvalidFieldTypeError;
30
38
  class InvalidValueError extends errors_1.BadRequest {
31
- constructor(value, fieldType) {
32
- const err = new Error(`Invalid "${fieldType}" value ${JSON.stringify(value)}`);
39
+ constructor(value, fieldDefinitionName, joiValidationError) {
40
+ const formattedErrorMessage = joiValidationError.message
41
+ .replace(/"/g, '')
42
+ .replace('value', `'${fieldDefinitionName}'`);
43
+ const formattedValue = typeof value === 'object' ? JSON.stringify(value) : value;
44
+ const invalidValueMessage = `Invalid Value on field '${fieldDefinitionName}'. ${formattedErrorMessage}. received: '${formattedValue}'`;
45
+ const err = new Error(invalidValueMessage);
33
46
  super([err], null, null);
34
- this.message = 'INVALID_VALUE';
47
+ this.message = invalidValueMessage;
35
48
  }
36
49
  }
37
50
  exports.InvalidValueError = InvalidValueError;
51
+ class InvalidEntriesError extends errors_1.BadRequest {
52
+ constructor(modelId, validationErrors) {
53
+ const errors = validationErrors.map((validationError) => new InvalidValueError(validationError.value, validationError.fieldDefinitionName, validationError.joiValidationError));
54
+ super(errors, null, null);
55
+ this.message = `Invalid entries on ${modelId}\n${validationErrors.map((validationError) => (`${validationError.fieldDefinitionName} - ${validationError.joiValidationError.message}`)).join('\n')}`;
56
+ }
57
+ }
58
+ exports.InvalidEntriesError = InvalidEntriesError;
38
59
  class MissingDefinitionError extends errors_1.BadRequest {
39
60
  constructor(fieldNames) {
40
61
  const err = new Error(`Missing custom field definition for field ${fieldNames.join(',')}`);
@@ -1,4 +1,5 @@
1
1
  import Events from '@autofleet/events';
2
+ import type { CustomFieldDefinition, CustomFieldEntries, CustomFieldValue, CustomValidator } from '../models';
2
3
  declare const events: Events;
3
- export declare const sendDimEvent: (instance: any) => void;
4
+ export declare const sendDimEvent: (instance: CustomFieldDefinition | CustomFieldValue | CustomFieldEntries | CustomValidator) => void;
4
5
  export default events;
@@ -5,20 +5,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.sendDimEvent = void 0;
7
7
  const events_1 = __importDefault(require("@autofleet/events"));
8
+ const zehut_1 = require("@autofleet/zehut");
8
9
  const logger_1 = __importDefault(require("../utils/logger"));
9
- const events = new events_1.default({ logger: logger_1.default });
10
- const KEYS_TO_CONVERT = ['value', 'defaultValue'];
10
+ const events = new events_1.default({
11
+ logger: logger_1.default,
12
+ getUserId: (payload) => payload?.user_id ?? (0, zehut_1.getUser)()?.id ?? null,
13
+ });
14
+ const KEYS_TO_CONVERT = ['value', 'defaultValue', 'blockEditingFromUI'];
11
15
  const stringifyBooleans = (savedObject, keysToConvert) => {
12
- if (!Object.keys(savedObject).some((key) => keysToConvert.includes(key))) {
16
+ const savedObjectKeySet = new Set(Object.keys(savedObject));
17
+ if (keysToConvert.every((key) => !savedObjectKeySet.has(key))) {
13
18
  return savedObject;
14
19
  }
15
- const objectToReturn = { ...savedObject };
16
- keysToConvert.forEach((key) => {
17
- if (typeof savedObject[key] === 'boolean') {
18
- objectToReturn[key] = savedObject[key].toString();
19
- }
20
- });
21
- return objectToReturn;
20
+ return {
21
+ ...savedObject,
22
+ ...Object.fromEntries(keysToConvert.map((key) => [key, typeof savedObject[key] === 'boolean' ? savedObject[key].toString() : savedObject[key]])),
23
+ };
22
24
  };
23
25
  const modelTableMapping = {
24
26
  CustomFieldDefinition: {
@@ -29,6 +31,10 @@ const modelTableMapping = {
29
31
  tableName: 'dim_custom_field_value',
30
32
  eventVersion: '1',
31
33
  },
34
+ CustomFieldEntries: {
35
+ tableName: 'dim_custom_field_entries',
36
+ eventVersion: '1',
37
+ },
32
38
  };
33
39
  const sendDimEvent = (instance) => {
34
40
  const mapping = modelTableMapping[instance.constructor.name];
@@ -42,7 +48,7 @@ const sendDimEvent = (instance) => {
42
48
  catch (err) {
43
49
  logger_1.default.error('Failed to convert booleans in dim event payload', err);
44
50
  }
45
- events.sendObject(mapping.tableName, mapping.eventVersion, objectToSend);
51
+ events.sendObject(mapping.tableName, mapping.eventVersion, objectToSend).catch(() => { });
46
52
  };
47
53
  exports.sendDimEvent = sendDimEvent;
48
54
  exports.default = events;
@@ -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>;
@@ -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,30 +45,51 @@ 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
+ const { include, useEntityIdFromInclude } = modelOptions;
51
52
  const modelType = instance.constructor.name;
52
53
  const identifiers = (0, scopeAttributes_1.default)(instance, scopeAttributes);
53
- // get all model's required definitions
54
- const requiredFieldsNames = await DefinitionRepo.getRequiredFields(modelType, instance.id, identifiers, modelOptions);
55
- const customFieldsIdx = fields.indexOf('customFields');
56
- const { customFields } = instance;
57
- if (customFieldsIdx > -1 && customFields) {
58
- const fieldsNames = Object.keys(customFields);
59
- const missingFields = requiredFieldsNames.filter((name) => !fieldsNames.includes(name));
60
- if (missingFields?.length > 0) {
61
- throw new errors_1.MissingRequiredCustomFieldError(missingFields);
62
- }
63
- await ValueRepo.updateValues(modelType, instance.id, identifiers, customFields, {
64
- transaction: options.transaction,
65
- modelOptions,
66
- }, true);
54
+ const where = {
55
+ modelType,
56
+ disabled: false,
57
+ ...(!useEntityIdFromInclude && { entityId: identifiers }),
58
+ };
59
+ const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: false, transaction: options.transaction, include: include?.(identifiers) });
60
+ const requiredFieldsNames = Array.from(new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)));
61
+ const fieldsWithDefaultValue = fieldDefinitions.filter((def) => ![null, undefined].includes(def.defaultValue));
62
+ if (fieldsWithDefaultValue.length) {
67
63
  // eslint-disable-next-line no-param-reassign
68
- fields.splice(customFieldsIdx, 1);
64
+ instance.customFields || (instance.customFields = {});
65
+ fieldsWithDefaultValue.filter((def) => (instance.customFields?.[def.name] === undefined)).forEach(({ name, defaultValue }) => {
66
+ // eslint-disable-next-line no-param-reassign
67
+ instance.customFields[name] = defaultValue;
68
+ });
69
+ }
70
+ const { customFields } = instance;
71
+ const fieldsNames = Object.keys(customFields ?? {});
72
+ const missingFields = requiredFieldsNames.filter((name) => !fieldsNames.includes(name));
73
+ if (missingFields?.length) {
74
+ throw new errors_1.MissingRequiredCustomFieldError(missingFields);
69
75
  }
70
- else if (requiredFieldsNames?.length > 0) {
71
- throw new errors_1.MissingRequiredCustomFieldError(requiredFieldsNames);
76
+ const customFieldsIdx = fields.indexOf('customFields');
77
+ if (customFieldsIdx === -1 || !customFields || !Object.keys(customFields).length) {
78
+ // After checking for required fields and fields with default values, and we have no custom fields.
79
+ return;
72
80
  }
81
+ await (0, updateInstanceValues_1.default)({
82
+ modelId: instance.id,
83
+ modelType,
84
+ identifiers,
85
+ customFields,
86
+ options: {
87
+ useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,
88
+ transaction: options.transaction,
89
+ modelOptions,
90
+ },
91
+ });
92
+ // eslint-disable-next-line no-param-reassign
93
+ fields.splice(customFieldsIdx, 1);
73
94
  };
74
95
  exports.beforeCreate = beforeCreate;