@autofleet/sadot 0.6.2-beta.0 → 0.6.2-temp-file-2
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/package.json +8 -8
- package/src/api/v1/definition/index.ts +1 -1
- package/src/api/v1/definition/validations.ts +14 -1
- package/src/hooks/create.ts +1 -1
- package/src/hooks/enrich.ts +4 -4
- package/src/hooks/update.ts +1 -1
- package/src/index.ts +2 -3
- package/src/models/CustomFieldDefinition.ts +7 -5
- package/src/models/CustomFieldValue.ts +1 -5
- package/src/repository/definition.ts +2 -5
- package/src/repository/value.ts +2 -2
- package/src/scopes/filter.ts +12 -31
- package/src/tests/functional/searching/index.ts +1 -1
- package/src/tests/mocks/definition.mock.ts +6 -8
- package/src/types/index.ts +2 -2
- package/src/utils/constants/index.ts +15 -1
- package/src/utils/helpers/index.ts +5 -5
- package/src/utils/init.ts +1 -1
- package/src/utils/validations/index.ts +18 -15
- package/src/utils/validations/{custom-fields.ts → schema/custom-fields.ts} +0 -1
- package/src/utils/validations/type.ts +14 -40
- package/src/utils/validations/validators/index.ts +39 -0
- package/src/utils/validations/validators/select.validator.ts +11 -0
- package/src/utils/validations/validators/status.validator.ts +18 -0
- package/dist/api/index.d.ts +0 -2
- package/dist/api/index.js +0 -11
- package/dist/api/v1/definition/index.d.ts +0 -2
- package/dist/api/v1/definition/index.js +0 -118
- package/dist/api/v1/definition/validations.d.ts +0 -2
- package/dist/api/v1/definition/validations.js +0 -36
- package/dist/api/v1/errors.d.ts +0 -2
- package/dist/api/v1/errors.js +0 -15
- package/dist/api/v1/index.d.ts +0 -2
- package/dist/api/v1/index.js +0 -10
- package/dist/errors/index.d.ts +0 -16
- package/dist/errors/index.js +0 -45
- package/dist/events/index.d.ts +0 -4
- package/dist/events/index.js +0 -47
- package/dist/hooks/create.d.ts +0 -10
- package/dist/hooks/create.js +0 -74
- package/dist/hooks/enrich.d.ts +0 -7
- package/dist/hooks/enrich.js +0 -126
- package/dist/hooks/find.d.ts +0 -1
- package/dist/hooks/find.js +0 -29
- package/dist/hooks/index.d.ts +0 -6
- package/dist/hooks/index.js +0 -18
- package/dist/hooks/update.d.ts +0 -10
- package/dist/hooks/update.js +0 -59
- package/dist/hooks/workaround.d.ts +0 -10
- package/dist/hooks/workaround.js +0 -37
- package/dist/index.d.ts +0 -14
- package/dist/index.js +0 -56
- package/dist/models/CustomFieldDefinition.d.ts +0 -23
- package/dist/models/CustomFieldDefinition.js +0 -165
- package/dist/models/CustomFieldValue.d.ts +0 -15
- package/dist/models/CustomFieldValue.js +0 -148
- package/dist/models/index.d.ts +0 -10
- package/dist/models/index.js +0 -93
- package/dist/models/tests/AssociatedTestModel.d.ts +0 -12
- package/dist/models/tests/AssociatedTestModel.js +0 -71
- package/dist/models/tests/TestModel.d.ts +0 -12
- package/dist/models/tests/TestModel.js +0 -69
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.d.ts +0 -10
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.js +0 -55
- package/dist/models/tests/contextAwareModels/ContextTestModel.d.ts +0 -13
- package/dist/models/tests/contextAwareModels/ContextTestModel.js +0 -47
- package/dist/repository/definition.d.ts +0 -20
- package/dist/repository/definition.js +0 -92
- package/dist/repository/value.d.ts +0 -28
- package/dist/repository/value.js +0 -117
- package/dist/scopes/filter.d.ts +0 -28
- package/dist/scopes/filter.js +0 -71
- package/dist/scopes/index.d.ts +0 -2
- package/dist/scopes/index.js +0 -6
- package/dist/tests/api/test-api.d.ts +0 -2
- package/dist/tests/api/test-api.js +0 -56
- package/dist/tests/functional/searching/index.d.ts +0 -8
- package/dist/tests/functional/searching/index.js +0 -44
- package/dist/tests/helpers/database-config.d.ts +0 -16
- package/dist/tests/helpers/database-config.js +0 -17
- package/dist/tests/helpers/index.d.ts +0 -2
- package/dist/tests/helpers/index.js +0 -20
- package/dist/tests/mocks/definition.mock.d.ts +0 -43
- package/dist/tests/mocks/definition.mock.js +0 -70
- package/dist/tests/mocks/events.mock.d.ts +0 -3
- package/dist/tests/mocks/events.mock.js +0 -19
- package/dist/tests/mocks/testModel.d.ts +0 -12
- package/dist/tests/mocks/testModel.js +0 -35
- package/dist/types/definition/index.d.ts +0 -23
- package/dist/types/definition/index.js +0 -2
- package/dist/types/index.d.ts +0 -31
- package/dist/types/index.js +0 -2
- package/dist/types/value/index.d.ts +0 -15
- package/dist/types/value/index.js +0 -2
- package/dist/utils/constants/index.d.ts +0 -5
- package/dist/utils/constants/index.js +0 -8
- package/dist/utils/db/index.d.ts +0 -4
- package/dist/utils/db/index.js +0 -24
- package/dist/utils/helpers/index.d.ts +0 -25
- package/dist/utils/helpers/index.js +0 -34
- package/dist/utils/init.d.ts +0 -5
- package/dist/utils/init.js +0 -106
- package/dist/utils/logger/index.d.ts +0 -2
- package/dist/utils/logger/index.js +0 -6
- package/dist/utils/scopeAttributes.d.ts +0 -2
- package/dist/utils/scopeAttributes.js +0 -11
- package/dist/utils/validations/custom-fields.d.ts +0 -3
- package/dist/utils/validations/custom-fields.js +0 -10
- package/dist/utils/validations/custom.d.ts +0 -15
- package/dist/utils/validations/custom.js +0 -42
- package/dist/utils/validations/index.d.ts +0 -2
- package/dist/utils/validations/index.js +0 -20
- package/dist/utils/validations/type.d.ts +0 -18
- package/dist/utils/validations/type.js +0 -50
- package/dist/utils/validations/validators.d.ts +0 -12
- package/dist/utils/validations/validators.js +0 -33
- package/src/utils/validations/custom.ts +0 -39
- package/src/utils/validations/validators.ts +0 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/sadot",
|
|
3
|
-
"version": "0.6.2-
|
|
3
|
+
"version": "0.6.2-temp-file-2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -45,18 +45,18 @@
|
|
|
45
45
|
"@types/express": "^4.17.17",
|
|
46
46
|
"@types/jest": "^27.0.9",
|
|
47
47
|
"@types/uuid": "^9.0.2",
|
|
48
|
-
"@typescript-eslint/eslint-plugin": "^4.
|
|
49
|
-
"@typescript-eslint/parser": "^4.
|
|
50
|
-
"eslint": "^7.
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "^6.4.0",
|
|
49
|
+
"@typescript-eslint/parser": "^6.4.0",
|
|
50
|
+
"eslint": "^7.32.0",
|
|
51
51
|
"eslint-config-airbnb-typescript": "^12.0.0",
|
|
52
52
|
"eslint-plugin-import": "^2.22.1",
|
|
53
|
-
"jest": "^
|
|
53
|
+
"jest": "^29.7.0",
|
|
54
|
+
"ts-jest": "^29.1.2",
|
|
54
55
|
"npm-watch": "^0.11.0",
|
|
55
|
-
"ts-jest": "^27.0.3",
|
|
56
56
|
"ts-node": "^8.6.2",
|
|
57
|
-
"typescript": "^
|
|
57
|
+
"typescript": "^5.3.3",
|
|
58
58
|
"typescript-eslint": "^0.0.1-alpha.0"
|
|
59
59
|
},
|
|
60
60
|
"author": "Autofleet",
|
|
61
61
|
"license": "ISC"
|
|
62
|
-
}
|
|
62
|
+
}
|
|
@@ -3,7 +3,7 @@ import { Router } from 'express';
|
|
|
3
3
|
import type { Request, Response } from 'express';
|
|
4
4
|
import handleError from '../errors';
|
|
5
5
|
import * as DefinitionRepo from '../../../repository/definition';
|
|
6
|
-
import { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../../../types/definition';
|
|
6
|
+
import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../../../types/definition';
|
|
7
7
|
import { validateCustomFieldDefinitionCreation, validateCustomFieldDefinitionUpdate } from './validations';
|
|
8
8
|
import logger from '../../../utils/logger';
|
|
9
9
|
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import Joi from 'joi';
|
|
2
|
-
import { CustomFieldDefinitionType } from '../../../utils/
|
|
2
|
+
import { CustomFieldDefinitionType } from '../../../utils/constants';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Schema for the validation of custom field definition
|
|
6
|
+
* The only custom validation is for
|
|
7
|
+
* {@link CustomFieldDefinitionType.SELECT SELECT}
|
|
8
|
+
* and
|
|
9
|
+
* {@link CustomFieldDefinitionType.STATUS STATUS}
|
|
10
|
+
* field types.
|
|
11
|
+
* The rest of the field types are validated by Joi
|
|
12
|
+
*/
|
|
4
13
|
const ValidationSchema = Joi.when('fieldType', {
|
|
5
14
|
is: CustomFieldDefinitionType.SELECT,
|
|
6
15
|
then: Joi.array().items(Joi.string()).min(1).unique(),
|
|
7
16
|
otherwise: Joi.any(),
|
|
17
|
+
}).when('fieldType', {
|
|
18
|
+
is: CustomFieldDefinitionType.STATUS,
|
|
19
|
+
then: Joi.string().min(1).required(),
|
|
20
|
+
otherwise: Joi.any(),
|
|
8
21
|
});
|
|
9
22
|
|
|
10
23
|
const CustomFieldDefinitionCreationSchema = Joi.object({
|
package/src/hooks/create.ts
CHANGED
|
@@ -2,7 +2,7 @@ import logger from '../utils/logger';
|
|
|
2
2
|
import * as ValueRepo from '../repository/value';
|
|
3
3
|
import * as DefinitionRepo from '../repository/definition';
|
|
4
4
|
import { MissingRequiredCustomFieldError } from '../errors';
|
|
5
|
-
import { ModelOptions } from '../types';
|
|
5
|
+
import type { ModelOptions } from '../types';
|
|
6
6
|
import applyScopeToInstance from '../utils/scopeAttributes';
|
|
7
7
|
|
|
8
8
|
/**
|
package/src/hooks/enrich.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
|
2
2
|
import * as ValueRepo from '../repository/value';
|
|
3
3
|
import * as DefinitionRepo from '../repository/definition';
|
|
4
|
-
import CustomFieldValue from '../models/CustomFieldValue';
|
|
5
|
-
import CustomFieldDefinition from '../models/CustomFieldDefinition';
|
|
6
|
-
import { SerializedCustomFields } from '../types/definition';
|
|
7
|
-
import { ModelOptions } from '../types';
|
|
4
|
+
import type CustomFieldValue from '../models/CustomFieldValue';
|
|
5
|
+
import type CustomFieldDefinition from '../models/CustomFieldDefinition';
|
|
6
|
+
import type { SerializedCustomFields } from '../types/definition';
|
|
7
|
+
import type { ModelOptions } from '../types';
|
|
8
8
|
import applyScopeToInstance from '../utils/scopeAttributes';
|
|
9
9
|
|
|
10
10
|
type SupportedHookTypes = 'afterFind' | 'afterCreate' | 'afterUpdate';
|
package/src/hooks/update.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Application } from 'express';
|
|
2
|
-
import { Sequelize } from 'sequelize-typescript';
|
|
2
|
+
import type { Sequelize } from 'sequelize-typescript';
|
|
3
3
|
import {
|
|
4
4
|
initTables, initTestModels,
|
|
5
5
|
} from './models';
|
|
@@ -11,13 +11,12 @@ import {
|
|
|
11
11
|
addHooks, addScopes, applyCustomAssociation, removeHooks,
|
|
12
12
|
} from './utils/init';
|
|
13
13
|
|
|
14
|
-
export * from './utils/validations/custom-fields';
|
|
14
|
+
export * from './utils/validations/schema/custom-fields';
|
|
15
15
|
|
|
16
16
|
export * from './utils/constants';
|
|
17
17
|
|
|
18
18
|
export * from './utils/helpers';
|
|
19
19
|
|
|
20
|
-
export { CustomFieldDefinitionType } from './utils/validations/type';
|
|
21
20
|
/**
|
|
22
21
|
* Adding custom fields enrichment to the models inside the MODELS_FILE_NAME json file
|
|
23
22
|
* @see {@link 'custom-fields/config'} for configurations
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
-
/* eslint-disable indent */
|
|
3
1
|
import {
|
|
4
2
|
Table,
|
|
5
3
|
Column,
|
|
@@ -12,11 +10,12 @@ import {
|
|
|
12
10
|
AfterSave,
|
|
13
11
|
Is,
|
|
14
12
|
} from 'sequelize-typescript';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
13
|
+
import { CustomFieldDefinitionType } from '../utils/constants';
|
|
14
|
+
import { CustomValidationTypes } from '../utils/validations/validators';
|
|
17
15
|
import { CustomFieldValue } from '.';
|
|
18
16
|
import { sendDimEvent } from '../events';
|
|
19
17
|
import { UnsupportedCustomFieldTypeError, UnsupportedCustomValidationError } from '../errors';
|
|
18
|
+
import logger from '../utils/logger';
|
|
20
19
|
|
|
21
20
|
@DefaultScope(() => ({ where: { disabled: false } }))
|
|
22
21
|
@Table({
|
|
@@ -30,7 +29,10 @@ import { UnsupportedCustomFieldTypeError, UnsupportedCustomValidationError } fro
|
|
|
30
29
|
timestamps: true,
|
|
31
30
|
validate: {
|
|
32
31
|
validationByType(this: CustomFieldDefinition) {
|
|
33
|
-
|
|
32
|
+
// Verify the validation is provided if needed
|
|
33
|
+
if (!this.validation && CustomValidationTypes[this.fieldType]) {
|
|
34
|
+
// TODO: enforce the validation-object schema based on the fieldType
|
|
35
|
+
logger.error(`No custom validation for custom field type ${this.fieldType} found`);
|
|
34
36
|
throw new UnsupportedCustomValidationError(`Validation provided for "${this.fieldType}" is not supported`);
|
|
35
37
|
}
|
|
36
38
|
},
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
-
/* eslint-disable indent */
|
|
3
1
|
import {
|
|
4
2
|
Table,
|
|
5
3
|
Column,
|
|
@@ -14,13 +12,11 @@ import {
|
|
|
14
12
|
BeforeUpdate,
|
|
15
13
|
BeforeBulkCreate,
|
|
16
14
|
BeforeBulkUpdate,
|
|
17
|
-
Scopes,
|
|
18
15
|
} from 'sequelize-typescript';
|
|
19
16
|
import { sendDimEvent } from '../events';
|
|
20
17
|
import { CustomFieldDefinition } from '.';
|
|
21
|
-
import validateValue from '../utils/validations';
|
|
18
|
+
import { validateValue } from '../utils/validations';
|
|
22
19
|
import * as CustomFieldDefinitionRepo from '../repository/definition';
|
|
23
|
-
import logger from '../utils/logger';
|
|
24
20
|
import { InvalidValueError } from '../errors';
|
|
25
21
|
|
|
26
22
|
@Table({
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
FindOptions,
|
|
3
|
-
Op, WhereOptions,
|
|
4
|
-
} from 'sequelize';
|
|
1
|
+
import { Op, type FindOptions, type WhereOptions } from 'sequelize';
|
|
5
2
|
import { CustomFieldDefinition } from '../models';
|
|
6
3
|
import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../types/definition';
|
|
7
|
-
import { ModelOptions } from '../types';
|
|
4
|
+
import type { ModelOptions } from '../types';
|
|
8
5
|
|
|
9
6
|
export const create = (data: CreateCustomFieldDefinition): Promise<CustomFieldDefinition> =>
|
|
10
7
|
CustomFieldDefinition.create(data);
|
package/src/repository/value.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import type { FindOptions, WhereOptions } from 'sequelize';
|
|
3
3
|
import { CustomFieldValue, CustomFieldDefinition } from '../models';
|
|
4
4
|
import * as DefinitionRepo from './definition';
|
|
5
|
-
import { CreateCustomFieldValue, ValuesToUpdate } from '../types/value';
|
|
5
|
+
import type { CreateCustomFieldValue, ValuesToUpdate } from '../types/value';
|
|
6
6
|
import logger from '../utils/logger';
|
|
7
7
|
import { MissingDefinitionError } from '../errors';
|
|
8
|
-
import { ModelOptions } from '../types';
|
|
8
|
+
import type { ModelOptions } from '../types';
|
|
9
9
|
|
|
10
10
|
export const findByModelIdAndDefinition = async (modelId: string, customFieldDefinitionId: string) =>
|
|
11
11
|
CustomFieldValue.findAll({ where: { modelId, customFieldDefinitionId }, include: [CustomFieldDefinition] });
|
package/src/scopes/filter.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable import/prefer-default-export */
|
|
2
|
-
import { Op, WhereOptions } from 'sequelize';
|
|
2
|
+
import { Op, type WhereOptions } from 'sequelize';
|
|
3
3
|
import { Sequelize } from 'sequelize-typescript';
|
|
4
4
|
import { customFields } from '@autofleet/common-types';
|
|
5
5
|
|
|
@@ -10,11 +10,7 @@ const { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;
|
|
|
10
10
|
* Currently supporting strings and arrays of strings.
|
|
11
11
|
* More types to be added (TBA).
|
|
12
12
|
*/
|
|
13
|
-
type
|
|
14
|
-
operator: string;
|
|
15
|
-
value: string;
|
|
16
|
-
};
|
|
17
|
-
export type ConditionValue = ConditionWithOperator | ConditionWithOperator[] | string | string[];
|
|
13
|
+
export type ConditionValue = string | string[];
|
|
18
14
|
|
|
19
15
|
export type CustomFieldSort = {
|
|
20
16
|
field: string;
|
|
@@ -25,13 +21,6 @@ export type CustomFieldFilterOptions = {
|
|
|
25
21
|
where?: WhereOptions;
|
|
26
22
|
}
|
|
27
23
|
|
|
28
|
-
const castIfNeeded = (conditionValue: string): string => {
|
|
29
|
-
if (!Number.isNaN(Date.parse(conditionValue))) {
|
|
30
|
-
return '::timestamp';
|
|
31
|
-
}
|
|
32
|
-
return '';
|
|
33
|
-
};
|
|
34
|
-
const AND_DELIMETER = ' AND ';
|
|
35
24
|
/**
|
|
36
25
|
* A Sequelize scope for filtering models by custom fields.
|
|
37
26
|
* This scope builds a WHERE clause to be applied on the main query.
|
|
@@ -48,33 +37,24 @@ export const customFieldsFilterScope = (
|
|
|
48
37
|
// Build the WHERE clause for custom field filtering
|
|
49
38
|
const conditionsStrings = Object.entries(conditions)
|
|
50
39
|
.map(
|
|
51
|
-
([key,
|
|
52
|
-
if (Array.isArray(
|
|
53
|
-
if (
|
|
40
|
+
([key, value]) => {
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
if (value.length === 0) {
|
|
54
43
|
// if empty array, the condition is ignored
|
|
55
44
|
return false;
|
|
56
45
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return `(custom_fields->>'${key}') IN (${values})`;
|
|
60
|
-
}
|
|
61
|
-
return condition
|
|
62
|
-
.map((c) => `(custom_fields->>'${key}')${castIfNeeded(c.value)} ${c.operator} '${c.value}'`).join(AND_DELIMETER);
|
|
46
|
+
const values = value.map((v) => `'${v}'`).join(',');
|
|
47
|
+
return `custom_fields->>'${key}' IN (${values})`;
|
|
63
48
|
}
|
|
64
|
-
|
|
65
|
-
return `(custom_fields->>'${key}')${castIfNeeded(condition)} = '${condition}'`;
|
|
66
|
-
}
|
|
67
|
-
if (condition?.operator) {
|
|
68
|
-
return `(custom_fields->>'${key}')${castIfNeeded(condition.value)} ${condition.operator} '${condition.value}'`;
|
|
69
|
-
}
|
|
70
|
-
return false;
|
|
49
|
+
return `custom_fields->>'${key}' = '${value}'`;
|
|
71
50
|
},
|
|
72
51
|
)
|
|
73
52
|
.filter(Boolean);
|
|
53
|
+
|
|
74
54
|
if (conditionsStrings.length === 0) {
|
|
75
55
|
return {};
|
|
76
56
|
}
|
|
77
|
-
const customFieldConditions = conditionsStrings.join(
|
|
57
|
+
const customFieldConditions = conditionsStrings.join(' AND ');
|
|
78
58
|
|
|
79
59
|
const subQuery = `${'SELECT model_id FROM ('
|
|
80
60
|
+ 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
|
|
@@ -82,7 +62,8 @@ export const customFieldsFilterScope = (
|
|
|
82
62
|
+ 'INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id '
|
|
83
63
|
+ `AND cd.model_type = '${name}' `
|
|
84
64
|
+ 'GROUP BY cv.model_id'
|
|
85
|
-
+ ') AS CustomFieldAggregation
|
|
65
|
+
+ ') AS CustomFieldAggregation '
|
|
66
|
+
+ 'WHERE '}${customFieldConditions}`;
|
|
86
67
|
|
|
87
68
|
return {
|
|
88
69
|
where: {
|
|
@@ -16,7 +16,7 @@ const customFieldsSearchTestFlow = async ({
|
|
|
16
16
|
fieldValue,
|
|
17
17
|
searchTerm,
|
|
18
18
|
expectedNumberOfQueryResults,
|
|
19
|
-
}: CustomFieldsSearchTestFlowInput)
|
|
19
|
+
}: CustomFieldsSearchTestFlowInput): Promise<void> => {
|
|
20
20
|
const definition = createDefinition({ fieldType, name: 'coolDefinition' });
|
|
21
21
|
await DefinitionRepo.create({ ...definition });
|
|
22
22
|
const [testModel1] = await createTestModels(definition.entityId, 2);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
-
import { CreateCustomFieldDefinition, CustomFieldDefinitionDTO } from '../../types/definition';
|
|
2
|
+
import type { CreateCustomFieldDefinition, CustomFieldDefinitionDTO } from '../../types/definition';
|
|
3
3
|
|
|
4
4
|
export const contextAwareFieldDefinition = {
|
|
5
5
|
name: 'cool field',
|
|
@@ -34,7 +34,7 @@ export const booleanField = (modelType: string): CreateCustomFieldDefinition =>
|
|
|
34
34
|
entityType: 'fleetId',
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
export const
|
|
37
|
+
export const selectField = (modelType: string, options): CreateCustomFieldDefinition => ({
|
|
38
38
|
name: 'choices',
|
|
39
39
|
modelType,
|
|
40
40
|
fieldType: 'select',
|
|
@@ -43,13 +43,11 @@ export const enumField = (modelType: string, options): CreateCustomFieldDefiniti
|
|
|
43
43
|
entityType: 'fleetId',
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
export const
|
|
47
|
-
name: '
|
|
46
|
+
export const statusField = (modelType: string, options): CreateCustomFieldDefinition => ({
|
|
47
|
+
name: 'lifecycle',
|
|
48
48
|
modelType,
|
|
49
|
-
fieldType: '
|
|
50
|
-
validation:
|
|
51
|
-
between: [10, 12],
|
|
52
|
-
},
|
|
49
|
+
fieldType: 'status',
|
|
50
|
+
validation: options,
|
|
53
51
|
entityId: uuidv4(),
|
|
54
52
|
entityType: 'fleetId',
|
|
55
53
|
});
|
package/src/types/index.ts
CHANGED
|
@@ -2,9 +2,23 @@ import { customFields } from '@autofleet/common-types';
|
|
|
2
2
|
|
|
3
3
|
const { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;
|
|
4
4
|
|
|
5
|
-
// eslint-disable-next-line import/prefer-default-export
|
|
6
5
|
export const supportedEntities = ['businessModelId', 'fleetId', 'demandSourceId'];
|
|
7
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
|
+
|
|
8
22
|
export {
|
|
9
23
|
/** @deprecated Use the value from `@autofleet/common-types` instead */
|
|
10
24
|
CUSTOM_FIELDS_FILTER_SCOPE,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable import/prefer-default-export */
|
|
2
|
-
import { WhereOptions, Op, BindOrReplacements } from 'sequelize';
|
|
3
|
-
import { ModelStatic, Sequelize } from 'sequelize-typescript';
|
|
4
|
-
import { CustomFieldDefinitionType } from '../
|
|
2
|
+
import { type WhereOptions, Op, type BindOrReplacements } from 'sequelize';
|
|
3
|
+
import { type ModelStatic, Sequelize } from 'sequelize-typescript';
|
|
4
|
+
import { CustomFieldDefinitionType } from '../constants';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Builds a WHERE clause and replacements for free-text search by custom fields.
|
|
@@ -28,8 +28,8 @@ interface CustomFieldsSearchPayload {
|
|
|
28
28
|
export const generateCustomFieldSearchQueryPayload = (
|
|
29
29
|
searchTerm: string,
|
|
30
30
|
model: ModelStatic,
|
|
31
|
-
entityId
|
|
32
|
-
customFieldsTypesToExclude
|
|
31
|
+
entityId: string,
|
|
32
|
+
customFieldsTypesToExclude: CustomFieldDefinitionType[] = [
|
|
33
33
|
CustomFieldDefinitionType.DATETIME,
|
|
34
34
|
CustomFieldDefinitionType.DATE,
|
|
35
35
|
],
|
package/src/utils/init.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import type { CustomFieldDefinitionType } from '../constants';
|
|
2
|
+
import { validators } from './validators';
|
|
3
3
|
|
|
4
|
-
const validateValue = (
|
|
5
|
-
|
|
4
|
+
export const validateValue = (
|
|
5
|
+
value: unknown,
|
|
6
|
+
valueType: CustomFieldDefinitionType,
|
|
7
|
+
validation?: unknown,
|
|
8
|
+
) => {
|
|
9
|
+
const validator = validators[valueType];
|
|
10
|
+
if (!validator) {
|
|
11
|
+
// Unsupported field type
|
|
6
12
|
return false;
|
|
7
13
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return true;
|
|
14
|
+
// Always allow null values
|
|
15
|
+
return value === null || validator(value, validation);
|
|
16
|
+
/** TODO: Add validation for required fields
|
|
17
|
+
* @example
|
|
18
|
+
* if (validations.required && !value) {
|
|
19
|
+
* return false;
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
17
22
|
};
|
|
18
|
-
|
|
19
|
-
export default validateValue;
|
|
@@ -1,45 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { CustomFieldDefinitionType } from '../constants';
|
|
2
|
+
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* Validator is a function that validates a custom-field `value`,
|
|
5
|
+
* against a custom-field definition `validation object`.
|
|
6
|
+
* @returns `true` if the value is valid, `false` otherwise.
|
|
4
7
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
DATETIME = 'datetime',
|
|
11
|
-
TEXT = 'text',
|
|
12
|
-
IMAGE = 'image',
|
|
13
|
-
SELECT = 'select',
|
|
14
|
-
}
|
|
8
|
+
export type Validator<Value, DefinitionValidationObject> = (
|
|
9
|
+
value: Value,
|
|
10
|
+
validation?: DefinitionValidationObject
|
|
11
|
+
) => boolean;
|
|
12
|
+
|
|
15
13
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* Validators is a map of custom-field types to their respective validators.
|
|
15
|
+
* The key is the custom-field type, and the value is the validator function.
|
|
18
16
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// Null is always allowed
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
switch (valueType) {
|
|
26
|
-
case CustomFieldDefinitionType.TEXT:
|
|
27
|
-
return typeof value === 'string';
|
|
28
|
-
case CustomFieldDefinitionType.NUMBER:
|
|
29
|
-
return typeof value === 'number';
|
|
30
|
-
case CustomFieldDefinitionType.BOOLEAN:
|
|
31
|
-
return typeof value === 'boolean';
|
|
32
|
-
case CustomFieldDefinitionType.DATE:
|
|
33
|
-
case CustomFieldDefinitionType.DATETIME:
|
|
34
|
-
return !Joi.date().validate(value).error;
|
|
35
|
-
case CustomFieldDefinitionType.SELECT:
|
|
36
|
-
return true; // custom validation
|
|
37
|
-
case CustomFieldDefinitionType.IMAGE:
|
|
38
|
-
return !Joi.array().min(1).unique().items(Joi.string().uri())
|
|
39
|
-
.validate(value).error;
|
|
40
|
-
default:
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
17
|
+
export type Validators = {
|
|
18
|
+
[K in CustomFieldDefinitionType]: Validator<unknown, unknown>;
|
|
43
19
|
};
|
|
44
|
-
|
|
45
|
-
export default validateValueType;
|
|
@@ -0,0 +1,39 @@
|
|
|
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) => (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()
|
|
29
|
+
.items(Joi.string().uri())
|
|
30
|
+
.validate(value).error),
|
|
31
|
+
[CustomFieldDefinitionType.FILE]: (value) => (!Joi.array().min(1).unique().items(Joi.object({
|
|
32
|
+
id: Joi.string().uuid().required(),
|
|
33
|
+
name: Joi.string().required(),
|
|
34
|
+
link: Joi.string().required(),
|
|
35
|
+
createdAt: Joi.date(),
|
|
36
|
+
addedBy: Joi.string().uuid(),
|
|
37
|
+
}))
|
|
38
|
+
.validate(value).error),
|
|
39
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Validator } from '../type';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validate that the value is one of the select values
|
|
5
|
+
*/
|
|
6
|
+
export const validateSelect: Validator<string, string[]> = (
|
|
7
|
+
value,
|
|
8
|
+
selectValues,
|
|
9
|
+
) => (Array.isArray(selectValues)
|
|
10
|
+
&& selectValues.includes(value)
|
|
11
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Validator } from '../type';
|
|
2
|
+
|
|
3
|
+
type StatusColor = string | null; // TODO: Takes from @autofleet/colors ?
|
|
4
|
+
type StatusValue = string;
|
|
5
|
+
type StatusOption = {
|
|
6
|
+
value: StatusValue;
|
|
7
|
+
color: StatusColor;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validate that the value is one of the status values
|
|
12
|
+
*/
|
|
13
|
+
export const validateStatus: Validator<StatusValue, StatusOption[]> = (
|
|
14
|
+
value,
|
|
15
|
+
statusValues,
|
|
16
|
+
) => (Array.isArray(statusValues)
|
|
17
|
+
&& statusValues.some((status) => status.value === value)
|
|
18
|
+
);
|
package/dist/api/index.d.ts
DELETED
package/dist/api/index.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
// export the api object
|
|
7
|
-
const express_1 = require("express");
|
|
8
|
-
const v1_1 = __importDefault(require("./v1"));
|
|
9
|
-
const router = (0, express_1.Router)({ mergeParams: true });
|
|
10
|
-
router.use('/v1', v1_1.default);
|
|
11
|
-
exports.default = router;
|