@autofleet/sadot 1.2.0 → 1.2.1-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 +388 -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 -31
- 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 -19
- 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 -7
- 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 +27 -0
- package/dist/repository/validator.js +69 -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 -48
- 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 +112 -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 +39 -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 +464 -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 +116 -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 +122 -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.103.0/helpers/decorate.cjs +0 -1
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/decorate.js +0 -1
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/decorateMetadata.cjs +0 -1
- package/dist/_virtual/_@oxc-project_runtime@0.103.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 -33
- 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/CustomFieldModelTypeMap.cjs +0 -2
- package/dist/models/CustomFieldModelTypeMap.cjs.map +0 -1
- package/dist/models/CustomFieldModelTypeMap.d.cts +0 -15
- package/dist/models/CustomFieldModelTypeMap.d.ts +0 -15
- package/dist/models/CustomFieldModelTypeMap.js +0 -2
- package/dist/models/CustomFieldModelTypeMap.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 -21
- 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 -7
- 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 -48
- 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,227 @@
|
|
|
1
|
+
import type { WhereOptions } from 'sequelize';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type representing possible condition values.
|
|
5
|
+
* Currently supporting strings and arrays of strings.
|
|
6
|
+
* More types to be added (TBA).
|
|
7
|
+
*/
|
|
8
|
+
export type ConditionWithOperator = {
|
|
9
|
+
operator: string;
|
|
10
|
+
value: string;
|
|
11
|
+
};
|
|
12
|
+
export type ConditionValue = ConditionWithOperator | ConditionWithOperator[] | string | string[];
|
|
13
|
+
|
|
14
|
+
export type CustomFieldSort = {
|
|
15
|
+
field: string;
|
|
16
|
+
direction: 'ASC' | 'DESC';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type CustomFieldFilterOptions = {
|
|
20
|
+
where?: WhereOptions;
|
|
21
|
+
replacements?: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export enum SubQueryType {
|
|
25
|
+
VALUES = 'values',
|
|
26
|
+
ENTRIES = 'entries',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const isConditionStringArray = (input: any): input is string[] => Array.isArray(input) && typeof input[0] === 'string';
|
|
30
|
+
export const isBooleanString = (input: string): boolean => ['true', 'false'].includes(input.toString());
|
|
31
|
+
export const isDate = (input: any): input is Date => input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
|
|
32
|
+
|
|
33
|
+
export const castValueToJsonb = (value: string, type: string) => `to_jsonb(${value}::${type})`;
|
|
34
|
+
export const castValueToJsonbText = (value: string) => castValueToJsonb(value, 'text');
|
|
35
|
+
export const castValueToJsonbBoolean = (value: string) => castValueToJsonb(value, 'boolean');
|
|
36
|
+
export const castValueToJsonbNumeric = (value: string) => castValueToJsonb(value, 'numeric');
|
|
37
|
+
export const castIfNeeded = (columnName: string, conditionValue: string): string => {
|
|
38
|
+
if (isDate(conditionValue)) {
|
|
39
|
+
return castValueToJsonb(columnName, 'timestamp');
|
|
40
|
+
}
|
|
41
|
+
return columnName;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const AND_DELIMITER = ' AND ';
|
|
45
|
+
export const OR_DELIMITER = ' OR ';
|
|
46
|
+
export const CD_TABLE_ALIAS = 'cd';
|
|
47
|
+
export const CD_NAME_COLUMN = `${CD_TABLE_ALIAS}.name`;
|
|
48
|
+
export const CV_TABLE_ALIAS = 'cv';
|
|
49
|
+
export const CV_VALUE_COLUMN = `${CV_TABLE_ALIAS}.value`;
|
|
50
|
+
export const CE_TABLE_ALIAS = 'ce';
|
|
51
|
+
|
|
52
|
+
const getSingleConditionWithOperator = (value: any, operator: string, replacementKey: string, reverseReplacementsMap: Map<string, string>) => {
|
|
53
|
+
let type = 'text';
|
|
54
|
+
if (isDate(value)) {
|
|
55
|
+
type = 'date';
|
|
56
|
+
} else if (!Number.isNaN(Number(value))) {
|
|
57
|
+
type = 'numeric';
|
|
58
|
+
} else if (isBooleanString(value)) {
|
|
59
|
+
type = 'boolean';
|
|
60
|
+
}
|
|
61
|
+
const replacedValue = reverseReplacementsMap.get(value);
|
|
62
|
+
|
|
63
|
+
return `(jsonb_extract_path_text(${CE_TABLE_ALIAS}.custom_fields, :${replacementKey})::${type}) ${operator} :${replacedValue}`;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const getFormattedValue = (value: string) => {
|
|
67
|
+
let formattedValue: string | number | boolean = value;
|
|
68
|
+
if (isBooleanString(value)) {
|
|
69
|
+
formattedValue = value === 'true';
|
|
70
|
+
} else if (!Number.isNaN(Number(value))) {
|
|
71
|
+
formattedValue = Number(value);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return formattedValue;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const getJSONSubQuery = (value: string, key: string) => {
|
|
78
|
+
const formattedValue = getFormattedValue(value);
|
|
79
|
+
const jsonQuery = JSON.stringify({ [key]: formattedValue });
|
|
80
|
+
let jsonQueryWithStringBoolean;
|
|
81
|
+
if (isBooleanString(value)) {
|
|
82
|
+
jsonQueryWithStringBoolean = `${CE_TABLE_ALIAS}.custom_fields @> '${JSON.stringify({ [key]: value })}'`;
|
|
83
|
+
}
|
|
84
|
+
return `
|
|
85
|
+
(
|
|
86
|
+
${jsonQueryWithStringBoolean ? `${jsonQueryWithStringBoolean} OR` : ''}
|
|
87
|
+
${CE_TABLE_ALIAS}.custom_fields @> '${jsonQuery}'
|
|
88
|
+
)
|
|
89
|
+
`;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const getFilterCustomFieldsSubQuery = (queryType: SubQueryType, modelType: string, conditionsStrings: Array<string | boolean>): string => {
|
|
93
|
+
switch (queryType) {
|
|
94
|
+
case SubQueryType.VALUES:
|
|
95
|
+
return `
|
|
96
|
+
SELECT ${CV_TABLE_ALIAS}.model_id
|
|
97
|
+
FROM custom_field_values AS ${CV_TABLE_ALIAS}
|
|
98
|
+
INNER JOIN custom_field_definitions AS ${CD_TABLE_ALIAS} ON ${CV_TABLE_ALIAS}.custom_field_definition_id = ${CD_TABLE_ALIAS}.id
|
|
99
|
+
${AND_DELIMITER}${CD_TABLE_ALIAS}.model_type = '${modelType}'
|
|
100
|
+
WHERE ${conditionsStrings.join(OR_DELIMITER)}
|
|
101
|
+
${AND_DELIMITER}${CV_TABLE_ALIAS}.deleted_at IS NULL${AND_DELIMITER}${CD_TABLE_ALIAS}.deleted_at IS NULL
|
|
102
|
+
GROUP BY ${CV_TABLE_ALIAS}.model_id
|
|
103
|
+
HAVING COUNT(DISTINCT ${CV_TABLE_ALIAS}.custom_field_definition_id) = ${conditionsStrings.length}
|
|
104
|
+
`.replace(/\n/g, '');
|
|
105
|
+
case SubQueryType.ENTRIES:
|
|
106
|
+
return `
|
|
107
|
+
SELECT ce.model_id
|
|
108
|
+
FROM custom_field_entries ce
|
|
109
|
+
JOIN custom_field_definitions cfd
|
|
110
|
+
ON ce.model_type = cfd.model_type
|
|
111
|
+
AND ce.entity_id = cfd.entity_id
|
|
112
|
+
WHERE
|
|
113
|
+
cfd.deleted_at IS NULL AND
|
|
114
|
+
${conditionsStrings.join(AND_DELIMITER)}
|
|
115
|
+
`;
|
|
116
|
+
default:
|
|
117
|
+
throw new Error('Invalid query type');
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const getSortCustomFieldsSubQuery = (queryType: SubQueryType, modelType: string, replacementKey: string): string => {
|
|
122
|
+
switch (queryType) {
|
|
123
|
+
case SubQueryType.VALUES:
|
|
124
|
+
return `(
|
|
125
|
+
SELECT value
|
|
126
|
+
FROM (SELECT cv.model_id, cv.value
|
|
127
|
+
FROM custom_field_values AS cv INNER JOIN custom_field_definitions AS cd
|
|
128
|
+
ON cv.custom_field_definition_id = cd.id
|
|
129
|
+
${AND_DELIMITER}cd.model_type = '${modelType}'
|
|
130
|
+
WHERE cv.model_id = "${modelType}"."id"
|
|
131
|
+
${AND_DELIMITER}cd.name = :${replacementKey}
|
|
132
|
+
) AS CustomFieldAggregation
|
|
133
|
+
)
|
|
134
|
+
`;
|
|
135
|
+
case SubQueryType.ENTRIES:
|
|
136
|
+
return `(
|
|
137
|
+
SELECT
|
|
138
|
+
customFields.value
|
|
139
|
+
FROM
|
|
140
|
+
custom_field_entries AS ${CE_TABLE_ALIAS},
|
|
141
|
+
jsonb_each_text(custom_fields) AS customFields
|
|
142
|
+
WHERE
|
|
143
|
+
customFields.key = :${replacementKey}${AND_DELIMITER}
|
|
144
|
+
${CE_TABLE_ALIAS}.model_type = '${modelType}'${AND_DELIMITER}
|
|
145
|
+
${CE_TABLE_ALIAS}.model_id = "${modelType}"."id"
|
|
146
|
+
)
|
|
147
|
+
`;
|
|
148
|
+
default:
|
|
149
|
+
throw new Error('Invalid query type');
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export const formatConditionsForValues = (key: string, condition: ConditionValue, reverseReplacementsMap: Map<string, string>) => {
|
|
154
|
+
const replacementKey = reverseReplacementsMap.get(key);
|
|
155
|
+
if (!replacementKey) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const columnCondition = `(${CD_NAME_COLUMN} = :${replacementKey})`;
|
|
160
|
+
if (Array.isArray(condition)) {
|
|
161
|
+
if (condition.length === 0) {
|
|
162
|
+
// if empty array, the condition is ignored
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (isConditionStringArray(condition)) {
|
|
167
|
+
const values = condition.flatMap((v) => {
|
|
168
|
+
const valRandom = reverseReplacementsMap.get(v);
|
|
169
|
+
if (isBooleanString(v)) {
|
|
170
|
+
return [castValueToJsonbText(`:${valRandom}`), castValueToJsonbBoolean(`:${valRandom}`)];
|
|
171
|
+
}
|
|
172
|
+
if (!Number.isNaN(Number(v))) {
|
|
173
|
+
return castValueToJsonbNumeric(`:${valRandom}`);
|
|
174
|
+
}
|
|
175
|
+
return castValueToJsonbText(`:${valRandom}`);
|
|
176
|
+
}).join(',');
|
|
177
|
+
return `(${columnCondition}${AND_DELIMITER}${CV_VALUE_COLUMN} IN (${values}))`;
|
|
178
|
+
}
|
|
179
|
+
return condition.map((c) => {
|
|
180
|
+
const valRep = reverseReplacementsMap.get(c.value);
|
|
181
|
+
const valueAsJsonb = castValueToJsonbText(`:${valRep}`);
|
|
182
|
+
|
|
183
|
+
return `(${columnCondition}${AND_DELIMITER}${castIfNeeded(CV_VALUE_COLUMN, c.value)} ${c.operator} ${valueAsJsonb})`;
|
|
184
|
+
}).join(AND_DELIMITER);
|
|
185
|
+
}
|
|
186
|
+
if (typeof condition === 'string' || typeof condition === 'number') {
|
|
187
|
+
const conditionRep = reverseReplacementsMap.get(condition);
|
|
188
|
+
const valueAsJsonb = !Number.isNaN(Number(condition)) ? castValueToJsonbNumeric(`:${conditionRep}`) : castValueToJsonbText(`:${conditionRep}`);
|
|
189
|
+
const valueAsJsonbBoolean = isBooleanString(condition) ? `${OR_DELIMITER}${CV_VALUE_COLUMN} = ${castValueToJsonbBoolean(`:${conditionRep}`)}` : '';
|
|
190
|
+
return `(${columnCondition}${AND_DELIMITER}(${castIfNeeded(CV_VALUE_COLUMN, condition)} = ${valueAsJsonb}${valueAsJsonbBoolean}))`;
|
|
191
|
+
}
|
|
192
|
+
if (condition?.operator) {
|
|
193
|
+
const valueRep = reverseReplacementsMap.get(condition.value);
|
|
194
|
+
const valueAsJsonb = castValueToJsonbText(`:${valueRep}`);
|
|
195
|
+
return `( ${columnCondition}${AND_DELIMITER}${castIfNeeded(CV_VALUE_COLUMN, condition.value)} ${condition.operator} ${valueAsJsonb})`;
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export const formatConditionsForEntries = (key: string, condition: ConditionValue, reverseReplacementsMap: Map<string, string>) => {
|
|
201
|
+
const replacementKey = reverseReplacementsMap.get(key);
|
|
202
|
+
if (!replacementKey) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (Array.isArray(condition)) {
|
|
207
|
+
if (condition.length === 0) {
|
|
208
|
+
// if empty array, the condition is ignored
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (isConditionStringArray(condition)) {
|
|
213
|
+
const values = condition.map((value) => getJSONSubQuery(value, key)).join(`${OR_DELIMITER}\n`);
|
|
214
|
+
return `( ${values})`;
|
|
215
|
+
}
|
|
216
|
+
return condition.map((c) => getSingleConditionWithOperator(c.value, c.operator, replacementKey, reverseReplacementsMap)).join(AND_DELIMITER);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (typeof condition === 'string' || typeof condition === 'number') {
|
|
220
|
+
return getJSONSubQuery(condition, key);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (condition?.operator) {
|
|
224
|
+
return getSingleConditionWithOperator(condition.value, condition.operator, replacementKey, reverseReplacementsMap);
|
|
225
|
+
}
|
|
226
|
+
return false;
|
|
227
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { Router } from '@autofleet/node-common';
|
|
3
|
+
import logger from '../../utils/logger';
|
|
4
|
+
import { TestModel } from '../../models';
|
|
5
|
+
|
|
6
|
+
const app = express();
|
|
7
|
+
app.use(express.json());
|
|
8
|
+
const api = Router({ logger });
|
|
9
|
+
|
|
10
|
+
api.get('/v1/test-models', async (req, res) => {
|
|
11
|
+
const testModels = await TestModel.findAll();
|
|
12
|
+
return res.json(testModels);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
api.get('/v1/test-models/:testModelId', async (req, res) => {
|
|
16
|
+
const { params: { testModelId } } = req;
|
|
17
|
+
const testModel = await TestModel.findByPk(testModelId);
|
|
18
|
+
return res.json(testModel);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
api.post('/v1/test-models', async (req, res) => {
|
|
22
|
+
const { body } = req;
|
|
23
|
+
const testModel = await TestModel.create(body);
|
|
24
|
+
return res.json(testModel);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
api.patch('/v1/test-models/:testModelId', async (req, res) => {
|
|
28
|
+
const { body, params: { testModelId } } = req;
|
|
29
|
+
const testModel = await TestModel.update(body, {
|
|
30
|
+
where: {
|
|
31
|
+
id: testModelId,
|
|
32
|
+
},
|
|
33
|
+
returning: true,
|
|
34
|
+
});
|
|
35
|
+
return res.json(testModel[1][0]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
app.use('/api', api);
|
|
39
|
+
|
|
40
|
+
export default app;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { generateCustomFieldSearchQueryPayload } from '../../../index';
|
|
2
|
+
import * as DefinitionRepo from '../../../repository/definition';
|
|
3
|
+
import { createDefinition } from '../../mocks/definition.mock';
|
|
4
|
+
import { createTestModels } from '../../mocks/testModel';
|
|
5
|
+
import { TestModel } from '../../../models';
|
|
6
|
+
|
|
7
|
+
interface CustomFieldsSearchTestFlowInput {
|
|
8
|
+
fieldType: string;
|
|
9
|
+
fieldValue: string | number;
|
|
10
|
+
searchTerm: string;
|
|
11
|
+
expectedNumberOfQueryResults: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const customFieldsSearchTestFlow = async ({
|
|
15
|
+
fieldType,
|
|
16
|
+
fieldValue,
|
|
17
|
+
searchTerm,
|
|
18
|
+
expectedNumberOfQueryResults,
|
|
19
|
+
}: CustomFieldsSearchTestFlowInput): Promise<void> => {
|
|
20
|
+
const definition = createDefinition({ fieldType, name: 'coolDefinition' });
|
|
21
|
+
await DefinitionRepo.create({ ...definition });
|
|
22
|
+
const [testModel1] = await createTestModels(definition.entityId, 2);
|
|
23
|
+
await testModel1.update({ customFields: { [definition.name]: fieldValue } });
|
|
24
|
+
const models = await TestModel.findAndCountAll(
|
|
25
|
+
{
|
|
26
|
+
...generateCustomFieldSearchQueryPayload(
|
|
27
|
+
searchTerm,
|
|
28
|
+
TestModel,
|
|
29
|
+
definition.entityId,
|
|
30
|
+
),
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
expect(models.count).toBe(expectedNumberOfQueryResults);
|
|
34
|
+
if (expectedNumberOfQueryResults > 0) {
|
|
35
|
+
expect(models.rows[0].dataValues.id).toBe(testModel1.id);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default customFieldsSearchTestFlow;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Application } from 'express';
|
|
2
|
+
import type { Sequelize } from 'sequelize-typescript';
|
|
3
|
+
import useCustomFields from '../..';
|
|
4
|
+
import { cleanup, getModel } from '.';
|
|
5
|
+
import databaseConfig from './database-config';
|
|
6
|
+
import initDB, { createSequelizeMeta } from '../../utils/db';
|
|
7
|
+
import type { Models } from '../../types';
|
|
8
|
+
|
|
9
|
+
export function commonTestHooks(
|
|
10
|
+
app: Application | null = null,
|
|
11
|
+
models: Models[] = [{ name: 'TestModel', scopeAttributes: ['fleetId'] }],
|
|
12
|
+
options: { useCustomFieldsEntries: boolean, useValidators?: boolean } = {
|
|
13
|
+
useCustomFieldsEntries: false,
|
|
14
|
+
useValidators: false,
|
|
15
|
+
},
|
|
16
|
+
) {
|
|
17
|
+
let sequelize: Sequelize;
|
|
18
|
+
|
|
19
|
+
beforeAll(async () => {
|
|
20
|
+
sequelize = initDB(databaseConfig);
|
|
21
|
+
await createSequelizeMeta(sequelize);
|
|
22
|
+
await useCustomFields(app, getModel, {
|
|
23
|
+
models,
|
|
24
|
+
databaseConfig,
|
|
25
|
+
getUser: () => undefined,
|
|
26
|
+
sequelize,
|
|
27
|
+
useCustomFieldsEntries: options.useCustomFieldsEntries,
|
|
28
|
+
useValidators: options.useValidators,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
jest.clearAllMocks();
|
|
34
|
+
await cleanup({
|
|
35
|
+
useCustomFieldsEntries: options.useCustomFieldsEntries,
|
|
36
|
+
useValidators: options.useValidators,
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterAll(async () => {
|
|
41
|
+
await sequelize.close();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
test: {
|
|
3
|
+
username: process.env.DB_USERNAME || '',
|
|
4
|
+
password: process.env.DB_PASSWORD || null,
|
|
5
|
+
database: process.env.DB_NAME || 'postgres',
|
|
6
|
+
host: process.env.DB_HOST || '127.0.0.1',
|
|
7
|
+
port: process.env.DB_PORT || 5432,
|
|
8
|
+
dialect: process.env.DB_TYPE || 'postgres',
|
|
9
|
+
define: {
|
|
10
|
+
underscored: true,
|
|
11
|
+
underscoredAll: true,
|
|
12
|
+
},
|
|
13
|
+
logging: false,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { generateFilterReplacements } from '@autofleet/sheilta';
|
|
2
|
+
import {
|
|
3
|
+
ContextAwareTestModel, ContextTestModel, CustomFieldDefinition, CustomFieldEntries, CustomValidator, TestModel,
|
|
4
|
+
} from '../../models';
|
|
5
|
+
import type { CustomFieldOptions } from '../../types';
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
8
|
+
export const cleanup = async (options?: Pick<CustomFieldOptions, 'useCustomFieldsEntries' | 'useValidators'>): Promise<void> => {
|
|
9
|
+
if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development') {
|
|
10
|
+
await CustomFieldDefinition.unscoped().destroy({ where: {} });
|
|
11
|
+
await TestModel.destroy({ where: {} });
|
|
12
|
+
await ContextAwareTestModel.destroy({ where: {} });
|
|
13
|
+
await ContextTestModel.destroy({ where: {} });
|
|
14
|
+
|
|
15
|
+
if (options?.useCustomFieldsEntries) {
|
|
16
|
+
await CustomFieldEntries.unscoped().destroy({ where: {} });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Clean up validators if they were used
|
|
20
|
+
if (options?.useValidators) {
|
|
21
|
+
await CustomValidator.unscoped().destroy({ where: {} });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const getModel = (name: string) => {
|
|
27
|
+
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
|
|
28
|
+
const models = require('../../models');
|
|
29
|
+
return models[name];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const getReplacementMapWithScopeValue = (conditions: Record<string, any>) => ({
|
|
33
|
+
replacementsMap: generateFilterReplacements(Object.fromEntries(Object.entries(conditions).map(([key, value]) => [`customFields.${key}`, value]))),
|
|
34
|
+
scopeValue: conditions,
|
|
35
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import type { CreateCustomFieldDefinition, CustomFieldDefinitionDTO } from '../../types/definition';
|
|
3
|
+
|
|
4
|
+
export const contextAwareFieldDefinition = {
|
|
5
|
+
name: 'cool field',
|
|
6
|
+
modelType: 'ContextAwareTestModel',
|
|
7
|
+
fieldType: 'number',
|
|
8
|
+
entityType: 'fleetId',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const coolFieldDefinition: CreateCustomFieldDefinition = {
|
|
12
|
+
name: 'cool field',
|
|
13
|
+
modelType: 'TestModel',
|
|
14
|
+
fieldType: 'number',
|
|
15
|
+
entityId: randomUUID(),
|
|
16
|
+
entityType: 'fleetId',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const coolFieldDefinition2 = {
|
|
20
|
+
...coolFieldDefinition,
|
|
21
|
+
name: 'cool field2',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const coolFieldDefinition3 = {
|
|
25
|
+
...coolFieldDefinition,
|
|
26
|
+
name: 'cool field3',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const booleanField = (modelType: string): CreateCustomFieldDefinition => ({
|
|
30
|
+
name: 'shapeless',
|
|
31
|
+
modelType,
|
|
32
|
+
fieldType: 'boolean',
|
|
33
|
+
entityId: randomUUID(),
|
|
34
|
+
entityType: 'fleetId',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const selectField = (modelType: string, options): CreateCustomFieldDefinition => ({
|
|
38
|
+
name: 'choices',
|
|
39
|
+
modelType,
|
|
40
|
+
fieldType: 'select',
|
|
41
|
+
validation: options,
|
|
42
|
+
entityId: randomUUID(),
|
|
43
|
+
entityType: 'fleetId',
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export const statusField = (modelType: string, options): CreateCustomFieldDefinition => ({
|
|
47
|
+
name: 'lifecycle',
|
|
48
|
+
modelType,
|
|
49
|
+
fieldType: 'status',
|
|
50
|
+
validation: options,
|
|
51
|
+
entityId: randomUUID(),
|
|
52
|
+
entityType: 'fleetId',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export const fileField = (modelType: string): CreateCustomFieldDefinition => ({
|
|
56
|
+
name: 'file',
|
|
57
|
+
modelType,
|
|
58
|
+
fieldType: 'file',
|
|
59
|
+
entityId: randomUUID(),
|
|
60
|
+
entityType: 'fleetId',
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// eslint-disable-next-line max-len
|
|
64
|
+
export const createDefinition = (defaults: Partial<CustomFieldDefinitionDTO>): CreateCustomFieldDefinition => ({
|
|
65
|
+
name: defaults?.name || `def_${randomUUID()}`,
|
|
66
|
+
modelType: defaults?.modelType || 'TestModel',
|
|
67
|
+
fieldType: defaults?.fieldType || 'boolean',
|
|
68
|
+
entityId: defaults?.entityId || randomUUID(),
|
|
69
|
+
entityType: defaults?.entityType || 'fleetId',
|
|
70
|
+
...(defaults?.validation && { validation: defaults.validation }),
|
|
71
|
+
...(defaults?.defaultValue && { defaultValue: defaults.defaultValue }),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
export const createDefinitions = (
|
|
75
|
+
defaults: Partial<CustomFieldDefinitionDTO>,
|
|
76
|
+
length = 1,
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
78
|
+
): CreateCustomFieldDefinition[] => (Array(length).fill({}).map((_) => ({
|
|
79
|
+
name: defaults?.name || `def_${randomUUID()}`,
|
|
80
|
+
modelType: defaults?.modelType || 'TestModel',
|
|
81
|
+
fieldType: defaults?.fieldType || 'boolean',
|
|
82
|
+
entityId: defaults?.entityId || randomUUID(),
|
|
83
|
+
entityType: defaults?.entityType || 'fleetId',
|
|
84
|
+
})));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
|
2
|
+
|
|
3
|
+
export const mockEvent = (events: any, eventName, numberOfEvents) => {
|
|
4
|
+
events.sendObject = jest.fn().mockResolvedValue(true);
|
|
5
|
+
return [
|
|
6
|
+
events.sendObject,
|
|
7
|
+
(): void => {
|
|
8
|
+
const matchingEvents = events.sendObject.mock.calls.filter((call) => call[0] === eventName);
|
|
9
|
+
expect(matchingEvents.length).toEqual(numberOfEvents);
|
|
10
|
+
},
|
|
11
|
+
];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const mockDimCustomFieldDefinitionEvent = (events, numberOfEvents) =>
|
|
15
|
+
mockEvent(events, 'dim_custom_field_definition', numberOfEvents);
|
|
16
|
+
|
|
17
|
+
export const mockDimCustomFieldValueEvent = (events, numberOfEvents) =>
|
|
18
|
+
mockEvent(events, 'dim_custom_field_value', numberOfEvents);
|
|
19
|
+
|
|
20
|
+
export const mockDimCustomFieldEntriesEvent = (events, numberOfEvents) =>
|
|
21
|
+
mockEvent(events, 'dim_custom_field_entries', numberOfEvents);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { TestModel, AssociatedTestModel } from '../../models';
|
|
2
|
+
|
|
3
|
+
export const createTestModel = (payload = {}) => TestModel.create(payload);
|
|
4
|
+
|
|
5
|
+
export const createTestModels = (fleetId, total: number) => {
|
|
6
|
+
const models: Promise<TestModel>[] = [];
|
|
7
|
+
for (let i = 0; i < total; i += 1) {
|
|
8
|
+
models.push(createTestModel({ fleetId }));
|
|
9
|
+
}
|
|
10
|
+
return Promise.all<TestModel>(models);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const upsertTestModel = (payload) => TestModel.upsert(payload);
|
|
14
|
+
|
|
15
|
+
export const destroyTestModels = () => TestModel.destroy({ truncate: true });
|
|
16
|
+
|
|
17
|
+
export const getTestModel = (
|
|
18
|
+
id: string,
|
|
19
|
+
options = {},
|
|
20
|
+
): Promise<TestModel | null> => TestModel.findByPk(id, options);
|
|
21
|
+
|
|
22
|
+
export const getSomeTestModels = (
|
|
23
|
+
options = { limit: 1 },
|
|
24
|
+
): Promise<TestModel[]> => TestModel.findAll(options);
|
|
25
|
+
|
|
26
|
+
export const updateTestModel = (payload, query) => TestModel.update(payload, query);
|
|
27
|
+
|
|
28
|
+
// Associations
|
|
29
|
+
export const createTestModelWithAssociation = async () => {
|
|
30
|
+
const model = await TestModel.create({});
|
|
31
|
+
await AssociatedTestModel.create({ testModelId: model.id });
|
|
32
|
+
return model;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const getTestModelWithAssociation = (limit = 1) => TestModel.findAll({
|
|
36
|
+
limit, include: [AssociatedTestModel],
|
|
37
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface CustomFieldDefinitionDTO {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
displayName?: string;
|
|
5
|
+
validation?: any;
|
|
6
|
+
defaultValue?: any;
|
|
7
|
+
fieldType: string;
|
|
8
|
+
entityId: string;
|
|
9
|
+
entityType: string;
|
|
10
|
+
modelType: string;
|
|
11
|
+
required?: boolean;
|
|
12
|
+
description?: string;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
createdAt?: Date;
|
|
15
|
+
updatedAt?: Date;
|
|
16
|
+
deletedAt?: Date;
|
|
17
|
+
blockEditingFromUI?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export type CreateCustomFieldDefinition = Omit<CustomFieldDefinitionDTO, 'id'>;
|
|
20
|
+
export type UpdateCustomFieldDefinition = Partial<CreateCustomFieldDefinition>;
|
|
21
|
+
|
|
22
|
+
export type SerializedCustomFields = {
|
|
23
|
+
[name: string]: CustomFieldDefinitionDTO & { value: any };
|
|
24
|
+
};
|
|
@@ -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[]|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>[];
|