@autofleet/sadot 1.1.3 → 1.1.4-beta
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.
- package/.nvmrc +1 -0
- package/dist/api/index.d.ts +3 -0
- package/dist/api/index.js +12 -2
- package/dist/api/v1/definition/index.d.ts +3 -0
- package/dist/api/v1/definition/index.js +116 -2
- package/dist/api/v1/definition/validations.d.ts +2 -0
- package/dist/api/v1/definition/validations.js +77 -2
- package/dist/api/v1/errors.d.ts +4 -0
- package/dist/api/v1/errors.js +12 -2
- package/dist/api/v1/index.d.ts +3 -0
- package/dist/api/v1/index.js +13 -2
- package/dist/api/v1/validator/index.d.ts +3 -0
- package/dist/api/v1/validator/index.js +143 -2
- package/dist/api/v1/validator/validations.d.ts +6 -23
- package/dist/api/v1/validator/validations.js +38 -2
- package/dist/errors/index.d.ts +24 -0
- package/dist/errors/index.js +66 -3
- package/dist/events/index.d.ts +5 -0
- package/dist/events/index.js +54 -2
- package/dist/hooks/create.d.ts +10 -0
- package/dist/hooks/create.js +95 -0
- package/dist/hooks/enrich.d.ts +25 -0
- package/dist/hooks/enrich.js +198 -2
- package/dist/hooks/find.d.ts +1 -0
- package/dist/hooks/find.js +29 -2
- package/dist/hooks/hooks.d.ts +17 -0
- package/dist/hooks/hooks.js +391 -2
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.js +17 -1
- package/dist/hooks/update.d.ts +10 -0
- package/dist/hooks/update.js +49 -0
- package/dist/hooks/utils/updateInstanceValues.d.ts +15 -0
- package/dist/hooks/utils/updateInstanceValues.js +50 -2
- package/dist/hooks/workaround.d.ts +10 -0
- package/dist/hooks/workaround.js +37 -0
- package/dist/index.d.ts +12 -22
- package/dist/index.js +67 -2
- package/dist/models/CustomFieldDefinition.d.ts +23 -29
- package/dist/models/CustomFieldDefinition.js +192 -2
- package/dist/models/CustomFieldEntries.d.ts +13 -14
- package/dist/models/CustomFieldEntries.js +123 -2
- package/dist/models/CustomFieldValue.d.ts +14 -20
- package/dist/models/CustomFieldValue.js +151 -2
- package/dist/models/CustomValidator.d.ts +15 -17
- package/dist/models/CustomValidator.js +98 -2
- package/dist/models/index.d.ts +18 -6
- package/dist/models/index.js +131 -2
- package/dist/models/tests/AssociatedTestModel.d.ts +12 -0
- package/dist/models/tests/AssociatedTestModel.js +71 -2
- package/dist/models/tests/TestModel.d.ts +12 -0
- package/dist/models/tests/TestModel.js +69 -2
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.d.ts +10 -0
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.js +53 -2
- package/dist/models/tests/contextAwareModels/ContextTestModel.d.ts +13 -0
- package/dist/models/tests/contextAwareModels/ContextTestModel.js +47 -2
- package/dist/repository/definition.d.ts +36 -0
- package/dist/repository/definition.js +121 -2
- package/dist/repository/entries.d.ts +13 -0
- package/dist/repository/entries.js +92 -2
- package/dist/repository/utils/formatValues.d.ts +3 -0
- package/dist/repository/utils/formatValues.js +16 -2
- package/dist/repository/validator.d.ts +21 -0
- package/dist/repository/validator.js +62 -2
- package/dist/repository/value.d.ts +28 -0
- package/dist/repository/value.js +124 -2
- package/dist/scopes/filter.d.ts +29 -22
- package/dist/scopes/filter.js +75 -2
- package/dist/scopes/helpers/filter.helpers.d.ts +40 -15
- package/dist/scopes/helpers/filter.helpers.js +183 -25
- package/dist/scopes/index.d.ts +2 -0
- package/dist/scopes/index.js +6 -1
- package/dist/tests/api/test-api.d.ts +2 -0
- package/dist/tests/api/test-api.js +38 -0
- package/dist/tests/functional/searching/index.d.ts +8 -0
- package/dist/tests/functional/searching/index.js +44 -0
- package/dist/tests/helpers/commonHooks.d.ts +6 -0
- package/dist/tests/helpers/commonHooks.js +62 -0
- package/dist/tests/helpers/database-config.d.ts +16 -0
- package/dist/tests/helpers/database-config.js +17 -0
- package/dist/tests/helpers/index.d.ts +7 -0
- package/dist/tests/helpers/index.js +33 -0
- package/dist/tests/mocks/definition.mock.d.ts +48 -0
- package/dist/tests/mocks/definition.mock.js +78 -0
- package/dist/tests/mocks/events.mock.d.ts +4 -0
- package/dist/tests/mocks/events.mock.js +21 -0
- package/dist/tests/mocks/testModel.d.ts +12 -0
- package/dist/tests/mocks/testModel.js +35 -0
- package/dist/types/definition/index.d.ts +25 -0
- package/dist/types/definition/index.js +2 -0
- package/dist/types/entries/index.d.ts +25 -0
- package/dist/types/entries/index.js +2 -0
- package/dist/types/index.d.ts +46 -45
- package/dist/types/index.js +2 -0
- package/dist/types/value/index.d.ts +15 -0
- package/dist/types/value/index.js +2 -0
- package/dist/utils/constants/index.d.ts +17 -20
- package/dist/utils/constants/index.js +22 -2
- package/dist/utils/db/index.d.ts +4 -0
- package/dist/utils/db/index.js +24 -2
- package/dist/utils/helpers/index.d.ts +23 -28
- package/dist/utils/helpers/index.js +40 -2
- package/dist/utils/init.d.ts +7 -0
- package/dist/utils/init.js +111 -2
- package/dist/utils/logger/index.d.ts +3 -0
- package/dist/utils/logger/index.js +42 -2
- package/dist/utils/scopeAttributes.d.ts +2 -0
- package/dist/utils/scopeAttributes.js +11 -2
- package/dist/utils/validations/index.d.ts +8 -0
- package/dist/utils/validations/index.js +41 -2
- package/dist/utils/validations/schema/custom-fields.d.ts +2 -6
- package/dist/utils/validations/schema/custom-fields.js +9 -2
- package/dist/utils/validations/schema/validator-schema.d.ts +9 -0
- package/dist/utils/validations/schema/validator-schema.js +95 -2
- package/dist/utils/validations/type.d.ts +15 -0
- package/dist/utils/validations/type.js +2 -0
- package/dist/utils/validations/validators/index.d.ts +14 -0
- package/dist/utils/validations/validators/index.js +40 -2
- package/dist/utils/validations/validators/select.validator.d.ts +5 -0
- package/dist/utils/validations/validators/select.validator.js +12 -2
- package/dist/utils/validations/validators/status.validator.d.ts +12 -0
- package/dist/utils/validations/validators/status.validator.js +15 -2
- package/package.json +39 -40
- package/src/api/index.ts +10 -0
- package/src/api/v1/definition/index.ts +104 -0
- package/src/api/v1/definition/validations.ts +75 -0
- package/src/api/v1/errors.ts +13 -0
- package/src/api/v1/index.ts +11 -0
- package/src/api/v1/validator/index.ts +141 -0
- package/src/api/v1/validator/validations.ts +38 -0
- package/src/errors/index.ts +70 -0
- package/src/events/index.ts +63 -0
- package/src/hooks/create.ts +81 -0
- package/src/hooks/enrich.ts +255 -0
- package/src/hooks/find.ts +27 -0
- package/src/hooks/hooks.ts +482 -0
- package/src/hooks/index.ts +20 -0
- package/src/hooks/update.ts +55 -0
- package/src/hooks/utils/updateInstanceValues.ts +63 -0
- package/src/hooks/workaround.ts +47 -0
- package/src/index.ts +52 -0
- package/src/models/CustomFieldDefinition.ts +162 -0
- package/src/models/CustomFieldEntries.ts +81 -0
- package/src/models/CustomFieldValue.ts +118 -0
- package/src/models/CustomValidator.ts +78 -0
- package/src/models/index.ts +165 -0
- package/src/models/tests/AssociatedTestModel.ts +57 -0
- package/src/models/tests/TestModel.ts +54 -0
- package/src/models/tests/contextAwareModels/ContextAwareTestModel.ts +43 -0
- package/src/models/tests/contextAwareModels/ContextTestModel.ts +38 -0
- package/src/repository/definition.ts +175 -0
- package/src/repository/entries.ts +88 -0
- package/src/repository/utils/formatValues.ts +14 -0
- package/src/repository/validator.ts +104 -0
- package/src/repository/value.ts +116 -0
- package/src/scopes/filter.ts +100 -0
- package/src/scopes/helpers/filter.helpers.ts +227 -0
- package/src/scopes/index.ts +6 -0
- package/src/tests/api/test-api.ts +40 -0
- package/src/tests/functional/searching/index.ts +39 -0
- package/src/tests/helpers/commonHooks.ts +43 -0
- package/src/tests/helpers/database-config.ts +15 -0
- package/src/tests/helpers/index.ts +35 -0
- package/src/tests/mocks/definition.mock.ts +84 -0
- package/src/tests/mocks/events.mock.ts +21 -0
- package/src/tests/mocks/testModel.ts +37 -0
- package/src/types/definition/index.ts +24 -0
- package/src/types/entries/index.ts +27 -0
- package/src/types/index.ts +52 -0
- package/src/types/value/index.ts +14 -0
- package/src/utils/constants/index.ts +25 -0
- package/src/utils/db/index.ts +21 -0
- package/src/utils/helpers/index.ts +66 -0
- package/src/utils/init.ts +120 -0
- package/src/utils/logger/index.ts +14 -0
- package/src/utils/scopeAttributes.ts +12 -0
- package/src/utils/validations/index.ts +46 -0
- package/src/utils/validations/schema/README.md +93 -0
- package/src/utils/validations/schema/custom-fields.ts +8 -0
- package/src/utils/validations/schema/validator-schema.ts +106 -0
- package/src/utils/validations/type.ts +20 -0
- package/src/utils/validations/validators/index.ts +38 -0
- package/src/utils/validations/validators/select.validator.ts +12 -0
- package/src/utils/validations/validators/status.validator.ts +22 -0
- package/tsconfig.build.json +7 -0
- package/tsconfig.json +16 -0
- package/dist/_virtual/_@oxc-project_runtime@0.101.0/helpers/decorate.cjs +0 -1
- package/dist/_virtual/_@oxc-project_runtime@0.101.0/helpers/decorate.js +0 -1
- package/dist/_virtual/_@oxc-project_runtime@0.101.0/helpers/decorateMetadata.cjs +0 -1
- package/dist/_virtual/_@oxc-project_runtime@0.101.0/helpers/decorateMetadata.js +0 -1
- package/dist/_virtual/rolldown_runtime.cjs +0 -1
- package/dist/api/index.cjs +0 -2
- package/dist/api/index.cjs.map +0 -1
- package/dist/api/index.js.map +0 -1
- package/dist/api/v1/definition/index.cjs +0 -2
- package/dist/api/v1/definition/index.cjs.map +0 -1
- package/dist/api/v1/definition/index.js.map +0 -1
- package/dist/api/v1/definition/validations.cjs +0 -2
- package/dist/api/v1/definition/validations.cjs.map +0 -1
- package/dist/api/v1/definition/validations.js.map +0 -1
- package/dist/api/v1/errors.cjs +0 -2
- package/dist/api/v1/errors.cjs.map +0 -1
- package/dist/api/v1/errors.js.map +0 -1
- package/dist/api/v1/index.cjs +0 -2
- package/dist/api/v1/index.cjs.map +0 -1
- package/dist/api/v1/index.js.map +0 -1
- package/dist/api/v1/validator/index.cjs +0 -2
- package/dist/api/v1/validator/index.cjs.map +0 -1
- package/dist/api/v1/validator/index.js.map +0 -1
- package/dist/api/v1/validator/validations.cjs +0 -2
- package/dist/api/v1/validator/validations.cjs.map +0 -1
- package/dist/api/v1/validator/validations.d.cts +0 -23
- package/dist/api/v1/validator/validations.js.map +0 -1
- package/dist/errors/index.cjs +0 -3
- package/dist/errors/index.cjs.map +0 -1
- package/dist/errors/index.js.map +0 -1
- package/dist/events/index.cjs +0 -2
- package/dist/events/index.cjs.map +0 -1
- package/dist/events/index.js.map +0 -1
- package/dist/hooks/enrich.cjs +0 -2
- package/dist/hooks/enrich.cjs.map +0 -1
- package/dist/hooks/enrich.js.map +0 -1
- package/dist/hooks/find.cjs +0 -2
- package/dist/hooks/find.cjs.map +0 -1
- package/dist/hooks/find.js.map +0 -1
- package/dist/hooks/hooks.cjs +0 -2
- package/dist/hooks/hooks.cjs.map +0 -1
- package/dist/hooks/hooks.js.map +0 -1
- package/dist/hooks/index.cjs +0 -1
- package/dist/hooks/utils/updateInstanceValues.cjs +0 -2
- package/dist/hooks/utils/updateInstanceValues.cjs.map +0 -1
- package/dist/hooks/utils/updateInstanceValues.js.map +0 -1
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -23
- package/dist/index.js.map +0 -1
- package/dist/models/CustomFieldDefinition.cjs +0 -2
- package/dist/models/CustomFieldDefinition.cjs.map +0 -1
- package/dist/models/CustomFieldDefinition.d.cts +0 -31
- package/dist/models/CustomFieldDefinition.js.map +0 -1
- package/dist/models/CustomFieldEntries.cjs +0 -2
- package/dist/models/CustomFieldEntries.cjs.map +0 -1
- package/dist/models/CustomFieldEntries.d.cts +0 -16
- package/dist/models/CustomFieldEntries.js.map +0 -1
- package/dist/models/CustomFieldValue.cjs +0 -2
- package/dist/models/CustomFieldValue.cjs.map +0 -1
- package/dist/models/CustomFieldValue.d.cts +0 -22
- package/dist/models/CustomFieldValue.js.map +0 -1
- package/dist/models/CustomValidator.cjs +0 -2
- package/dist/models/CustomValidator.cjs.map +0 -1
- package/dist/models/CustomValidator.d.cts +0 -19
- package/dist/models/CustomValidator.js.map +0 -1
- package/dist/models/index.cjs +0 -2
- package/dist/models/index.cjs.map +0 -1
- package/dist/models/index.d.cts +0 -6
- package/dist/models/index.js.map +0 -1
- package/dist/models/tests/AssociatedTestModel.cjs +0 -2
- package/dist/models/tests/AssociatedTestModel.cjs.map +0 -1
- package/dist/models/tests/AssociatedTestModel.js.map +0 -1
- package/dist/models/tests/TestModel.cjs +0 -2
- package/dist/models/tests/TestModel.cjs.map +0 -1
- package/dist/models/tests/TestModel.js.map +0 -1
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.cjs +0 -2
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.cjs.map +0 -1
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.js.map +0 -1
- package/dist/models/tests/contextAwareModels/ContextTestModel.cjs +0 -2
- package/dist/models/tests/contextAwareModels/ContextTestModel.cjs.map +0 -1
- package/dist/models/tests/contextAwareModels/ContextTestModel.js.map +0 -1
- package/dist/repository/definition.cjs +0 -2
- package/dist/repository/definition.cjs.map +0 -1
- package/dist/repository/definition.js.map +0 -1
- package/dist/repository/entries.cjs +0 -2
- package/dist/repository/entries.cjs.map +0 -1
- package/dist/repository/entries.js.map +0 -1
- package/dist/repository/utils/formatValues.cjs +0 -2
- package/dist/repository/utils/formatValues.cjs.map +0 -1
- package/dist/repository/utils/formatValues.js.map +0 -1
- package/dist/repository/validator.cjs +0 -2
- package/dist/repository/validator.cjs.map +0 -1
- package/dist/repository/validator.js.map +0 -1
- package/dist/repository/value.cjs +0 -2
- package/dist/repository/value.cjs.map +0 -1
- package/dist/repository/value.js.map +0 -1
- package/dist/scopes/filter.cjs +0 -2
- package/dist/scopes/filter.cjs.map +0 -1
- package/dist/scopes/filter.d.cts +0 -23
- package/dist/scopes/filter.js.map +0 -1
- package/dist/scopes/helpers/filter.helpers.cjs +0 -46
- package/dist/scopes/helpers/filter.helpers.cjs.map +0 -1
- package/dist/scopes/helpers/filter.helpers.d.cts +0 -17
- package/dist/scopes/helpers/filter.helpers.js.map +0 -1
- package/dist/scopes/index.cjs +0 -1
- package/dist/types/index.d.cts +0 -45
- package/dist/utils/constants/index.cjs +0 -2
- package/dist/utils/constants/index.cjs.map +0 -1
- package/dist/utils/constants/index.d.cts +0 -22
- package/dist/utils/constants/index.js.map +0 -1
- package/dist/utils/db/index.cjs +0 -2
- package/dist/utils/db/index.cjs.map +0 -1
- package/dist/utils/db/index.js.map +0 -1
- package/dist/utils/helpers/index.cjs +0 -2
- package/dist/utils/helpers/index.cjs.map +0 -1
- package/dist/utils/helpers/index.d.cts +0 -31
- package/dist/utils/helpers/index.js.map +0 -1
- package/dist/utils/init.cjs +0 -2
- package/dist/utils/init.cjs.map +0 -1
- package/dist/utils/init.js.map +0 -1
- package/dist/utils/logger/index.cjs +0 -2
- package/dist/utils/logger/index.cjs.map +0 -1
- package/dist/utils/logger/index.js.map +0 -1
- package/dist/utils/scopeAttributes.cjs +0 -2
- package/dist/utils/scopeAttributes.cjs.map +0 -1
- package/dist/utils/scopeAttributes.js.map +0 -1
- package/dist/utils/validations/index.cjs +0 -2
- package/dist/utils/validations/index.cjs.map +0 -1
- package/dist/utils/validations/index.js.map +0 -1
- package/dist/utils/validations/schema/custom-fields.cjs +0 -2
- package/dist/utils/validations/schema/custom-fields.cjs.map +0 -1
- package/dist/utils/validations/schema/custom-fields.d.cts +0 -7
- package/dist/utils/validations/schema/custom-fields.js.map +0 -1
- package/dist/utils/validations/schema/validator-schema.cjs +0 -2
- package/dist/utils/validations/schema/validator-schema.cjs.map +0 -1
- package/dist/utils/validations/schema/validator-schema.js.map +0 -1
- package/dist/utils/validations/validators/index.cjs +0 -2
- package/dist/utils/validations/validators/index.cjs.map +0 -1
- package/dist/utils/validations/validators/index.js.map +0 -1
- package/dist/utils/validations/validators/select.validator.cjs +0 -2
- package/dist/utils/validations/validators/select.validator.cjs.map +0 -1
- package/dist/utils/validations/validators/select.validator.js.map +0 -1
- package/dist/utils/validations/validators/status.validator.cjs +0 -2
- package/dist/utils/validations/validators/status.validator.cjs.map +0 -1
- package/dist/utils/validations/validators/status.validator.js.map +0 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ValidationError } from 'joi';
|
|
2
|
+
|
|
3
|
+
export interface CustomFieldEntriesDTO {
|
|
4
|
+
modelId: string;
|
|
5
|
+
entityId: string;
|
|
6
|
+
modelType: string;
|
|
7
|
+
/**
|
|
8
|
+
* A collection of custom fields where each key is the name of a CustomFieldDefinition and
|
|
9
|
+
* each value is the value of that custom field for this specific `modelId`.
|
|
10
|
+
*
|
|
11
|
+
* Example:
|
|
12
|
+
* {
|
|
13
|
+
* "vehicleColor": "Red",
|
|
14
|
+
* "vehicleType": "premium",
|
|
15
|
+
* "isActive": true
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
customFields: Record<string, any>;
|
|
19
|
+
createdAt?: Date;
|
|
20
|
+
updatedAt?: Date;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface EntriesValidationError {
|
|
24
|
+
value: any,
|
|
25
|
+
fieldDefinitionName: string,
|
|
26
|
+
joiValidationError: ValidationError
|
|
27
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { IncludeOptions, Transaction } from 'sequelize';
|
|
2
|
+
import type { ModelCtor, Sequelize } from 'sequelize-typescript';
|
|
3
|
+
import type { getUser as GetUserType } from '@autofleet/zehut';
|
|
4
|
+
import type CustomFieldDefinition from '../models/CustomFieldDefinition';
|
|
5
|
+
import type CustomValidator from '../models/CustomValidator';
|
|
6
|
+
|
|
7
|
+
export type ModelFetcher = (name: string) => any;
|
|
8
|
+
|
|
9
|
+
export interface TransactionOptions extends Record<string, any> {
|
|
10
|
+
transaction?: Transaction & {
|
|
11
|
+
definitionCache?: Map<string, CustomFieldDefinition[]>;
|
|
12
|
+
validationsCache?: Map<string, CustomValidator[]>;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type ModelOptions = {
|
|
17
|
+
/**
|
|
18
|
+
* Include options for the model
|
|
19
|
+
*/
|
|
20
|
+
include?: (entityIds: string[]) => IncludeOptions[];
|
|
21
|
+
/**
|
|
22
|
+
* Custom association for the model
|
|
23
|
+
* @param model - The model to associate with
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
customAssociation?: (model: ModelCtor) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Whether to use the entity id from the instance per scope attribute
|
|
29
|
+
*/
|
|
30
|
+
useEntityIdFromInclude?: boolean
|
|
31
|
+
/**
|
|
32
|
+
* Which attributes to include in the model
|
|
33
|
+
*/
|
|
34
|
+
attributes?: string[];
|
|
35
|
+
}
|
|
36
|
+
export type Models = {
|
|
37
|
+
name: string;
|
|
38
|
+
scopeAttributes: any[];
|
|
39
|
+
modelOptions?: ModelOptions;
|
|
40
|
+
creationWebhookHandler?: (instance: any) => any;
|
|
41
|
+
updateWebhookHandler?: (instance: any) => any;
|
|
42
|
+
deletionWebhookHandler?: (instance: any) => any;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type CustomFieldOptions = {
|
|
46
|
+
models: Models[];
|
|
47
|
+
databaseConfig: any;
|
|
48
|
+
getUser: typeof GetUserType;
|
|
49
|
+
sequelize?: Sequelize;
|
|
50
|
+
useCustomFieldsEntries?: boolean;
|
|
51
|
+
useValidators?: boolean;
|
|
52
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface CustomFieldValueDTO {
|
|
2
|
+
id?: string;
|
|
3
|
+
modelId: string;
|
|
4
|
+
customFieldDefinitionId: string;
|
|
5
|
+
value: any;
|
|
6
|
+
createdAt?: Date;
|
|
7
|
+
updatedAt?: Date;
|
|
8
|
+
deletedAt?: Date;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type ValuesToUpdate = {[name: string]: any };
|
|
12
|
+
export type CreateCustomFieldValue = Omit<CustomFieldValueDTO, 'id'>;
|
|
13
|
+
export type UpdateCustomFieldValue = ValuesToUpdate;
|
|
14
|
+
export type BulkUpdateCustomFieldValue = Partial<CustomFieldValueDTO>[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { customFields } from '@autofleet/common-types';
|
|
2
|
+
|
|
3
|
+
const { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;
|
|
4
|
+
|
|
5
|
+
export const supportedEntities = ['businessModelId', 'fleetId', 'demandSourceId'];
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Supported custom field types
|
|
9
|
+
*/
|
|
10
|
+
export enum CustomFieldDefinitionType {
|
|
11
|
+
NUMBER = 'number',
|
|
12
|
+
BOOLEAN = 'boolean',
|
|
13
|
+
DATE = 'date',
|
|
14
|
+
DATETIME = 'datetime',
|
|
15
|
+
TEXT = 'text',
|
|
16
|
+
IMAGE = 'image',
|
|
17
|
+
SELECT = 'select',
|
|
18
|
+
STATUS = 'status',
|
|
19
|
+
FILE = 'file',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
/** @deprecated Use the value from `@autofleet/common-types` instead */
|
|
24
|
+
CUSTOM_FIELDS_FILTER_SCOPE,
|
|
25
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Sequelize } from 'sequelize-typescript';
|
|
2
|
+
import { QueryTypes } from 'sequelize';
|
|
3
|
+
|
|
4
|
+
export default (databaseConfig: any): Sequelize => {
|
|
5
|
+
const ENV_DEV = 'test';
|
|
6
|
+
const env: string = process.env.NODE_ENV || ENV_DEV;
|
|
7
|
+
const config = databaseConfig[env];
|
|
8
|
+
let sequelize: Sequelize;
|
|
9
|
+
if (config.use_env_variable) {
|
|
10
|
+
sequelize = new Sequelize(process.env[config.use_env_variable], config);
|
|
11
|
+
} else {
|
|
12
|
+
sequelize = new Sequelize(config.database, config.username, config.password, config);
|
|
13
|
+
}
|
|
14
|
+
return sequelize;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const createSequelizeMeta = (sequelize: Sequelize) => sequelize.query(`
|
|
18
|
+
CREATE TABLE IF NOT EXISTS "SequelizeMeta" (
|
|
19
|
+
name character varying(255) PRIMARY KEY
|
|
20
|
+
);
|
|
21
|
+
`, { type: QueryTypes.SELECT });
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* eslint-disable import/prefer-default-export */
|
|
2
|
+
import { type WhereOptions, Op, type BindOrReplacements } from 'sequelize';
|
|
3
|
+
import { type ModelStatic, Sequelize } from 'sequelize-typescript';
|
|
4
|
+
import { randomInt } from 'node:crypto';
|
|
5
|
+
import { CustomFieldDefinitionType } from '../constants';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Builds a WHERE clause and replacements for free-text search by custom fields.
|
|
9
|
+
*
|
|
10
|
+
* This function constructs a WHERE clause and replacement bindings that allow searching
|
|
11
|
+
* for a given term within custom fields associated with a specific model type and entity ID.
|
|
12
|
+
* The WHERE clause and replacements are designed to be added to the main query.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} searchTerm - The term to search for within custom fields.
|
|
15
|
+
* @param {ModelStatic} model - The Sequelize model representing the entity type to search for.
|
|
16
|
+
* @param {string} entityId - The entity ID to filter the custom fields by.
|
|
17
|
+
* @param {CustomFieldDefinitionType[]} excludedCustomFieldsTypes - An array of custom field types
|
|
18
|
+
* to exclude from the search
|
|
19
|
+
*
|
|
20
|
+
* @returns {CustomFieldsSearchPayload} - An object containing the WHERE clause and replacements
|
|
21
|
+
* for Sequelize.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
interface CustomFieldsSearchPayload {
|
|
25
|
+
where: WhereOptions;
|
|
26
|
+
replacements: BindOrReplacements;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const generateRandomString = (length = 5): string => {
|
|
30
|
+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
31
|
+
return Array.from({ length }, () => characters.charAt(randomInt(characters.length))).join('');
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const generateCustomFieldSearchQueryPayload = (
|
|
35
|
+
searchTerm: string,
|
|
36
|
+
model: ModelStatic,
|
|
37
|
+
entityId: string,
|
|
38
|
+
customFieldsTypesToExclude: CustomFieldDefinitionType[] = [
|
|
39
|
+
CustomFieldDefinitionType.DATETIME,
|
|
40
|
+
CustomFieldDefinitionType.DATE,
|
|
41
|
+
],
|
|
42
|
+
): CustomFieldsSearchPayload => {
|
|
43
|
+
const excludedTypesString = customFieldsTypesToExclude.map((type) => `'${type}'`).join(',');
|
|
44
|
+
|
|
45
|
+
const subQuery = 'EXISTS ('
|
|
46
|
+
+ ' SELECT 1'
|
|
47
|
+
+ ' FROM "custom_field_values" AS "cv"'
|
|
48
|
+
+ ' INNER JOIN custom_field_definitions AS cd '
|
|
49
|
+
+ ` ON cd.entity_id = '${entityId}'`
|
|
50
|
+
+ ' AND cv.custom_field_definition_id = cd.id'
|
|
51
|
+
+ ` AND cd.model_type = '${model.name}'`
|
|
52
|
+
+ ` ${excludedTypesString ? `AND cd.field_type NOT IN (${excludedTypesString})` : ''}`
|
|
53
|
+
+ ' WHERE'
|
|
54
|
+
+ ' "cv"."deleted_at" IS NULL'
|
|
55
|
+
+ ` AND "cv"."model_id" = "${model.name}"."id"`
|
|
56
|
+
+ ' AND CAST("cv"."value" AS TEXT) ILIKE :searchTerm)';
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
where: {
|
|
60
|
+
[Op.or]: [
|
|
61
|
+
Sequelize.where(Sequelize.literal(subQuery), true),
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
replacements: { searchTerm: `%${searchTerm}%` },
|
|
65
|
+
};
|
|
66
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { DataTypes } from 'sequelize';
|
|
2
|
+
import type { ModelCtor } from 'sequelize-typescript';
|
|
3
|
+
import { customFields } from '@autofleet/common-types';
|
|
4
|
+
import { CUSTOM_FIELDS_SORT_SCOPE } from '@autofleet/common-types/lib/custom-fields';
|
|
5
|
+
import {
|
|
6
|
+
CustomFieldDefinition,
|
|
7
|
+
CustomFieldValue,
|
|
8
|
+
} from '../models';
|
|
9
|
+
import {
|
|
10
|
+
beforeFind,
|
|
11
|
+
enrichResults,
|
|
12
|
+
beforeBulkUpdate,
|
|
13
|
+
beforeUpdate,
|
|
14
|
+
beforeCreate,
|
|
15
|
+
beforeBulkCreate,
|
|
16
|
+
} from '../hooks';
|
|
17
|
+
import { customFieldsFilterScope } from '../scopes';
|
|
18
|
+
import logger from './logger';
|
|
19
|
+
import type { CustomFieldOptions, ModelFetcher, Models } from '../types';
|
|
20
|
+
import { customFieldsSortScope } from '../scopes/filter';
|
|
21
|
+
|
|
22
|
+
const { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;
|
|
23
|
+
|
|
24
|
+
export const addHooks = (
|
|
25
|
+
models: Models[],
|
|
26
|
+
getModel: ModelFetcher,
|
|
27
|
+
sadotOptions: { useCustomFieldsEntries: boolean } = { useCustomFieldsEntries: false },
|
|
28
|
+
): void => {
|
|
29
|
+
models.forEach(async ({
|
|
30
|
+
name, scopeAttributes, modelOptions,
|
|
31
|
+
}) => {
|
|
32
|
+
try {
|
|
33
|
+
const model = getModel(name);
|
|
34
|
+
if (!model) {
|
|
35
|
+
logger.warn('sadot - tried to addHooks to a model that does not exist yet', {
|
|
36
|
+
name,
|
|
37
|
+
scopeAttributes,
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
model.rawAttributes.customFields = {
|
|
42
|
+
type: DataTypes.VIRTUAL,
|
|
43
|
+
};
|
|
44
|
+
model.refreshAttributes();
|
|
45
|
+
// TODO: Uncomment after tests are passed
|
|
46
|
+
// model.addHook('afterFind', workaround);
|
|
47
|
+
model.addHook('beforeFind', 'sadot-beforeFind', beforeFind(scopeAttributes));
|
|
48
|
+
model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', beforeBulkCreate);
|
|
49
|
+
model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', beforeBulkUpdate);
|
|
50
|
+
model.addHook('beforeCreate', 'sadot-beforeCreate', beforeCreate(scopeAttributes, modelOptions, sadotOptions));
|
|
51
|
+
model.addHook('beforeUpdate', 'sadot-beforeUpdate', beforeUpdate(scopeAttributes, modelOptions, sadotOptions));
|
|
52
|
+
model.addHook('afterFind', 'sadot-afterFind', enrichResults(name, scopeAttributes, 'afterFind', modelOptions, sadotOptions));
|
|
53
|
+
model.addHook('afterUpdate', 'sadot-afterUpdate', enrichResults(name, scopeAttributes, null, modelOptions, sadotOptions));
|
|
54
|
+
model.addHook('afterCreate', 'sadot-afterCreate', enrichResults(name, scopeAttributes, null, modelOptions, sadotOptions));
|
|
55
|
+
} catch (e) {
|
|
56
|
+
logger.error(`Could not add custom fields hook to model ${name}. `, e);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const removeHooks = (models: Models[], getModel: ModelFetcher): void => {
|
|
62
|
+
models.forEach(async ({ name }) => {
|
|
63
|
+
try {
|
|
64
|
+
const model = getModel(name);
|
|
65
|
+
if (!model) return;
|
|
66
|
+
if (model.rawAttributes.customFields) {
|
|
67
|
+
delete model.rawAttributes.customFields;
|
|
68
|
+
model.refreshAttributes();
|
|
69
|
+
}
|
|
70
|
+
// model.removeHook('afterFind', 'sadot-workaround');
|
|
71
|
+
model.removeHook('beforeFind', 'sadot-beforeFind');
|
|
72
|
+
model.removeHook('beforeBulkCreate', 'sadot-beforeBulkCreate');
|
|
73
|
+
model.removeHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate');
|
|
74
|
+
model.removeHook('beforeCreate', 'sadot-beforeCreate');
|
|
75
|
+
model.removeHook('beforeUpdate', 'sadot-beforeUpdate');
|
|
76
|
+
model.removeHook('afterFind', 'sadot-afterFind');
|
|
77
|
+
model.removeHook('afterUpdate', 'sadot-afterUpdate');
|
|
78
|
+
} catch (e) {
|
|
79
|
+
logger.error(`Could not add custom fields hook to model ${name}. `, e);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Necessary associations for the {@link customFieldsFilterScope} scope
|
|
86
|
+
*/
|
|
87
|
+
const addAssociations = (model: ModelCtor, modelName: string, options: Pick<CustomFieldOptions, 'useCustomFieldsEntries'>): void => {
|
|
88
|
+
if (options?.useCustomFieldsEntries) return;
|
|
89
|
+
model.hasMany(CustomFieldValue, { foreignKey: 'modelId', as: 'customFieldValue' });
|
|
90
|
+
// TBC: maybe can be removed
|
|
91
|
+
CustomFieldValue.belongsTo(model, { foreignKey: 'modelId', as: modelName });
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const addScopes = (models: Models[], getModel: ModelFetcher, options: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false }): void => {
|
|
95
|
+
models.forEach(async ({ name, scopeAttributes }) => {
|
|
96
|
+
try {
|
|
97
|
+
const model = getModel(name);
|
|
98
|
+
if (!model) {
|
|
99
|
+
logger.warn('sadot - tried to addScopes to a model that does not exist yet', {
|
|
100
|
+
name,
|
|
101
|
+
scopeAttributes,
|
|
102
|
+
});
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// Necessary associations for the filtering scope
|
|
106
|
+
addAssociations(model, name, options);
|
|
107
|
+
// Add filter scope
|
|
108
|
+
model.addScope(CUSTOM_FIELDS_FILTER_SCOPE, customFieldsFilterScope(name, options));
|
|
109
|
+
model.addScope(CUSTOM_FIELDS_SORT_SCOPE, customFieldsSortScope(name, options));
|
|
110
|
+
} catch (e) {
|
|
111
|
+
logger.error(`Could not add custom fields scopes to model ${name}. `, e);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const applyCustomAssociation = (models: Models[]): void => {
|
|
117
|
+
models.forEach(({ modelOptions }) => {
|
|
118
|
+
modelOptions?.customAssociation?.(CustomFieldDefinition);
|
|
119
|
+
});
|
|
120
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Logger from '@autofleet/logger';
|
|
2
|
+
|
|
3
|
+
const logger = Logger();
|
|
4
|
+
|
|
5
|
+
export async function tryAddingTraceIdMiddleware() {
|
|
6
|
+
try {
|
|
7
|
+
const { outbreak } = await import('@autofleet/zehut');
|
|
8
|
+
logger.addContextMiddleware(() => ({ traceId: outbreak.getCurrentContextTraceId() }));
|
|
9
|
+
} catch (err) {
|
|
10
|
+
logger.error('Failed to add traceId middleware', { err });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default logger;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const mapAttributeToInstance = (scopeAttributes: string[], instance: any) =>
|
|
2
|
+
scopeAttributes.map((attr) => instance[attr]);
|
|
3
|
+
|
|
4
|
+
const applyScopeToInstance = (instance: any, scopeAttributes: string[]) => {
|
|
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
|
+
|
|
12
|
+
export default applyScopeToInstance;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { InvalidEntriesError } from '../../errors';
|
|
2
|
+
import type { CustomFieldDefinition } from '../../models';
|
|
3
|
+
import type { CustomFieldEntriesDTO } from '../../types/entries';
|
|
4
|
+
import type { CustomFieldDefinitionType } from '../constants';
|
|
5
|
+
import { validators } from './validators';
|
|
6
|
+
|
|
7
|
+
export const validateFieldType = (type: CustomFieldDefinitionType): boolean => Object.keys(validators).includes(type);
|
|
8
|
+
|
|
9
|
+
export const validateValue = (
|
|
10
|
+
value: unknown,
|
|
11
|
+
valueType: CustomFieldDefinitionType,
|
|
12
|
+
validation?: unknown,
|
|
13
|
+
) => {
|
|
14
|
+
const validator = validators[valueType];
|
|
15
|
+
return validator(value, validation);
|
|
16
|
+
/** TODO: Add validation for required fields
|
|
17
|
+
* @example
|
|
18
|
+
* if (validations.required && !value) {
|
|
19
|
+
* return false;
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
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;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ValidationResult } from 'joi';
|
|
2
|
+
import type { CustomFieldDefinitionType } from '../constants';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Validator is a function that validates a custom-field `value`,
|
|
6
|
+
* against a custom-field definition `validation object`.
|
|
7
|
+
* @returns `true` if the value is valid, `false` otherwise.
|
|
8
|
+
*/
|
|
9
|
+
export type Validator<Value, DefinitionValidationObject> = (
|
|
10
|
+
value: Value,
|
|
11
|
+
validation?: DefinitionValidationObject
|
|
12
|
+
) => ValidationResult;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Validators is a map of custom-field types to their respective validators.
|
|
16
|
+
* The key is the custom-field type, and the value is the validator function.
|
|
17
|
+
*/
|
|
18
|
+
export type Validators = {
|
|
19
|
+
[K in CustomFieldDefinitionType]: Validator<unknown, unknown>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Joi from 'joi';
|
|
2
|
+
import { CustomFieldDefinitionType } from '../../constants';
|
|
3
|
+
import type { Validators } from '../type';
|
|
4
|
+
import { validateSelect } from './select.validator';
|
|
5
|
+
import { validateStatus } from './status.validator';
|
|
6
|
+
|
|
7
|
+
type CustomValidationTypes = Extract<CustomFieldDefinitionType, 'select' | 'status'>;
|
|
8
|
+
/**
|
|
9
|
+
* Custom field types that must have custom validation.
|
|
10
|
+
* TODO: remove this after moving to schema-based validation
|
|
11
|
+
*/
|
|
12
|
+
export const CustomValidationTypes = {
|
|
13
|
+
[CustomFieldDefinitionType.SELECT]: CustomFieldDefinitionType.SELECT,
|
|
14
|
+
[CustomFieldDefinitionType.STATUS]: CustomFieldDefinitionType.STATUS,
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Validators for custom fields
|
|
19
|
+
*/
|
|
20
|
+
export const validators: Validators = {
|
|
21
|
+
[CustomFieldDefinitionType.SELECT]: validateSelect,
|
|
22
|
+
[CustomFieldDefinitionType.STATUS]: validateStatus,
|
|
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
|
+
.items(Joi.string().uri())
|
|
30
|
+
.validate(value),
|
|
31
|
+
[CustomFieldDefinitionType.FILE]: (value) => Joi.array().min(1).unique().items(Joi.object({
|
|
32
|
+
name: Joi.string().required(),
|
|
33
|
+
type: Joi.string(),
|
|
34
|
+
size: Joi.string(),
|
|
35
|
+
addedBy: Joi.string().uuid(),
|
|
36
|
+
}))
|
|
37
|
+
.validate(value),
|
|
38
|
+
};
|