@autofleet/sadot 0.6.2 → 0.6.3-beta.1
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/dist/api/v1/definition/validations.d.ts +2 -2
- package/dist/api/v1/definition/validations.js +2 -2
- package/dist/api/v1/errors.js +1 -1
- package/dist/hooks/create.d.ts +2 -1
- package/dist/hooks/create.js +8 -4
- package/dist/hooks/enrich.d.ts +3 -1
- package/dist/hooks/enrich.js +22 -4
- package/dist/hooks/update.d.ts +2 -1
- package/dist/hooks/update.js +4 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -1
- package/dist/models/index.d.ts +3 -1
- package/dist/models/index.js +8 -2
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.d.ts +10 -0
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.js +55 -0
- package/dist/models/tests/contextAwareModels/ContextTestModel.d.ts +13 -0
- package/dist/models/tests/contextAwareModels/ContextTestModel.js +47 -0
- package/dist/repository/definition.d.ts +7 -4
- package/dist/repository/definition.js +27 -15
- package/dist/repository/value.d.ts +5 -1
- package/dist/repository/value.js +13 -3
- package/dist/scopes/filter.d.ts +9 -0
- package/dist/scopes/filter.js +37 -5
- package/dist/tests/functional/searching/index.d.ts +8 -0
- package/dist/tests/functional/searching/index.js +44 -0
- package/dist/tests/helpers/database-config.d.ts +1 -0
- package/dist/tests/helpers/database-config.js +1 -0
- package/dist/tests/helpers/index.js +2 -0
- package/dist/tests/mocks/definition.mock.d.ts +6 -0
- package/dist/tests/mocks/definition.mock.js +7 -1
- package/dist/tests/mocks/testModel.d.ts +1 -1
- package/dist/types/index.d.ts +20 -2
- package/dist/utils/constants/index.d.ts +4 -1
- package/dist/utils/constants/index.js +3 -1
- package/dist/utils/helpers/index.d.ts +25 -0
- package/dist/utils/helpers/index.js +34 -0
- package/dist/utils/init.d.ts +5 -4
- package/dist/utils/init.js +19 -9
- package/dist/utils/scopeAttributes.d.ts +2 -0
- package/dist/utils/scopeAttributes.js +11 -0
- package/dist/utils/validations/custom-fields.d.ts +2 -1
- package/dist/utils/validations/custom-fields.js +1 -1
- package/dist/utils/validations/type.js +1 -1
- package/package.json +8 -8
- package/src/api/v1/definition/index.ts +1 -1
- package/src/api/v1/definition/validations.ts +0 -13
- 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 -2
- package/src/models/CustomFieldDefinition.ts +5 -9
- package/src/models/CustomFieldValue.ts +5 -1
- package/src/repository/definition.ts +5 -2
- package/src/repository/value.ts +2 -2
- package/src/scopes/filter.ts +26 -1
- package/src/tests/functional/searching/index.ts +1 -1
- package/src/tests/mocks/definition.mock.ts +8 -6
- package/src/types/index.ts +2 -2
- package/src/utils/helpers/index.ts +4 -4
- package/src/utils/init.ts +4 -1
- package/src/utils/validations/{schema/custom-fields.ts → custom-fields.ts} +1 -0
- package/src/utils/validations/custom.ts +39 -0
- package/src/utils/validations/index.ts +15 -18
- package/src/utils/validations/type.ts +32 -5
- package/src/utils/validations/validators.ts +34 -0
- package/.env +0 -3
- package/src/utils/validations/validators/index.ts +0 -29
- package/src/utils/validations/validators/select.validator.ts +0 -13
- package/src/utils/validations/validators/status.validator.ts +0 -20
package/dist/scopes/filter.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.scopeName = exports.customFieldsFilterScope = void 0;
|
|
3
|
+
exports.customFieldsSortScope = exports.scopeName = exports.customFieldsFilterScope = void 0;
|
|
4
4
|
/* eslint-disable import/prefer-default-export */
|
|
5
5
|
const sequelize_1 = require("sequelize");
|
|
6
6
|
const sequelize_typescript_1 = require("sequelize-typescript");
|
|
7
|
-
const
|
|
7
|
+
const common_types_1 = require("@autofleet/common-types");
|
|
8
|
+
const { CUSTOM_FIELDS_FILTER_SCOPE } = common_types_1.customFields;
|
|
8
9
|
/**
|
|
9
10
|
* A Sequelize scope for filtering models by custom fields.
|
|
10
11
|
* This scope builds a WHERE clause to be applied on the main query.
|
|
@@ -13,16 +14,27 @@ const constants_1 = require("../utils/constants");
|
|
|
13
14
|
* @returns {Function} - A function that takes conditions and returns the Sequelize options object.
|
|
14
15
|
*/
|
|
15
16
|
const customFieldsFilterScope = (name) => (conditions) => {
|
|
17
|
+
if (!conditions || Object.keys(conditions).length === 0) {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
16
20
|
// Build the WHERE clause for custom field filtering
|
|
17
|
-
const
|
|
21
|
+
const conditionsStrings = Object.entries(conditions)
|
|
18
22
|
.map(([key, value]) => {
|
|
19
23
|
if (Array.isArray(value)) {
|
|
24
|
+
if (value.length === 0) {
|
|
25
|
+
// if empty array, the condition is ignored
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
20
28
|
const values = value.map((v) => `'${v}'`).join(',');
|
|
21
29
|
return `custom_fields->>'${key}' IN (${values})`;
|
|
22
30
|
}
|
|
23
31
|
return `custom_fields->>'${key}' = '${value}'`;
|
|
24
32
|
})
|
|
25
|
-
.
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
if (conditionsStrings.length === 0) {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
const customFieldConditions = conditionsStrings.join(' AND ');
|
|
26
38
|
const subQuery = `${'SELECT model_id FROM ('
|
|
27
39
|
+ 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
|
|
28
40
|
+ 'FROM custom_field_values AS cv '
|
|
@@ -40,4 +52,24 @@ const customFieldsFilterScope = (name) => (conditions) => {
|
|
|
40
52
|
};
|
|
41
53
|
};
|
|
42
54
|
exports.customFieldsFilterScope = customFieldsFilterScope;
|
|
43
|
-
exports.scopeName =
|
|
55
|
+
exports.scopeName = CUSTOM_FIELDS_FILTER_SCOPE;
|
|
56
|
+
const customFieldsSortScope = (name) => (sort) => {
|
|
57
|
+
if (!sort || sort.length === 0) {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
const incluedes = Object.entries(sort).map(([key]) => sequelize_typescript_1.Sequelize.literal(`(select CustomFieldAggregation.custom_fields->>'${key}'
|
|
61
|
+
from (SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields
|
|
62
|
+
FROM custom_field_values AS cv
|
|
63
|
+
INNER JOIN custom_field_definitions AS cd
|
|
64
|
+
ON cv.custom_field_definition_id = cd.id AND cd.model_type = 'Task'
|
|
65
|
+
where cv.model_id = "${name}"."id"
|
|
66
|
+
GROUP BY cv.model_id) AS CustomFieldAggregation) as "customFields.${key}"`));
|
|
67
|
+
const orders = Object.entries(sort).map(([key, value]) => sequelize_typescript_1.Sequelize.literal(`"customFields.${key}" ${value}`));
|
|
68
|
+
return {
|
|
69
|
+
attributes: {
|
|
70
|
+
include: incluedes,
|
|
71
|
+
},
|
|
72
|
+
order: orders,
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
exports.customFieldsSortScope = customFieldsSortScope;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface CustomFieldsSearchTestFlowInput {
|
|
2
|
+
fieldType: string;
|
|
3
|
+
fieldValue: string | number;
|
|
4
|
+
searchTerm: string;
|
|
5
|
+
expectedNumberOfQueryResults: number;
|
|
6
|
+
}
|
|
7
|
+
declare const customFieldsSearchTestFlow: ({ fieldType, fieldValue, searchTerm, expectedNumberOfQueryResults, }: CustomFieldsSearchTestFlowInput) => Promise<void>;
|
|
8
|
+
export default customFieldsSearchTestFlow;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const index_1 = require("../../../index");
|
|
27
|
+
const DefinitionRepo = __importStar(require("../../../repository/definition"));
|
|
28
|
+
const definition_mock_1 = require("../../mocks/definition.mock");
|
|
29
|
+
const testModel_1 = require("../../mocks/testModel");
|
|
30
|
+
const models_1 = require("../../../models");
|
|
31
|
+
const customFieldsSearchTestFlow = async ({ fieldType, fieldValue, searchTerm, expectedNumberOfQueryResults, }) => {
|
|
32
|
+
const definition = (0, definition_mock_1.createDefinition)({ fieldType, name: 'coolDefinition' });
|
|
33
|
+
await DefinitionRepo.create({ ...definition });
|
|
34
|
+
const [testModel1] = await (0, testModel_1.createTestModels)(definition.entityId, 2);
|
|
35
|
+
await testModel1.update({ customFields: { [definition.name]: fieldValue } });
|
|
36
|
+
const models = await models_1.TestModel.findAndCountAll({
|
|
37
|
+
...(0, index_1.generateCustomFieldSearchQueryPayload)(searchTerm, models_1.TestModel, definition.entityId),
|
|
38
|
+
});
|
|
39
|
+
expect(models.count).toBe(expectedNumberOfQueryResults);
|
|
40
|
+
if (expectedNumberOfQueryResults > 0) {
|
|
41
|
+
expect(models.rows[0].dataValues.id).toBe(testModel1.id);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
exports.default = customFieldsSearchTestFlow;
|
|
@@ -6,6 +6,7 @@ exports.default = {
|
|
|
6
6
|
password: process.env.DB_PASSWORD || null,
|
|
7
7
|
database: process.env.DB_NAME || 'sadot_package_test',
|
|
8
8
|
host: process.env.DB_HOST || '127.0.0.1',
|
|
9
|
+
port: process.env.DB_PORT || 5432,
|
|
9
10
|
dialect: process.env.DB_TYPE || 'postgres',
|
|
10
11
|
define: {
|
|
11
12
|
underscored: true,
|
|
@@ -7,6 +7,8 @@ const cleanup = async () => {
|
|
|
7
7
|
if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development') {
|
|
8
8
|
await models_1.CustomFieldDefinition.unscoped().destroy({ where: {} });
|
|
9
9
|
await models_1.TestModel.destroy({ where: {} });
|
|
10
|
+
await models_1.ContextAwareTestModel.destroy({ where: {} });
|
|
11
|
+
await models_1.ContextTestModel.destroy({ where: {} });
|
|
10
12
|
}
|
|
11
13
|
};
|
|
12
14
|
exports.cleanup = cleanup;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { CreateCustomFieldDefinition, CustomFieldDefinitionDTO } from '../../types/definition';
|
|
2
|
+
export declare const contextAwareFieldDefinition: {
|
|
3
|
+
name: string;
|
|
4
|
+
modelType: string;
|
|
5
|
+
fieldType: string;
|
|
6
|
+
entityType: string;
|
|
7
|
+
};
|
|
2
8
|
export declare const coolFieldDefinition: CreateCustomFieldDefinition;
|
|
3
9
|
export declare const coolFieldDefinition2: {
|
|
4
10
|
name: string;
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createDefinitions = exports.createDefinition = exports.rangeField = exports.enumField = exports.booleanField = exports.coolFieldDefinition3 = exports.coolFieldDefinition2 = exports.coolFieldDefinition = void 0;
|
|
3
|
+
exports.createDefinitions = exports.createDefinition = exports.rangeField = exports.enumField = exports.booleanField = exports.coolFieldDefinition3 = exports.coolFieldDefinition2 = exports.coolFieldDefinition = exports.contextAwareFieldDefinition = void 0;
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
|
+
exports.contextAwareFieldDefinition = {
|
|
6
|
+
name: 'cool field',
|
|
7
|
+
modelType: 'ContextAwareTestModel',
|
|
8
|
+
fieldType: 'number',
|
|
9
|
+
entityType: 'fleetId',
|
|
10
|
+
};
|
|
5
11
|
exports.coolFieldDefinition = {
|
|
6
12
|
name: 'cool field',
|
|
7
13
|
modelType: 'TestModel',
|
|
@@ -3,7 +3,7 @@ export declare const createTestModel: (payload?: {}) => Promise<TestModel>;
|
|
|
3
3
|
export declare const createTestModels: (fleetId: any, total: number) => Promise<TestModel[]>;
|
|
4
4
|
export declare const upsertTestModel: (payload: any) => Promise<[TestModel, boolean]>;
|
|
5
5
|
export declare const destroyTestModels: () => Promise<number>;
|
|
6
|
-
export declare const getTestModel: (id: string, options?: {}) => Promise<TestModel>;
|
|
6
|
+
export declare const getTestModel: (id: string, options?: {}) => Promise<TestModel | null>;
|
|
7
7
|
export declare const getSomeTestModels: (options?: {
|
|
8
8
|
limit: number;
|
|
9
9
|
}) => Promise<TestModel[]>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,13 +1,31 @@
|
|
|
1
|
+
import { IncludeOptions } from 'sequelize';
|
|
2
|
+
import { ModelCtor } from 'sequelize-typescript';
|
|
1
3
|
export type ModelFetcher = (name: string) => any;
|
|
2
4
|
export type ModelOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Include options for the model
|
|
7
|
+
*/
|
|
8
|
+
include?: (entityIds: string[]) => IncludeOptions[];
|
|
9
|
+
/**
|
|
10
|
+
* Custom association for the model
|
|
11
|
+
* @param model - The model to associate with
|
|
12
|
+
*/
|
|
13
|
+
customAssociation?: (model: ModelCtor) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Whether to use the entity id from the instance per scope attribute
|
|
16
|
+
*/
|
|
17
|
+
useEntityIdFromInclude?: boolean;
|
|
18
|
+
};
|
|
19
|
+
export type Models = {
|
|
3
20
|
name: string;
|
|
4
|
-
scopeAttributes:
|
|
21
|
+
scopeAttributes: any[];
|
|
22
|
+
modelOptions?: ModelOptions;
|
|
5
23
|
creationWebhookHandler?: (instance: any) => any;
|
|
6
24
|
updateWebhookHandler?: (instance: any) => any;
|
|
7
25
|
deletionWebhookHandler?: (instance: any) => any;
|
|
8
26
|
};
|
|
9
27
|
export type CustomFieldOptions = {
|
|
10
|
-
models:
|
|
28
|
+
models: Models[];
|
|
11
29
|
databaseConfig: any;
|
|
12
30
|
getUser: () => any;
|
|
13
31
|
};
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
+
declare const CUSTOM_FIELDS_FILTER_SCOPE: string;
|
|
1
2
|
export declare const supportedEntities: string[];
|
|
2
|
-
export
|
|
3
|
+
export {
|
|
4
|
+
/** @deprecated Use the value from `@autofleet/common-types` instead */
|
|
5
|
+
CUSTOM_FIELDS_FILTER_SCOPE, };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CUSTOM_FIELDS_FILTER_SCOPE = exports.supportedEntities = void 0;
|
|
4
|
+
const common_types_1 = require("@autofleet/common-types");
|
|
5
|
+
const { CUSTOM_FIELDS_FILTER_SCOPE } = common_types_1.customFields;
|
|
6
|
+
exports.CUSTOM_FIELDS_FILTER_SCOPE = CUSTOM_FIELDS_FILTER_SCOPE;
|
|
4
7
|
// eslint-disable-next-line import/prefer-default-export
|
|
5
8
|
exports.supportedEntities = ['businessModelId', 'fleetId', 'demandSourceId'];
|
|
6
|
-
exports.CUSTOM_FIELDS_FILTER_SCOPE = 'filterByCustomFields';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { WhereOptions, BindOrReplacements } from 'sequelize';
|
|
2
|
+
import { ModelStatic } from 'sequelize-typescript';
|
|
3
|
+
import { CustomFieldDefinitionType } from '../validations/type';
|
|
4
|
+
/**
|
|
5
|
+
* Builds a WHERE clause and replacements for free-text search by custom fields.
|
|
6
|
+
*
|
|
7
|
+
* This function constructs a WHERE clause and replacement bindings that allow searching
|
|
8
|
+
* for a given term within custom fields associated with a specific model type and entity ID.
|
|
9
|
+
* The WHERE clause and replacements are designed to be added to the main query.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} searchTerm - The term to search for within custom fields.
|
|
12
|
+
* @param {ModelStatic} model - The Sequelize model representing the entity type to search for.
|
|
13
|
+
* @param {string} entityId - The entity ID to filter the custom fields by.
|
|
14
|
+
* @param {CustomFieldDefinitionType[]} excludedCustomFieldsTypes - An array of custom field types
|
|
15
|
+
* to exclude from the search
|
|
16
|
+
*
|
|
17
|
+
* @returns {CustomFieldsSearchPayload} - An object containing the WHERE clause and replacements
|
|
18
|
+
* for Sequelize.
|
|
19
|
+
*/
|
|
20
|
+
interface CustomFieldsSearchPayload {
|
|
21
|
+
where: WhereOptions;
|
|
22
|
+
replacements: BindOrReplacements;
|
|
23
|
+
}
|
|
24
|
+
export declare const generateCustomFieldSearchQueryPayload: (searchTerm: string, model: ModelStatic, entityId: string, customFieldsTypesToExclude?: CustomFieldDefinitionType[]) => CustomFieldsSearchPayload;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateCustomFieldSearchQueryPayload = void 0;
|
|
4
|
+
/* eslint-disable import/prefer-default-export */
|
|
5
|
+
const sequelize_1 = require("sequelize");
|
|
6
|
+
const sequelize_typescript_1 = require("sequelize-typescript");
|
|
7
|
+
const type_1 = require("../validations/type");
|
|
8
|
+
const generateCustomFieldSearchQueryPayload = (searchTerm, model, entityId, customFieldsTypesToExclude = [
|
|
9
|
+
type_1.CustomFieldDefinitionType.DATETIME,
|
|
10
|
+
type_1.CustomFieldDefinitionType.DATE,
|
|
11
|
+
]) => {
|
|
12
|
+
const excludedTypesString = customFieldsTypesToExclude.map((type) => `'${type}'`).join(',');
|
|
13
|
+
const subQuery = 'EXISTS ('
|
|
14
|
+
+ ' SELECT 1'
|
|
15
|
+
+ ' FROM "custom_field_values" AS "cv"'
|
|
16
|
+
+ ' INNER JOIN custom_field_definitions AS cd '
|
|
17
|
+
+ ` ON cd.entity_id = '${entityId}'`
|
|
18
|
+
+ ' AND cv.custom_field_definition_id = cd.id'
|
|
19
|
+
+ ` AND cd.model_type = '${model.name}'`
|
|
20
|
+
+ ` ${excludedTypesString ? `AND cd.field_type NOT IN (${excludedTypesString})` : ''}`
|
|
21
|
+
+ ' WHERE'
|
|
22
|
+
+ ' "cv"."deleted_at" IS NULL'
|
|
23
|
+
+ ` AND "cv"."model_id" = "${model.name}"."id"`
|
|
24
|
+
+ ' AND CAST("cv"."value" AS TEXT) ILIKE :searchTerm)';
|
|
25
|
+
return {
|
|
26
|
+
where: {
|
|
27
|
+
[sequelize_1.Op.or]: [
|
|
28
|
+
sequelize_typescript_1.Sequelize.where(sequelize_typescript_1.Sequelize.literal(subQuery), true),
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
replacements: { searchTerm: `%${searchTerm}%` },
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
exports.generateCustomFieldSearchQueryPayload = generateCustomFieldSearchQueryPayload;
|
package/dist/utils/init.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { ModelFetcher,
|
|
2
|
-
export declare const addHooks: (models:
|
|
3
|
-
export declare const removeHooks: (models:
|
|
4
|
-
export declare const addScopes: (models:
|
|
1
|
+
import type { ModelFetcher, Models } from '../types';
|
|
2
|
+
export declare const addHooks: (models: Models[], getModel: ModelFetcher) => void;
|
|
3
|
+
export declare const removeHooks: (models: Models[], getModel: ModelFetcher) => void;
|
|
4
|
+
export declare const addScopes: (models: Models[], getModel: ModelFetcher) => void;
|
|
5
|
+
export declare const applyCustomAssociation: (models: Models[]) => void;
|
package/dist/utils/init.js
CHANGED
|
@@ -3,15 +3,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.addScopes = exports.removeHooks = exports.addHooks = void 0;
|
|
6
|
+
exports.applyCustomAssociation = exports.addScopes = exports.removeHooks = exports.addHooks = void 0;
|
|
7
7
|
const sequelize_1 = require("sequelize");
|
|
8
|
+
const common_types_1 = require("@autofleet/common-types");
|
|
9
|
+
const custom_fields_1 = require("@autofleet/common-types/lib/custom-fields");
|
|
8
10
|
const models_1 = require("../models");
|
|
9
11
|
const hooks_1 = require("../hooks");
|
|
10
12
|
const scopes_1 = require("../scopes");
|
|
11
13
|
const logger_1 = __importDefault(require("./logger"));
|
|
12
|
-
const
|
|
14
|
+
const filter_1 = require("../scopes/filter");
|
|
15
|
+
const { CUSTOM_FIELDS_FILTER_SCOPE } = common_types_1.customFields;
|
|
13
16
|
const addHooks = (models, getModel) => {
|
|
14
|
-
models.forEach(async ({ name, scopeAttributes }) => {
|
|
17
|
+
models.forEach(async ({ name, scopeAttributes, modelOptions, }) => {
|
|
15
18
|
try {
|
|
16
19
|
const model = getModel(name);
|
|
17
20
|
if (!model) {
|
|
@@ -30,11 +33,11 @@ const addHooks = (models, getModel) => {
|
|
|
30
33
|
model.addHook('beforeFind', 'sadot-beforeFind', (0, hooks_1.beforeFind)(scopeAttributes));
|
|
31
34
|
model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', hooks_1.beforeBulkCreate);
|
|
32
35
|
model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', hooks_1.beforeBulkUpdate);
|
|
33
|
-
model.addHook('beforeCreate', 'sadot-beforeCreate', (0, hooks_1.beforeCreate)(scopeAttributes));
|
|
34
|
-
model.addHook('beforeUpdate', 'sadot-beforeUpdate', (0, hooks_1.beforeUpdate)(scopeAttributes));
|
|
35
|
-
model.addHook('afterFind', 'sadot-afterFind', (0, hooks_1.enrichResults)(name, scopeAttributes));
|
|
36
|
-
model.addHook('afterUpdate', 'sadot-afterUpdate', (0, hooks_1.enrichResults)(name, scopeAttributes));
|
|
37
|
-
model.addHook('afterCreate', 'sadot-afterCreate', (0, hooks_1.enrichResults)(name, scopeAttributes));
|
|
36
|
+
model.addHook('beforeCreate', 'sadot-beforeCreate', (0, hooks_1.beforeCreate)(scopeAttributes, modelOptions));
|
|
37
|
+
model.addHook('beforeUpdate', 'sadot-beforeUpdate', (0, hooks_1.beforeUpdate)(scopeAttributes, modelOptions));
|
|
38
|
+
model.addHook('afterFind', 'sadot-afterFind', (0, hooks_1.enrichResults)(name, scopeAttributes, 'afterFind', modelOptions));
|
|
39
|
+
model.addHook('afterUpdate', 'sadot-afterUpdate', (0, hooks_1.enrichResults)(name, scopeAttributes, null, modelOptions));
|
|
40
|
+
model.addHook('afterCreate', 'sadot-afterCreate', (0, hooks_1.enrichResults)(name, scopeAttributes, null, modelOptions));
|
|
38
41
|
}
|
|
39
42
|
catch (e) {
|
|
40
43
|
logger_1.default.error(`Could not add custom fields hook to model ${name}. `, e);
|
|
@@ -89,7 +92,8 @@ const addScopes = (models, getModel) => {
|
|
|
89
92
|
// Necessary associations for the filtering scope
|
|
90
93
|
addAssociations(model, name);
|
|
91
94
|
// Add filter scope
|
|
92
|
-
model.addScope(
|
|
95
|
+
model.addScope(CUSTOM_FIELDS_FILTER_SCOPE, (0, scopes_1.customFieldsFilterScope)(name));
|
|
96
|
+
model.addScope(custom_fields_1.CUSTOM_FIELDS_SORT_SCOPE, (0, filter_1.customFieldsSortScope)(name));
|
|
93
97
|
}
|
|
94
98
|
catch (e) {
|
|
95
99
|
logger_1.default.error(`Could not add custom fields scopes to model ${name}. `, e);
|
|
@@ -97,3 +101,9 @@ const addScopes = (models, getModel) => {
|
|
|
97
101
|
});
|
|
98
102
|
};
|
|
99
103
|
exports.addScopes = addScopes;
|
|
104
|
+
const applyCustomAssociation = (models) => {
|
|
105
|
+
models.forEach(({ modelOptions }) => {
|
|
106
|
+
modelOptions?.customAssociation?.(models_1.CustomFieldDefinition);
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
exports.applyCustomAssociation = applyCustomAssociation;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const mapAttributeToInstance = (scopeAttributes, instance) => scopeAttributes.map((attr) => instance[attr]);
|
|
4
|
+
const applyScopeToInstance = (instance, scopeAttributes) => {
|
|
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
|
+
exports.default = applyScopeToInstance;
|
|
@@ -5,6 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.CustomFieldsSchema = void 0;
|
|
7
7
|
/* eslint-disable import/prefer-default-export */
|
|
8
|
-
const joi_1 = __importDefault(require("
|
|
8
|
+
const joi_1 = __importDefault(require("joi"));
|
|
9
9
|
const CustomFieldsSchema = joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.any());
|
|
10
10
|
exports.CustomFieldsSchema = CustomFieldsSchema;
|
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.CustomFieldDefinitionType = void 0;
|
|
7
|
-
const joi_1 = __importDefault(require("
|
|
7
|
+
const joi_1 = __importDefault(require("joi"));
|
|
8
8
|
/**
|
|
9
9
|
* Supported custom field types
|
|
10
10
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/sadot",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3-beta.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"linter": "./node_modules/.bin/eslint .",
|
|
10
10
|
"test": "jest --forceExit --runInBand",
|
|
11
11
|
"coverage": "jest --coverage --forceExit --runInBand && rm -rf ./coverage",
|
|
12
|
-
"build-to-local-repo": "npm run build && cp -r dist/*
|
|
12
|
+
"build-to-local-repo": "npm run build && cp -r dist/* ../task-ms/node_modules/$npm_package_name/dist",
|
|
13
13
|
"dev": "nodemon",
|
|
14
14
|
"watch": "npm-watch build-to-local-repo"
|
|
15
15
|
},
|
|
@@ -45,16 +45,16 @@
|
|
|
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": "^
|
|
49
|
-
"@typescript-eslint/parser": "^
|
|
50
|
-
"eslint": "^7.
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "^4.8.1",
|
|
49
|
+
"@typescript-eslint/parser": "^4.33.0",
|
|
50
|
+
"eslint": "^7.13.0",
|
|
51
51
|
"eslint-config-airbnb-typescript": "^12.0.0",
|
|
52
52
|
"eslint-plugin-import": "^2.22.1",
|
|
53
|
-
"jest": "^
|
|
54
|
-
"ts-jest": "^29.1.2",
|
|
53
|
+
"jest": "^27.0.5",
|
|
55
54
|
"npm-watch": "^0.11.0",
|
|
55
|
+
"ts-jest": "^27.0.3",
|
|
56
56
|
"ts-node": "^8.6.2",
|
|
57
|
-
"typescript": "^
|
|
57
|
+
"typescript": "^4.9.5",
|
|
58
58
|
"typescript-eslint": "^0.0.1-alpha.0"
|
|
59
59
|
},
|
|
60
60
|
"author": "Autofleet",
|
|
@@ -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
|
|
6
|
+
import { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../../../types/definition';
|
|
7
7
|
import { validateCustomFieldDefinitionCreation, validateCustomFieldDefinitionUpdate } from './validations';
|
|
8
8
|
import logger from '../../../utils/logger';
|
|
9
9
|
|
|
@@ -1,23 +1,10 @@
|
|
|
1
1
|
import Joi from 'joi';
|
|
2
2
|
import { CustomFieldDefinitionType } from '../../../utils/validations/type';
|
|
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
|
-
*/
|
|
13
4
|
const ValidationSchema = Joi.when('fieldType', {
|
|
14
5
|
is: CustomFieldDefinitionType.SELECT,
|
|
15
6
|
then: Joi.array().items(Joi.string()).min(1).unique(),
|
|
16
7
|
otherwise: Joi.any(),
|
|
17
|
-
}).when('fieldType', {
|
|
18
|
-
is: CustomFieldDefinitionType.STATUS,
|
|
19
|
-
then: Joi.string().min(1).required(),
|
|
20
|
-
otherwise: Joi.any(),
|
|
21
8
|
});
|
|
22
9
|
|
|
23
10
|
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
|
|
5
|
+
import { 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
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
4
|
+
import CustomFieldValue from '../models/CustomFieldValue';
|
|
5
|
+
import CustomFieldDefinition from '../models/CustomFieldDefinition';
|
|
6
|
+
import { SerializedCustomFields } from '../types/definition';
|
|
7
|
+
import { 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
|
|
2
|
+
import { Sequelize } from 'sequelize-typescript';
|
|
3
3
|
import {
|
|
4
4
|
initTables, initTestModels,
|
|
5
5
|
} from './models';
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
addHooks, addScopes, applyCustomAssociation, removeHooks,
|
|
12
12
|
} from './utils/init';
|
|
13
13
|
|
|
14
|
-
export * from './utils/validations/
|
|
14
|
+
export * from './utils/validations/custom-fields';
|
|
15
15
|
|
|
16
16
|
export * from './utils/constants';
|
|
17
17
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
/* eslint-disable indent */
|
|
1
3
|
import {
|
|
2
4
|
Table,
|
|
3
5
|
Column,
|
|
@@ -10,13 +12,11 @@ import {
|
|
|
10
12
|
AfterSave,
|
|
11
13
|
Is,
|
|
12
14
|
} from 'sequelize-typescript';
|
|
13
|
-
|
|
15
|
+
import { validateValidation } from '../utils/validations/custom';
|
|
14
16
|
import { CustomFieldDefinitionType } from '../utils/validations/type';
|
|
15
|
-
import { CustomValidationTypes } from '../utils/validations/validators';
|
|
16
17
|
import { CustomFieldValue } from '.';
|
|
17
18
|
import { sendDimEvent } from '../events';
|
|
18
19
|
import { UnsupportedCustomFieldTypeError, UnsupportedCustomValidationError } from '../errors';
|
|
19
|
-
import logger from '../utils/logger';
|
|
20
20
|
|
|
21
21
|
@DefaultScope(() => ({ where: { disabled: false } }))
|
|
22
22
|
@Table({
|
|
@@ -30,12 +30,8 @@ import logger from '../utils/logger';
|
|
|
30
30
|
timestamps: true,
|
|
31
31
|
validate: {
|
|
32
32
|
validationByType(this: CustomFieldDefinition) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (CustomValidationTypes[this.fieldType]) {
|
|
36
|
-
logger.error(`No custom validation for custom field type ${this.fieldType} found`);
|
|
37
|
-
throw new UnsupportedCustomValidationError(`Validation provided for "${this.fieldType}" is not supported`);
|
|
38
|
-
}
|
|
33
|
+
if (!validateValidation(this.fieldType, this.validation)) {
|
|
34
|
+
throw new UnsupportedCustomValidationError(`Validation provided for "${this.fieldType}" is not supported`);
|
|
39
35
|
}
|
|
40
36
|
},
|
|
41
37
|
},
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
/* eslint-disable indent */
|
|
1
3
|
import {
|
|
2
4
|
Table,
|
|
3
5
|
Column,
|
|
@@ -12,11 +14,13 @@ import {
|
|
|
12
14
|
BeforeUpdate,
|
|
13
15
|
BeforeBulkCreate,
|
|
14
16
|
BeforeBulkUpdate,
|
|
17
|
+
Scopes,
|
|
15
18
|
} from 'sequelize-typescript';
|
|
16
19
|
import { sendDimEvent } from '../events';
|
|
17
20
|
import { CustomFieldDefinition } from '.';
|
|
18
|
-
import
|
|
21
|
+
import validateValue from '../utils/validations';
|
|
19
22
|
import * as CustomFieldDefinitionRepo from '../repository/definition';
|
|
23
|
+
import logger from '../utils/logger';
|
|
20
24
|
import { InvalidValueError } from '../errors';
|
|
21
25
|
|
|
22
26
|
@Table({
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
FindOptions,
|
|
3
|
+
Op, WhereOptions,
|
|
4
|
+
} from 'sequelize';
|
|
2
5
|
import { CustomFieldDefinition } from '../models';
|
|
3
6
|
import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../types/definition';
|
|
4
|
-
import
|
|
7
|
+
import { ModelOptions } from '../types';
|
|
5
8
|
|
|
6
9
|
export const create = (data: CreateCustomFieldDefinition): Promise<CustomFieldDefinition> =>
|
|
7
10
|
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
|
|
5
|
+
import { CreateCustomFieldValue, ValuesToUpdate } from '../types/value';
|
|
6
6
|
import logger from '../utils/logger';
|
|
7
7
|
import { MissingDefinitionError } from '../errors';
|
|
8
|
-
import
|
|
8
|
+
import { ModelOptions } from '../types';
|
|
9
9
|
|
|
10
10
|
export const findByModelIdAndDefinition = async (modelId: string, customFieldDefinitionId: string) =>
|
|
11
11
|
CustomFieldValue.findAll({ where: { modelId, customFieldDefinitionId }, include: [CustomFieldDefinition] });
|