@autofleet/sadot 0.7.8-beta-1 → 0.7.8-beta--beta-1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/v1/definition/validations.js +24 -2
- package/dist/events/index.js +21 -20
- package/dist/hooks/create.js +32 -16
- package/dist/models/CustomFieldDefinition.d.ts +1 -0
- package/dist/models/CustomFieldDefinition.js +14 -0
- package/dist/models/index.js +1 -1
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.js +1 -3
- package/dist/repository/definition.d.ts +12 -6
- package/dist/repository/value.js +26 -10
- package/dist/scopes/filter.d.ts +10 -3
- package/dist/scopes/filter.js +21 -28
- package/dist/tests/mocks/definition.mock.d.ts +2 -0
- package/dist/tests/mocks/definition.mock.js +10 -9
- package/dist/types/definition/index.d.ts +1 -0
- package/dist/utils/logger/index.d.ts +1 -1
- package/dist/utils/logger/index.js +5 -3
- package/package.json +1 -1
|
@@ -6,10 +6,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.validateCustomFieldDefinitionUpdate = exports.validateCustomFieldDefinitionCreation = void 0;
|
|
7
7
|
const joi_1 = __importDefault(require("joi"));
|
|
8
8
|
const constants_1 = require("../../../utils/constants");
|
|
9
|
-
const
|
|
9
|
+
const FileValidationSchema = joi_1.default.object({
|
|
10
|
+
name: joi_1.default.string().required(),
|
|
11
|
+
type: joi_1.default.string(),
|
|
12
|
+
size: joi_1.default.string(),
|
|
13
|
+
addedBy: joi_1.default.string().uuid(),
|
|
14
|
+
});
|
|
15
|
+
const statusValidationObject = joi_1.default.object({
|
|
10
16
|
value: joi_1.default.string().required(),
|
|
11
17
|
color: joi_1.default.string().required(),
|
|
12
|
-
})
|
|
18
|
+
});
|
|
19
|
+
const statusValidationObjectSchema = joi_1.default.array().items(statusValidationObject).min(1).unique('value');
|
|
13
20
|
/**
|
|
14
21
|
* Schema for the validation of custom field definition
|
|
15
22
|
* The only custom validation is for
|
|
@@ -28,10 +35,24 @@ const ValidationSchema = joi_1.default.when('fieldType', {
|
|
|
28
35
|
then: statusValidationObjectSchema,
|
|
29
36
|
otherwise: joi_1.default.any(),
|
|
30
37
|
});
|
|
38
|
+
const DefaultValueSchema = joi_1.default.when('fieldType', {
|
|
39
|
+
switch: [
|
|
40
|
+
{ is: constants_1.CustomFieldDefinitionType.BOOLEAN, then: joi_1.default.boolean().allow(null) },
|
|
41
|
+
{ is: constants_1.CustomFieldDefinitionType.DATE, then: joi_1.default.date().allow(null) },
|
|
42
|
+
{ is: constants_1.CustomFieldDefinitionType.DATETIME, then: joi_1.default.date().allow(null) },
|
|
43
|
+
{ is: constants_1.CustomFieldDefinitionType.FILE, then: FileValidationSchema },
|
|
44
|
+
{ is: constants_1.CustomFieldDefinitionType.IMAGE, then: joi_1.default.string().uri().allow(null) },
|
|
45
|
+
{ is: constants_1.CustomFieldDefinitionType.NUMBER, then: joi_1.default.number().allow(null) },
|
|
46
|
+
{ is: constants_1.CustomFieldDefinitionType.SELECT, then: joi_1.default.string().allow(null) },
|
|
47
|
+
{ is: constants_1.CustomFieldDefinitionType.STATUS, then: statusValidationObject.allow(null) },
|
|
48
|
+
{ is: constants_1.CustomFieldDefinitionType.TEXT, then: joi_1.default.string().allow(null) },
|
|
49
|
+
],
|
|
50
|
+
});
|
|
31
51
|
const CustomFieldDefinitionCreationSchema = joi_1.default.object({
|
|
32
52
|
name: joi_1.default.string().required(),
|
|
33
53
|
displayName: joi_1.default.string().required(),
|
|
34
54
|
validation: ValidationSchema,
|
|
55
|
+
defaultValue: DefaultValueSchema,
|
|
35
56
|
fieldType: joi_1.default.string().valid(...Object.values(constants_1.CustomFieldDefinitionType)).required(),
|
|
36
57
|
entityId: joi_1.default.string().guid().required(),
|
|
37
58
|
entityType: joi_1.default.string().required(),
|
|
@@ -42,6 +63,7 @@ const CustomFieldDefinitionCreationSchema = joi_1.default.object({
|
|
|
42
63
|
const CustomFieldDefinitionUpdateSchema = joi_1.default.object({
|
|
43
64
|
displayName: joi_1.default.string(),
|
|
44
65
|
validation: ValidationSchema,
|
|
66
|
+
defaultValue: DefaultValueSchema,
|
|
45
67
|
fieldType: joi_1.default.string().valid(...Object.values(constants_1.CustomFieldDefinitionType)),
|
|
46
68
|
description: joi_1.default.string().allow(null),
|
|
47
69
|
required: joi_1.default.boolean(),
|
package/dist/events/index.js
CHANGED
|
@@ -7,18 +7,18 @@ exports.sendDimEvent = void 0;
|
|
|
7
7
|
const events_1 = __importDefault(require("@autofleet/events"));
|
|
8
8
|
const logger_1 = __importDefault(require("../utils/logger"));
|
|
9
9
|
const events = new events_1.default({ logger: logger_1.default });
|
|
10
|
-
const KEYS_TO_CONVERT = ['value'];
|
|
11
|
-
const
|
|
12
|
-
if (Object.keys(savedObject).some((key) => keysToConvert.includes(key))) {
|
|
13
|
-
|
|
14
|
-
keysToConvert.forEach((key) => {
|
|
15
|
-
if (typeof savedObject[key] === 'boolean') {
|
|
16
|
-
objectToReturn[key] = savedObject[key].toString();
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
return objectToReturn;
|
|
10
|
+
const KEYS_TO_CONVERT = ['value', 'defaultValue'];
|
|
11
|
+
const stringifyBooleans = (savedObject, keysToConvert) => {
|
|
12
|
+
if (!Object.keys(savedObject).some((key) => keysToConvert.includes(key))) {
|
|
13
|
+
return savedObject;
|
|
20
14
|
}
|
|
21
|
-
|
|
15
|
+
const objectToReturn = { ...savedObject };
|
|
16
|
+
keysToConvert.forEach((key) => {
|
|
17
|
+
if (typeof savedObject[key] === 'boolean') {
|
|
18
|
+
objectToReturn[key] = savedObject[key].toString();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
return objectToReturn;
|
|
22
22
|
};
|
|
23
23
|
const modelTableMapping = {
|
|
24
24
|
CustomFieldDefinition: {
|
|
@@ -32,16 +32,17 @@ const modelTableMapping = {
|
|
|
32
32
|
};
|
|
33
33
|
const sendDimEvent = (instance) => {
|
|
34
34
|
const mapping = modelTableMapping[instance.constructor.name];
|
|
35
|
-
if (mapping) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
if (!mapping) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let objectToSend = instance.get();
|
|
39
|
+
try {
|
|
40
|
+
objectToSend = stringifyBooleans(instance.get(), KEYS_TO_CONVERT);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
logger_1.default.error('Failed to convert booleans in dim event payload', err);
|
|
44
44
|
}
|
|
45
|
+
events.sendObject(mapping.tableName, mapping.eventVersion, objectToSend);
|
|
45
46
|
};
|
|
46
47
|
exports.sendDimEvent = sendDimEvent;
|
|
47
48
|
exports.default = events;
|
package/dist/hooks/create.js
CHANGED
|
@@ -48,27 +48,43 @@ exports.beforeBulkCreate = beforeBulkCreate;
|
|
|
48
48
|
const beforeCreate = (scopeAttributes, modelOptions = {}) => async (instance, options) => {
|
|
49
49
|
logger_1.default.debug('sadot - before create hook');
|
|
50
50
|
const { fields } = options;
|
|
51
|
+
const { include, useEntityIdFromInclude } = modelOptions;
|
|
51
52
|
const modelType = instance.constructor.name;
|
|
52
53
|
const identifiers = (0, scopeAttributes_1.default)(instance, scopeAttributes);
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
const where = {
|
|
55
|
+
modelType,
|
|
56
|
+
disabled: false,
|
|
57
|
+
...(!useEntityIdFromInclude && { entityId: identifiers }),
|
|
58
|
+
};
|
|
59
|
+
const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: false, transaction: options.transaction, include: include?.(identifiers) });
|
|
60
|
+
const requiredFieldsNames = Array.from(new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)));
|
|
55
61
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw new errors_1.MissingRequiredCustomFieldError(missingFields);
|
|
62
|
-
}
|
|
63
|
-
await ValueRepo.updateValues(modelType, instance.id, identifiers, customFields, {
|
|
64
|
-
transaction: options.transaction,
|
|
65
|
-
modelOptions,
|
|
66
|
-
});
|
|
62
|
+
if ((customFieldsIdx === -1 || !instance.customFields) && requiredFieldsNames?.length > 0) {
|
|
63
|
+
throw new errors_1.MissingRequiredCustomFieldError(requiredFieldsNames);
|
|
64
|
+
}
|
|
65
|
+
const fieldsWithDefaultValue = fieldDefinitions.filter((def) => ![null, undefined].includes(def.defaultValue));
|
|
66
|
+
if (fieldsWithDefaultValue.length) {
|
|
67
67
|
// eslint-disable-next-line no-param-reassign
|
|
68
|
-
|
|
68
|
+
instance.customFields || (instance.customFields = {});
|
|
69
|
+
fieldsWithDefaultValue.filter((def) => !(def.name in instance.customFields)).forEach(({ name, defaultValue }) => {
|
|
70
|
+
// eslint-disable-next-line no-param-reassign
|
|
71
|
+
instance.customFields[name] = defaultValue;
|
|
72
|
+
});
|
|
69
73
|
}
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
const { customFields } = instance;
|
|
75
|
+
if (customFieldsIdx === -1 || !customFields) {
|
|
76
|
+
return;
|
|
72
77
|
}
|
|
78
|
+
const fieldsNames = Object.keys(customFields);
|
|
79
|
+
const missingFields = requiredFieldsNames.filter((name) => !fieldsNames.includes(name));
|
|
80
|
+
if (missingFields?.length > 0) {
|
|
81
|
+
throw new errors_1.MissingRequiredCustomFieldError(missingFields);
|
|
82
|
+
}
|
|
83
|
+
await ValueRepo.updateValues(modelType, instance.id, identifiers, customFields, {
|
|
84
|
+
transaction: options.transaction,
|
|
85
|
+
modelOptions,
|
|
86
|
+
});
|
|
87
|
+
// eslint-disable-next-line no-param-reassign
|
|
88
|
+
fields.splice(customFieldsIdx, 1);
|
|
73
89
|
};
|
|
74
90
|
exports.beforeCreate = beforeCreate;
|
|
@@ -19,12 +19,19 @@ const _1 = require(".");
|
|
|
19
19
|
const events_1 = require("../events");
|
|
20
20
|
const errors_1 = require("../errors");
|
|
21
21
|
const logger_1 = __importDefault(require("../utils/logger"));
|
|
22
|
+
const validations_1 = require("../utils/validations");
|
|
22
23
|
let CustomFieldDefinition = class CustomFieldDefinition extends sequelize_typescript_1.Model {
|
|
23
24
|
static displayNameDefaultValue(instance) {
|
|
24
25
|
if (!instance?.displayName) {
|
|
25
26
|
// eslint-disable-next-line no-param-reassign
|
|
26
27
|
instance.displayName = instance.name;
|
|
27
28
|
}
|
|
29
|
+
if (![null, undefined].includes(instance.defaultValue)) {
|
|
30
|
+
const isValid = (0, validations_1.validateValue)(instance.defaultValue, instance.fieldType, instance.validation);
|
|
31
|
+
if (!isValid) {
|
|
32
|
+
throw new errors_1.InvalidValueError(instance.defaultValue, instance.fieldType);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
28
35
|
}
|
|
29
36
|
static afterSaveHandler(instance, options) {
|
|
30
37
|
if (options.transaction) {
|
|
@@ -116,6 +123,13 @@ __decorate([
|
|
|
116
123
|
}),
|
|
117
124
|
__metadata("design:type", Boolean)
|
|
118
125
|
], CustomFieldDefinition.prototype, "disabled", void 0);
|
|
126
|
+
__decorate([
|
|
127
|
+
(0, sequelize_typescript_1.Column)({
|
|
128
|
+
type: sequelize_typescript_1.DataType.JSONB,
|
|
129
|
+
allowNull: true,
|
|
130
|
+
}),
|
|
131
|
+
__metadata("design:type", Object)
|
|
132
|
+
], CustomFieldDefinition.prototype, "defaultValue", void 0);
|
|
119
133
|
__decorate([
|
|
120
134
|
sequelize_typescript_1.Column,
|
|
121
135
|
__metadata("design:type", Date)
|
package/dist/models/index.js
CHANGED
|
@@ -22,7 +22,7 @@ exports.AssociatedTestModel = AssociatedTestModel_1.default;
|
|
|
22
22
|
const productionModels = [CustomFieldDefinition_1.default, CustomFieldValue_1.default];
|
|
23
23
|
const testModels = [TestModel_1.default, AssociatedTestModel_1.default, ContextAwareTestModel_1.default, ContextTestModel_1.default];
|
|
24
24
|
const SADOT_MIGRATION_PREFIX = 'sadot-migration';
|
|
25
|
-
const SCHEMA_VERSION =
|
|
25
|
+
const SCHEMA_VERSION = 2;
|
|
26
26
|
const CUSTOM_FIELDS_SCHEMA_VERSION = `${SADOT_MIGRATION_PREFIX}_${SCHEMA_VERSION}`;
|
|
27
27
|
const initTables = async (sequelize, getUser) => {
|
|
28
28
|
logger_1.default.info('custom-fields: initialize custom-fields tables');
|
|
@@ -28,9 +28,7 @@ __decorate([
|
|
|
28
28
|
], ContextAwareTestModel.prototype, "id", void 0);
|
|
29
29
|
__decorate([
|
|
30
30
|
(0, sequelize_typescript_1.ForeignKey)(() => ContextTestModel_1.default),
|
|
31
|
-
(0, sequelize_typescript_1.Column)({
|
|
32
|
-
type: sequelize_typescript_1.DataType.UUID,
|
|
33
|
-
}),
|
|
31
|
+
(0, sequelize_typescript_1.Column)({ type: sequelize_typescript_1.DataType.UUID }),
|
|
34
32
|
__metadata("design:type", String)
|
|
35
33
|
], ContextAwareTestModel.prototype, "contextId", void 0);
|
|
36
34
|
__decorate([
|
|
@@ -1,20 +1,26 @@
|
|
|
1
|
-
import { type FindOptions, type WhereOptions } from 'sequelize';
|
|
1
|
+
import { type Includeable, type Transaction, type FindOptions, type WhereOptions } from 'sequelize';
|
|
2
2
|
import { CustomFieldDefinition } from '../models';
|
|
3
3
|
import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../types/definition';
|
|
4
4
|
import type { ModelOptions } from '../types';
|
|
5
5
|
export declare const create: (data: CreateCustomFieldDefinition) => Promise<CustomFieldDefinition>;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
interface SadotFindOptions {
|
|
7
|
+
withDisabled?: boolean;
|
|
8
|
+
transaction?: Transaction;
|
|
9
|
+
include?: Includeable | Includeable[];
|
|
10
|
+
}
|
|
11
|
+
export declare const findAll: (where: WhereOptions, options?: SadotFindOptions) => Promise<CustomFieldDefinition[]>;
|
|
12
|
+
export declare const findByIds: (ids: string[], options?: SadotFindOptions) => Promise<CustomFieldDefinition[]>;
|
|
13
|
+
export declare const findById: (id: string, options?: Pick<SadotFindOptions, 'withDisabled'>) => Promise<CustomFieldDefinition | null>;
|
|
9
14
|
export declare const findByEntityIds: (modelType: string, entityIds: string[], options?: FindOptions & {
|
|
10
15
|
modelOptions?: ModelOptions;
|
|
11
16
|
}) => Promise<CustomFieldDefinition[]>;
|
|
12
17
|
export declare const findByWhere: (where: any) => Promise<CustomFieldDefinition | null>;
|
|
13
18
|
export declare const findDefinitionsByModels: (modelTypes: string[], options?: any) => Promise<CustomFieldDefinition[]>;
|
|
14
19
|
export declare const update: (id: string, data: UpdateCustomFieldDefinition) => Promise<CustomFieldDefinition>;
|
|
15
|
-
export declare const disable: (id: string) => Promise<
|
|
16
|
-
export declare const destroy: (id: string) => Promise<
|
|
20
|
+
export declare const disable: (id: string) => Promise<[affectedCount: number]>;
|
|
21
|
+
export declare const destroy: (id: string) => Promise<number>;
|
|
17
22
|
/**
|
|
18
23
|
* Return the names of the required fields for a given model
|
|
19
24
|
*/
|
|
20
25
|
export declare const getRequiredFields: (modelType: string, modelId: string | string[], entityId: string | string[], modelOptions?: ModelOptions) => Promise<string[]>;
|
|
26
|
+
export {};
|
package/dist/repository/value.js
CHANGED
|
@@ -31,6 +31,7 @@ const models_1 = require("../models");
|
|
|
31
31
|
const DefinitionRepo = __importStar(require("./definition"));
|
|
32
32
|
const logger_1 = __importDefault(require("../utils/logger"));
|
|
33
33
|
const errors_1 = require("../errors");
|
|
34
|
+
const constants_1 = require("../utils/constants");
|
|
34
35
|
const findByModelIdAndDefinition = async (modelId, customFieldDefinitionId) => models_1.CustomFieldValue.findAll({ where: { modelId, customFieldDefinitionId }, include: [models_1.CustomFieldDefinition] });
|
|
35
36
|
exports.findByModelIdAndDefinition = findByModelIdAndDefinition;
|
|
36
37
|
const create = async (data, withAssociations = false) => {
|
|
@@ -66,6 +67,18 @@ const findValuesByModelIds = async (modelIds, options) => {
|
|
|
66
67
|
});
|
|
67
68
|
};
|
|
68
69
|
exports.findValuesByModelIds = findValuesByModelIds;
|
|
70
|
+
const formatFunctions = {
|
|
71
|
+
[constants_1.CustomFieldDefinitionType.DATE]: (value) => {
|
|
72
|
+
if (value) {
|
|
73
|
+
const date = new Date(value);
|
|
74
|
+
if (date.toString() === 'Invalid Date') {
|
|
75
|
+
throw new Error(`Invalid date value: ${value}`);
|
|
76
|
+
}
|
|
77
|
+
return date.toISOString();
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
},
|
|
81
|
+
};
|
|
69
82
|
/**
|
|
70
83
|
* Try to update custom field values for a model instance.
|
|
71
84
|
* Create new value record if not exists, but fails if value's definition not exist.
|
|
@@ -83,11 +96,9 @@ const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, opt
|
|
|
83
96
|
const where = {
|
|
84
97
|
modelType,
|
|
85
98
|
name: names,
|
|
99
|
+
...(!options.modelOptions?.useEntityIdFromInclude && { entityId: identifiers }),
|
|
86
100
|
};
|
|
87
|
-
|
|
88
|
-
where.entityId = identifiers;
|
|
89
|
-
}
|
|
90
|
-
const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true, transaction, include: modelOptions.include?.(identifiers) }) || [];
|
|
101
|
+
const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true, transaction, include: modelOptions.include?.(identifiers) }) ?? [];
|
|
91
102
|
const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
|
|
92
103
|
if (fieldDefinitions.length !== names.length) {
|
|
93
104
|
logger_1.default.warn(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
|
|
@@ -99,12 +110,17 @@ const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, opt
|
|
|
99
110
|
if (valuesWithDisabledDefinitions?.length > 0) {
|
|
100
111
|
logger_1.default.warn(`custom-fields: trying to update disabled values: ${valuesWithDisabledDefinitions.join(', ')}`);
|
|
101
112
|
}
|
|
102
|
-
const values = names.map((name) =>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
const values = names.map((name) => {
|
|
114
|
+
const fieldDefinition = fieldDefinitions.find((def) => def.name === name);
|
|
115
|
+
const formatFunction = formatFunctions[fieldDefinition.fieldType];
|
|
116
|
+
const value = formatFunction ? formatFunction(valuesToUpdate[name]) : valuesToUpdate[name];
|
|
117
|
+
return {
|
|
118
|
+
modelId,
|
|
119
|
+
updatedAt: new Date(),
|
|
120
|
+
customFieldDefinitionId: fieldDefinition.id,
|
|
121
|
+
value: value !== undefined ? value : fieldDefinition.defaultValue,
|
|
122
|
+
};
|
|
123
|
+
});
|
|
108
124
|
return Promise.all(values.map(async (value) => {
|
|
109
125
|
const [cfv] = await models_1.CustomFieldValue.upsert(value, {
|
|
110
126
|
transaction: options.transaction,
|
package/dist/scopes/filter.d.ts
CHANGED
|
@@ -17,6 +17,10 @@ export type CustomFieldFilterOptions = {
|
|
|
17
17
|
where?: WhereOptions;
|
|
18
18
|
replacements?: Record<string, string>;
|
|
19
19
|
};
|
|
20
|
+
type customFieldsFilterScopeParams = {
|
|
21
|
+
replacementsMap: Record<string, string>;
|
|
22
|
+
scopeValue: Record<string, ConditionValue>;
|
|
23
|
+
};
|
|
20
24
|
/**
|
|
21
25
|
* A Sequelize scope for filtering models by custom fields.
|
|
22
26
|
* This scope builds a WHERE clause to be applied on the main query.
|
|
@@ -24,9 +28,12 @@ export type CustomFieldFilterOptions = {
|
|
|
24
28
|
* @param name - The model type name used to join custom_field_definitions.
|
|
25
29
|
* @returns A function that takes conditions and returns the Sequelize options object.
|
|
26
30
|
*/
|
|
27
|
-
export declare const customFieldsFilterScope: (name: string) => (
|
|
31
|
+
export declare const customFieldsFilterScope: (name: string) => ({ replacementsMap: replacements, scopeValue: conditions, }: customFieldsFilterScopeParams) => CustomFieldFilterOptions;
|
|
28
32
|
export declare const scopeName = "filterByCustomFields";
|
|
29
|
-
export declare const customFieldsSortScope: (name: string) => (
|
|
33
|
+
export declare const customFieldsSortScope: (name: string) => ({ replacementsMap, scopeValue: sort }: {
|
|
34
|
+
replacementsMap: any;
|
|
35
|
+
scopeValue: any;
|
|
36
|
+
}) => {
|
|
30
37
|
attributes?: undefined;
|
|
31
38
|
order?: undefined;
|
|
32
39
|
replacements?: undefined;
|
|
@@ -35,6 +42,6 @@ export declare const customFieldsSortScope: (name: string) => (sort: CustomField
|
|
|
35
42
|
include: (string | import("sequelize/types/utils").Literal)[][];
|
|
36
43
|
};
|
|
37
44
|
order: import("sequelize/types/utils").Literal[];
|
|
38
|
-
replacements:
|
|
45
|
+
replacements: any;
|
|
39
46
|
};
|
|
40
47
|
export {};
|
package/dist/scopes/filter.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.customFieldsSortScope = exports.scopeName = exports.customFieldsFilterScope = void 0;
|
|
7
4
|
/* eslint-disable import/prefer-default-export */
|
|
8
5
|
const sequelize_1 = require("sequelize");
|
|
9
6
|
const sequelize_typescript_1 = require("sequelize-typescript");
|
|
10
7
|
const common_types_1 = require("@autofleet/common-types");
|
|
11
|
-
const moment_1 = __importDefault(require("moment"));
|
|
12
8
|
const helpers_1 = require("../utils/helpers");
|
|
13
9
|
const { CUSTOM_FIELDS_FILTER_SCOPE } = common_types_1.customFields;
|
|
10
|
+
const isDate = (input) => input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
|
|
14
11
|
const castIfNeeded = (conditionValue) => {
|
|
15
|
-
if (
|
|
12
|
+
if (isDate(conditionValue)) {
|
|
16
13
|
return '::timestamp';
|
|
17
14
|
}
|
|
18
15
|
if (!Number.isNaN(Number(conditionValue))) {
|
|
@@ -34,19 +31,17 @@ const castValueToJsonb = (value) => `to_jsonb(${value}::text)`;
|
|
|
34
31
|
* @param name - The model type name used to join custom_field_definitions.
|
|
35
32
|
* @returns A function that takes conditions and returns the Sequelize options object.
|
|
36
33
|
*/
|
|
37
|
-
const customFieldsFilterScope = (name) => (conditions) => {
|
|
34
|
+
const customFieldsFilterScope = (name) => ({ replacementsMap: replacements, scopeValue: conditions, }) => {
|
|
38
35
|
if (!conditions || Object.keys(conditions).length === 0) {
|
|
39
36
|
return {};
|
|
40
37
|
}
|
|
41
|
-
const ConditionNameRandomStr = (0, helpers_1.generateRandomString)();
|
|
42
|
-
const replacements = {};
|
|
43
|
-
replacements[ConditionNameRandomStr] = `${name}`;
|
|
44
38
|
// Build the WHERE clause for custom field filtering
|
|
45
39
|
const conditionsStrings = Object.entries(conditions)
|
|
46
40
|
.map(([key, condition]) => {
|
|
47
|
-
const replacemetKey = (
|
|
48
|
-
replacements[replacemetKey] = `${key}`;
|
|
41
|
+
const replacemetKey = Object.keys(replacements).find((randomString) => replacements[randomString] === key);
|
|
49
42
|
const columnCondition = `(${CD_NAME_COLUMN} = :${replacemetKey})`;
|
|
43
|
+
if (!replacemetKey)
|
|
44
|
+
return false;
|
|
50
45
|
if (Array.isArray(condition)) {
|
|
51
46
|
if (condition.length === 0) {
|
|
52
47
|
// if empty array, the condition is ignored
|
|
@@ -54,29 +49,25 @@ const customFieldsFilterScope = (name) => (conditions) => {
|
|
|
54
49
|
}
|
|
55
50
|
if (typeof condition[0] === 'string') {
|
|
56
51
|
const values = condition.map((v) => {
|
|
57
|
-
const valRandom = (
|
|
58
|
-
replacements[`${valRandom}`] = `${v}`;
|
|
52
|
+
const valRandom = Object.keys(replacements).find((randomString) => replacements[randomString] === v);
|
|
59
53
|
return castValueToJsonb(`:${valRandom}`);
|
|
60
54
|
}).join(',');
|
|
61
|
-
return `(${columnCondition} AND ${CV_VALUE_COLUMN} IN (
|
|
55
|
+
return `(${columnCondition} AND ${CV_VALUE_COLUMN} IN (${values}))`;
|
|
62
56
|
}
|
|
63
57
|
return condition
|
|
64
58
|
.map((c) => {
|
|
65
|
-
const valRep = (
|
|
66
|
-
replacements[valRep] = `${c.value}`;
|
|
59
|
+
const valRep = Object.keys(replacements).find((replacementKey) => replacements[replacementKey] === c.value);
|
|
67
60
|
const valueAsJsonb = castValueToJsonb(`:${valRep}`);
|
|
68
61
|
return `(${columnCondition} AND ${CV_VALUE_COLUMN}${castIfNeeded(c.value)} ${c.operator} ${valueAsJsonb})`;
|
|
69
62
|
}).join(AND_DELIMETER);
|
|
70
63
|
}
|
|
71
64
|
if (typeof condition === 'string' || typeof condition === 'number') {
|
|
72
|
-
const conditionRep = (
|
|
73
|
-
replacements[conditionRep] = `${condition}`;
|
|
65
|
+
const conditionRep = Object.keys(replacements).find((replacementKey) => replacements[replacementKey] === condition);
|
|
74
66
|
const valueAsJsonb = castValueToJsonb(`:${conditionRep}`);
|
|
75
67
|
return `(${columnCondition} AND ${CV_VALUE_COLUMN}${castIfNeeded(condition)} = ${valueAsJsonb})`;
|
|
76
68
|
}
|
|
77
69
|
if (condition?.operator) {
|
|
78
|
-
const valueRep = (
|
|
79
|
-
replacements[valueRep] = `${condition.value}`;
|
|
70
|
+
const valueRep = Object.keys(replacements).find((replacementKey) => replacements[replacementKey] === condition.value);
|
|
80
71
|
const valueAsJsonb = castValueToJsonb(`:${valueRep}`);
|
|
81
72
|
return `( ${columnCondition} AND ${CV_VALUE_COLUMN}${castIfNeeded(condition.value)} ${condition.operator} ${valueAsJsonb})`;
|
|
82
73
|
}
|
|
@@ -91,8 +82,9 @@ const customFieldsFilterScope = (name) => (conditions) => {
|
|
|
91
82
|
SELECT cv.model_id
|
|
92
83
|
FROM custom_field_values AS cv
|
|
93
84
|
INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id
|
|
94
|
-
AND cd.model_type =
|
|
85
|
+
AND cd.model_type = '${name}'
|
|
95
86
|
WHERE ${customFieldConditions}
|
|
87
|
+
AND cv.deleted_at IS NULL AND cd.deleted_at IS NULL
|
|
96
88
|
GROUP BY cv.model_id
|
|
97
89
|
HAVING COUNT(DISTINCT cv.custom_field_definition_id) = ${conditionsStrings.length}
|
|
98
90
|
`.replace(/\n/g, '');
|
|
@@ -107,15 +99,13 @@ const customFieldsFilterScope = (name) => (conditions) => {
|
|
|
107
99
|
};
|
|
108
100
|
exports.customFieldsFilterScope = customFieldsFilterScope;
|
|
109
101
|
exports.scopeName = CUSTOM_FIELDS_FILTER_SCOPE;
|
|
110
|
-
const customFieldsSortScope = (name) => (sort) => {
|
|
102
|
+
const customFieldsSortScope = (name) => ({ replacementsMap, scopeValue: sort }) => {
|
|
111
103
|
if (!sort || sort.length === 0) {
|
|
112
104
|
return {};
|
|
113
105
|
}
|
|
114
106
|
const randomStr = (0, helpers_1.generateRandomString)();
|
|
115
|
-
const replacements = {};
|
|
116
107
|
const includes = Object.entries(sort).map(([key]) => {
|
|
117
|
-
const
|
|
118
|
-
replacements[keyRandomReplacement] = `${key}`;
|
|
108
|
+
const replacemetKey = Object.keys(replacementsMap).find((randomString) => replacementsMap[randomString] === key);
|
|
119
109
|
return ([
|
|
120
110
|
sequelize_typescript_1.Sequelize.literal(`(
|
|
121
111
|
SELECT value
|
|
@@ -124,19 +114,22 @@ const customFieldsSortScope = (name) => (sort) => {
|
|
|
124
114
|
ON cv.custom_field_definition_id = cd.id
|
|
125
115
|
AND cd.model_type = '${name}'
|
|
126
116
|
WHERE cv.model_id = "${name}"."id"
|
|
127
|
-
AND cd.name = :${
|
|
117
|
+
AND cd.name = :${replacemetKey}
|
|
128
118
|
) AS CustomFieldAggregation
|
|
129
119
|
)
|
|
130
120
|
`), randomStr,
|
|
131
121
|
]);
|
|
132
122
|
});
|
|
133
|
-
const orders = Object.entries(sort).map(([,
|
|
123
|
+
const orders = Object.entries(sort).map(([, sortObject]) => {
|
|
124
|
+
const direction = typeof sortObject === 'string' ? sortObject : Object.values(sortObject)[0];
|
|
125
|
+
return sequelize_typescript_1.Sequelize.literal(`"${randomStr}" ${direction || 'ASC'}`);
|
|
126
|
+
});
|
|
134
127
|
return {
|
|
135
128
|
attributes: {
|
|
136
129
|
include: includes,
|
|
137
130
|
},
|
|
138
131
|
order: orders,
|
|
139
|
-
replacements,
|
|
132
|
+
replacements: replacementsMap,
|
|
140
133
|
};
|
|
141
134
|
};
|
|
142
135
|
exports.customFieldsSortScope = customFieldsSortScope;
|
|
@@ -14,6 +14,7 @@ export declare const coolFieldDefinition2: {
|
|
|
14
14
|
createdAt?: Date;
|
|
15
15
|
updatedAt?: Date;
|
|
16
16
|
deletedAt?: Date;
|
|
17
|
+
defaultValue?: any;
|
|
17
18
|
displayName?: string;
|
|
18
19
|
validation?: any;
|
|
19
20
|
fieldType: string;
|
|
@@ -29,6 +30,7 @@ export declare const coolFieldDefinition3: {
|
|
|
29
30
|
createdAt?: Date;
|
|
30
31
|
updatedAt?: Date;
|
|
31
32
|
deletedAt?: Date;
|
|
33
|
+
defaultValue?: any;
|
|
32
34
|
displayName?: string;
|
|
33
35
|
validation?: any;
|
|
34
36
|
fieldType: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createDefinitions = exports.createDefinition = exports.statusField = exports.selectField = exports.booleanField = exports.coolFieldDefinition3 = exports.coolFieldDefinition2 = exports.coolFieldDefinition = exports.contextAwareFieldDefinition = void 0;
|
|
4
|
-
const
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
5
|
exports.contextAwareFieldDefinition = {
|
|
6
6
|
name: 'cool field',
|
|
7
7
|
modelType: 'ContextAwareTestModel',
|
|
@@ -12,7 +12,7 @@ exports.coolFieldDefinition = {
|
|
|
12
12
|
name: 'cool field',
|
|
13
13
|
modelType: 'TestModel',
|
|
14
14
|
fieldType: 'number',
|
|
15
|
-
entityId: (0,
|
|
15
|
+
entityId: (0, node_crypto_1.randomUUID)(),
|
|
16
16
|
entityType: 'fleetId',
|
|
17
17
|
};
|
|
18
18
|
exports.coolFieldDefinition2 = {
|
|
@@ -27,7 +27,7 @@ const booleanField = (modelType) => ({
|
|
|
27
27
|
name: 'shapeless',
|
|
28
28
|
modelType,
|
|
29
29
|
fieldType: 'boolean',
|
|
30
|
-
entityId: (0,
|
|
30
|
+
entityId: (0, node_crypto_1.randomUUID)(),
|
|
31
31
|
entityType: 'fleetId',
|
|
32
32
|
});
|
|
33
33
|
exports.booleanField = booleanField;
|
|
@@ -36,7 +36,7 @@ const selectField = (modelType, options) => ({
|
|
|
36
36
|
modelType,
|
|
37
37
|
fieldType: 'select',
|
|
38
38
|
validation: options,
|
|
39
|
-
entityId: (0,
|
|
39
|
+
entityId: (0, node_crypto_1.randomUUID)(),
|
|
40
40
|
entityType: 'fleetId',
|
|
41
41
|
});
|
|
42
42
|
exports.selectField = selectField;
|
|
@@ -45,24 +45,25 @@ const statusField = (modelType, options) => ({
|
|
|
45
45
|
modelType,
|
|
46
46
|
fieldType: 'status',
|
|
47
47
|
validation: options,
|
|
48
|
-
entityId: (0,
|
|
48
|
+
entityId: (0, node_crypto_1.randomUUID)(),
|
|
49
49
|
entityType: 'fleetId',
|
|
50
50
|
});
|
|
51
51
|
exports.statusField = statusField;
|
|
52
52
|
// eslint-disable-next-line max-len
|
|
53
53
|
const createDefinition = (defaults) => ({
|
|
54
|
-
name: defaults?.name || `def_${(0,
|
|
54
|
+
name: defaults?.name || `def_${(0, node_crypto_1.randomUUID)()}`,
|
|
55
55
|
modelType: defaults?.modelType || 'TestModel',
|
|
56
56
|
fieldType: defaults?.fieldType || 'boolean',
|
|
57
|
-
entityId: defaults?.entityId || (0,
|
|
57
|
+
entityId: defaults?.entityId || (0, node_crypto_1.randomUUID)(),
|
|
58
58
|
entityType: defaults?.entityType || 'fleetId',
|
|
59
|
+
...(defaults?.defaultValue && { defaultValue: defaults.defaultValue }),
|
|
59
60
|
});
|
|
60
61
|
exports.createDefinition = createDefinition;
|
|
61
62
|
const createDefinitions = (defaults, length = 1) => (Array(length).fill({}).map((_) => ({
|
|
62
|
-
name: defaults?.name || `def_${(0,
|
|
63
|
+
name: defaults?.name || `def_${(0, node_crypto_1.randomUUID)()}`,
|
|
63
64
|
modelType: defaults?.modelType || 'TestModel',
|
|
64
65
|
fieldType: defaults?.fieldType || 'boolean',
|
|
65
|
-
entityId: defaults?.entityId || (0,
|
|
66
|
+
entityId: defaults?.entityId || (0, node_crypto_1.randomUUID)(),
|
|
66
67
|
entityType: defaults?.entityType || 'fleetId',
|
|
67
68
|
})));
|
|
68
69
|
exports.createDefinitions = createDefinitions;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const logger:
|
|
1
|
+
declare const logger: import("@autofleet/logger").LoggerInstanceManager;
|
|
2
2
|
export default logger;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const logger = Logger();
|
|
6
|
+
const logger_1 = __importDefault(require("@autofleet/logger"));
|
|
7
|
+
const logger = (0, logger_1.default)();
|
|
6
8
|
exports.default = logger;
|