@autofleet/sadot 1.0.0-beta.0 → 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
@@ -1,18 +1,18 @@
1
+ import { InvalidEntriesError } from '../../errors';
2
+ import type { CustomFieldDefinition } from '../../models';
3
+ import type { CustomFieldEntriesDTO } from '../../types/entries';
1
4
  import type { CustomFieldDefinitionType } from '../constants';
2
5
  import { validators } from './validators';
3
6
 
7
+ export const validateFieldType = (type: CustomFieldDefinitionType): boolean => Object.keys(validators).includes(type);
8
+
4
9
  export const validateValue = (
5
10
  value: unknown,
6
11
  valueType: CustomFieldDefinitionType,
7
12
  validation?: unknown,
8
13
  ) => {
9
14
  const validator = validators[valueType];
10
- if (!validator) {
11
- // Unsupported field type
12
- return false;
13
- }
14
- // Always allow null values
15
- return value === null || validator(value, validation);
15
+ return validator(value, validation);
16
16
  /** TODO: Add validation for required fields
17
17
  * @example
18
18
  * if (validations.required && !value) {
@@ -20,3 +20,27 @@ export const validateValue = (
20
20
  * }
21
21
  */
22
22
  };
23
+
24
+ export const validateInstanceCustomFieldEntries = (instance: CustomFieldEntriesDTO, definitionsByName: { [defName: string]: CustomFieldDefinition; }) => {
25
+ const validationErrors = Object.entries(instance.customFields)
26
+ .map(([customFieldName, value]) => {
27
+ // Allow NULL values, just like we do in custom_field_values.
28
+ if (value === null) return null;
29
+
30
+ const { validation, fieldType } = definitionsByName[customFieldName];
31
+ const result = validateValue(value, fieldType, validation);
32
+ if (result?.error) {
33
+ return {
34
+ joiValidationError: result.error,
35
+ fieldDefinitionName: customFieldName,
36
+ value,
37
+ };
38
+ }
39
+ return null;
40
+ })
41
+ .filter((result) => !!result);
42
+
43
+ if (validationErrors?.length) {
44
+ throw new InvalidEntriesError(instance.modelId, validationErrors);
45
+ }
46
+ };
@@ -0,0 +1,93 @@
1
+ # Schema Validation
2
+
3
+ This directory contains schema validation utilities for the Sadot library, including:
4
+
5
+ 1. **Custom Fields Schemas** - Schema definitions for custom field validation
6
+ 2. **Validator Schemas** - Meta-validation for custom validator JSON schemas
7
+
8
+ ## Custom Fields Schema
9
+
10
+ `custom-fields.ts` defines JSON Schema validation for custom field definitions, ensuring fields are created with valid configurations.
11
+
12
+ ## Validator Schema Validation
13
+
14
+ `validator-schema.ts` provides meta-validation for custom validator schemas. It ensures that:
15
+
16
+ 1. The validator schema follows the expected structure
17
+ 2. The schema is a valid JSON Schema that can be compiled by AJV
18
+ 3. The validation rules defined in the schema are syntactically correct
19
+
20
+ ### Usage
21
+
22
+ The `validateValidatorSchema` function is used by the custom validator API to validate schemas at creation and update time:
23
+
24
+ ```typescript
25
+ import { validateValidatorSchema } from '../utils/validations/schema/validator-schema';
26
+
27
+ // Validates a schema, throws BadRequest if invalid
28
+ validateValidatorSchema(schema);
29
+ ```
30
+
31
+ ### Schema Structure
32
+
33
+ Validator schemas must follow this general structure:
34
+
35
+ ```json
36
+ {
37
+ "type": "object",
38
+ "properties": {
39
+ "before": {
40
+ "type": "object",
41
+ "properties": {
42
+ // Model properties to validate before update
43
+ }
44
+ },
45
+ "after": {
46
+ "type": "object",
47
+ "properties": {
48
+ // Model properties to validate after update/create
49
+ }
50
+ }
51
+ }
52
+ }
53
+ ```
54
+
55
+ For create operations, only the `after` section is used. For update operations, both `before` and `after` can be used.
56
+
57
+ ### Conditional Validation
58
+
59
+ The schema can include conditional validation using JSON Schema's `if/then/else` constructs:
60
+
61
+ ```json
62
+ {
63
+ "type": "object",
64
+ "if": {
65
+ "properties": {
66
+ "before": {
67
+ "properties": {
68
+ "status": { "const": "PENDING" }
69
+ }
70
+ }
71
+ }
72
+ },
73
+ "then": {
74
+ "properties": {
75
+ "after": {
76
+ "properties": {
77
+ "status": { "enum": ["APPROVED", "REJECTED"] }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ This example validates that if a model's status was "PENDING", it can only be updated to "APPROVED" or "REJECTED".
86
+
87
+ ### Error Handling
88
+
89
+ Schema validation errors are thrown as BadRequest errors with descriptive messages indicating:
90
+
91
+ 1. The specific validation error in the schema
92
+ 2. The path within the schema where the error occurred
93
+ 3. A readable message for API consumers
@@ -0,0 +1,106 @@
1
+ import Ajv from 'ajv';
2
+ import addFormats from 'ajv-formats';
3
+ import { BadRequest } from '@autofleet/errors';
4
+ import logger from '../../logger';
5
+
6
+ // Instantiate Ajv for meta-validation
7
+ const metaValidator = new Ajv({
8
+ allErrors: true,
9
+ strict: false,
10
+ strictTypes: false,
11
+ $data: true, // Enable $data references
12
+ });
13
+ addFormats(metaValidator);
14
+
15
+ /**
16
+ * Schema for validating JSON Schema objects in custom validators
17
+ * This is a meta-schema to ensure that custom validator schemas are valid Ajv schemas
18
+ */
19
+ const validatorMetaSchema = {
20
+ type: 'object',
21
+ properties: {
22
+ type: { type: 'string', enum: ['object'] },
23
+ properties: {
24
+ type: 'object',
25
+ properties: {
26
+ before: {
27
+ type: 'object',
28
+ properties: {
29
+ type: { type: 'string', enum: ['object'] },
30
+ properties: { type: 'object' },
31
+ },
32
+ },
33
+ after: {
34
+ type: 'object',
35
+ properties: {
36
+ type: { type: 'string', enum: ['object'] },
37
+ properties: { type: 'object' },
38
+ },
39
+ },
40
+ },
41
+ },
42
+ required: {
43
+ type: 'array',
44
+ items: { type: 'string' },
45
+ },
46
+ if: { type: 'object' },
47
+ then: { type: 'object' },
48
+ else: { type: 'object' },
49
+ },
50
+ required: ['type', 'properties'],
51
+ };
52
+
53
+ /**
54
+ * Validates that a given schema is a valid Ajv schema
55
+ * This function is used to validate schemas passed to custom validators
56
+ *
57
+ * @param schema The schema to validate
58
+ * @returns true if valid, throws an error if invalid
59
+ */
60
+ export const validateValidatorSchema = (schema: Record<string, unknown>): boolean => {
61
+ try {
62
+ // First validate the schema structure
63
+ const validateMetaSchema = metaValidator.compile(validatorMetaSchema);
64
+ const isValidStructure = validateMetaSchema(schema);
65
+
66
+ if (!isValidStructure) {
67
+ const errorDetails = validateMetaSchema.errors?.map((err) =>
68
+ `${(err as unknown as { instancePath: string }).instancePath || ''} ${(err as unknown as { message: string }).message || 'Invalid schema structure'}`).join(', ');
69
+
70
+ logger.error('Invalid validator schema structure', {
71
+ errors: validateMetaSchema.errors,
72
+ schema,
73
+ });
74
+
75
+ throw new BadRequest(
76
+ [new Error(`Invalid validator schema structure: ${errorDetails}`)],
77
+ ['Invalid validator schema structure'],
78
+ );
79
+ }
80
+
81
+ // Then try to compile the schema with Ajv to verify it's a valid JSON Schema
82
+ try {
83
+ metaValidator.compile(schema);
84
+ return true;
85
+ } catch (compileError) {
86
+ logger.error('Failed to compile validator schema', { error: compileError, schema });
87
+
88
+ throw new BadRequest(
89
+ [new Error(`Failed to compile validator schema: ${(compileError as Error).message}`)],
90
+ ['Invalid validator schema'],
91
+ );
92
+ }
93
+ } catch (error) {
94
+ if (error instanceof BadRequest) {
95
+ throw error;
96
+ }
97
+
98
+ logger.error('Error validating validator schema', { error, schema });
99
+ throw new BadRequest(
100
+ [new Error(`Error validating validator schema: ${(error as Error).message}`)],
101
+ ['Invalid validator schema'],
102
+ );
103
+ }
104
+ };
105
+
106
+ export default validateValidatorSchema;
@@ -1,3 +1,4 @@
1
+ import type { ValidationResult } from 'joi';
1
2
  import type { CustomFieldDefinitionType } from '../constants';
2
3
 
3
4
  /**
@@ -8,7 +9,7 @@ import type { CustomFieldDefinitionType } from '../constants';
8
9
  export type Validator<Value, DefinitionValidationObject> = (
9
10
  value: Value,
10
11
  validation?: DefinitionValidationObject
11
- ) => boolean;
12
+ ) => ValidationResult;
12
13
 
13
14
  /**
14
15
  * Validators is a map of custom-field types to their respective validators.
@@ -20,19 +20,19 @@ export const CustomValidationTypes = {
20
20
  export const validators: Validators = {
21
21
  [CustomFieldDefinitionType.SELECT]: validateSelect,
22
22
  [CustomFieldDefinitionType.STATUS]: validateStatus,
23
- [CustomFieldDefinitionType.TEXT]: (value) => (typeof value === 'string'),
24
- [CustomFieldDefinitionType.NUMBER]: (value) => (typeof value === 'number'),
25
- [CustomFieldDefinitionType.BOOLEAN]: (value) => (typeof value === 'boolean'),
26
- [CustomFieldDefinitionType.DATE]: (value) => (!Joi.date().validate(value).error),
27
- [CustomFieldDefinitionType.DATETIME]: (value) => (!Joi.date().validate(value).error),
28
- [CustomFieldDefinitionType.IMAGE]: (value) => (!Joi.array().min(1).unique()
23
+ [CustomFieldDefinitionType.TEXT]: (value) => Joi.string().min(0).validate(value),
24
+ [CustomFieldDefinitionType.NUMBER]: (value) => Joi.number().strict(true).validate(value),
25
+ [CustomFieldDefinitionType.BOOLEAN]: (value) => Joi.boolean().strict().validate(value),
26
+ [CustomFieldDefinitionType.DATE]: (value) => Joi.date().validate(value),
27
+ [CustomFieldDefinitionType.DATETIME]: (value) => Joi.date().validate(value),
28
+ [CustomFieldDefinitionType.IMAGE]: (value) => Joi.array().min(1).unique()
29
29
  .items(Joi.string().uri())
30
- .validate(value).error),
31
- [CustomFieldDefinitionType.FILE]: (value) => (!Joi.array().min(1).unique().items(Joi.object({
30
+ .validate(value),
31
+ [CustomFieldDefinitionType.FILE]: (value) => Joi.array().min(1).unique().items(Joi.object({
32
32
  name: Joi.string().required(),
33
33
  type: Joi.string(),
34
34
  size: Joi.string(),
35
35
  addedBy: Joi.string().uuid(),
36
36
  }))
37
- .validate(value).error),
37
+ .validate(value),
38
38
  };
@@ -1,3 +1,4 @@
1
+ import Joi from 'joi';
1
2
  import type { Validator } from '../type';
2
3
 
3
4
  /**
@@ -6,6 +7,6 @@ import type { Validator } from '../type';
6
7
  export const validateSelect: Validator<string, string[]> = (
7
8
  value,
8
9
  selectValues,
9
- ) => (Array.isArray(selectValues)
10
- && selectValues.includes(value)
10
+ ) => (
11
+ Joi.string().allow(null).valid(...selectValues).validate(value)
11
12
  );
@@ -1,3 +1,4 @@
1
+ import Joi from 'joi';
1
2
  import type { Validator } from '../type';
2
3
 
3
4
  type StatusColor = string | null; // TODO: Takes from @autofleet/colors ?
@@ -13,6 +14,9 @@ type StatusOption = {
13
14
  export const validateStatus: Validator<StatusValue, StatusOption[]> = (
14
15
  value,
15
16
  statusValues,
16
- ) => (Array.isArray(statusValues)
17
- && statusValues.some((status) => status.value === value)
17
+ ) => (
18
+ Joi.string()
19
+ .allow(null)
20
+ .valid(...statusValues.map((statusValue) => statusValue.value))
21
+ .validate(value)
18
22
  );
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "include": [
4
+ "src/**/*",
5
+ ],
6
+ "exclude": ["node_modules", "src/tests/**", "**/*.test.ts"]
7
+ }
package/tsconfig.json CHANGED
@@ -12,5 +12,5 @@
12
12
  "include": [
13
13
  "src/**/*",
14
14
  ],
15
- "exclude": ["node_modules", "**/*.test.ts"]
15
+ "exclude": ["node_modules"]
16
16
  }