@autofleet/sadot 0.7.6-beta-0ecad376.3 → 0.7.6-beta.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/hooks/create.js +16 -32
- package/dist/repository/definition.d.ts +6 -12
- package/dist/repository/value.d.ts +1 -1
- package/dist/repository/value.js +17 -3
- package/dist/utils/helpers/index.d.ts +3 -3
- package/dist/utils/helpers/index.js +23 -21
- package/package.json +1 -1
- package/src/hooks/create.ts +24 -42
- package/src/repository/definition.ts +6 -15
- package/src/repository/value.ts +19 -2
- package/src/utils/helpers/index.ts +28 -26
package/dist/hooks/create.js
CHANGED
|
@@ -48,43 +48,27 @@ 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;
|
|
52
51
|
const modelType = instance.constructor.name;
|
|
53
52
|
const identifiers = (0, scopeAttributes_1.default)(instance, scopeAttributes);
|
|
54
|
-
|
|
55
|
-
|
|
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)));
|
|
53
|
+
// get all model's required definitions
|
|
54
|
+
const requiredFieldsNames = await DefinitionRepo.getRequiredFields(modelType, instance.id, identifiers, modelOptions);
|
|
61
55
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
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
|
-
// eslint-disable-next-line no-param-reassign
|
|
68
|
-
instance.customFields || (instance.customFields = {});
|
|
69
|
-
fieldDefinitions.filter((def) => !(def.name in instance.customFields) && ![null, undefined].includes(def.defaultValue)).forEach(({ name, defaultValue }) => {
|
|
70
|
-
// eslint-disable-next-line no-param-reassign
|
|
71
|
-
instance.customFields[name] = defaultValue;
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
56
|
const { customFields } = instance;
|
|
75
|
-
if (customFieldsIdx
|
|
76
|
-
|
|
57
|
+
if (customFieldsIdx > -1 && customFields) {
|
|
58
|
+
const fieldsNames = Object.keys(customFields);
|
|
59
|
+
const missingFields = requiredFieldsNames.filter((name) => !fieldsNames.includes(name));
|
|
60
|
+
if (missingFields?.length > 0) {
|
|
61
|
+
throw new errors_1.MissingRequiredCustomFieldError(missingFields);
|
|
62
|
+
}
|
|
63
|
+
await ValueRepo.updateValues(modelType, instance.id, identifiers, customFields, {
|
|
64
|
+
transaction: options.transaction,
|
|
65
|
+
modelOptions,
|
|
66
|
+
}, true);
|
|
67
|
+
// eslint-disable-next-line no-param-reassign
|
|
68
|
+
fields.splice(customFieldsIdx, 1);
|
|
77
69
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (missingFields?.length > 0) {
|
|
81
|
-
throw new errors_1.MissingRequiredCustomFieldError(missingFields);
|
|
70
|
+
else if (requiredFieldsNames?.length > 0) {
|
|
71
|
+
throw new errors_1.MissingRequiredCustomFieldError(requiredFieldsNames);
|
|
82
72
|
}
|
|
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);
|
|
89
73
|
};
|
|
90
74
|
exports.beforeCreate = beforeCreate;
|
|
@@ -1,26 +1,20 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { 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
|
-
|
|
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>;
|
|
6
|
+
export declare const findAll: (where: WhereOptions, options?: any) => Promise<CustomFieldDefinition[]>;
|
|
7
|
+
export declare const findByIds: (ids: string[], options?: any) => Promise<CustomFieldDefinition[]>;
|
|
8
|
+
export declare const findById: (id: string, options?: any) => Promise<CustomFieldDefinition | null>;
|
|
14
9
|
export declare const findByEntityIds: (modelType: string, entityIds: string[], options?: FindOptions & {
|
|
15
10
|
modelOptions?: ModelOptions;
|
|
16
11
|
}) => Promise<CustomFieldDefinition[]>;
|
|
17
12
|
export declare const findByWhere: (where: any) => Promise<CustomFieldDefinition | null>;
|
|
18
13
|
export declare const findDefinitionsByModels: (modelTypes: string[], options?: any) => Promise<CustomFieldDefinition[]>;
|
|
19
14
|
export declare const update: (id: string, data: UpdateCustomFieldDefinition) => Promise<CustomFieldDefinition>;
|
|
20
|
-
export declare const disable: (id: string) => Promise<
|
|
21
|
-
export declare const destroy: (id: string) => Promise<
|
|
15
|
+
export declare const disable: (id: string) => Promise<any>;
|
|
16
|
+
export declare const destroy: (id: string) => Promise<any>;
|
|
22
17
|
/**
|
|
23
18
|
* Return the names of the required fields for a given model
|
|
24
19
|
*/
|
|
25
20
|
export declare const getRequiredFields: (modelType: string, modelId: string | string[], entityId: string | string[], modelOptions?: ModelOptions) => Promise<string[]>;
|
|
26
|
-
export {};
|
|
@@ -24,5 +24,5 @@ export declare const findValuesByModelIds: (modelIds: string[], options?: any) =
|
|
|
24
24
|
*/
|
|
25
25
|
export declare const updateValues: (modelType: string, modelId: string, identifiers: string[], valuesToUpdate: ValuesToUpdate, options?: FindOptions & {
|
|
26
26
|
modelOptions?: ModelOptions;
|
|
27
|
-
}) => Promise<CustomFieldValue[]>;
|
|
27
|
+
}, defineAllDefaults?: boolean) => Promise<CustomFieldValue[]>;
|
|
28
28
|
export declare const deleteValue: (id: string, options?: any) => Promise<any>;
|
package/dist/repository/value.js
CHANGED
|
@@ -84,7 +84,7 @@ const formatFunctions = {
|
|
|
84
84
|
* Create new value record if not exists, but fails if value's definition not exist.
|
|
85
85
|
* Return the updated values
|
|
86
86
|
*/
|
|
87
|
-
const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, options = {}) => {
|
|
87
|
+
const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, options = {}, defineAllDefaults = false) => {
|
|
88
88
|
const names = Object.keys(valuesToUpdate);
|
|
89
89
|
logger_1.default.debug(`custom-fields: updating values for ${modelType} ${modelId}`, {
|
|
90
90
|
names,
|
|
@@ -96,9 +96,11 @@ const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, opt
|
|
|
96
96
|
const where = {
|
|
97
97
|
modelType,
|
|
98
98
|
name: names,
|
|
99
|
-
...(!options.modelOptions?.useEntityIdFromInclude && { entityId: identifiers }),
|
|
100
99
|
};
|
|
101
|
-
|
|
100
|
+
if (!options.modelOptions?.useEntityIdFromInclude) {
|
|
101
|
+
where.entityId = identifiers;
|
|
102
|
+
}
|
|
103
|
+
const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true, transaction, include: modelOptions.include?.(identifiers) }) || [];
|
|
102
104
|
const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
|
|
103
105
|
if (fieldDefinitions.length !== names.length) {
|
|
104
106
|
logger_1.default.warn(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
|
|
@@ -110,8 +112,10 @@ const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, opt
|
|
|
110
112
|
if (valuesWithDisabledDefinitions?.length > 0) {
|
|
111
113
|
logger_1.default.warn(`custom-fields: trying to update disabled values: ${valuesWithDisabledDefinitions.join(', ')}`);
|
|
112
114
|
}
|
|
115
|
+
const visitedFields = new Set();
|
|
113
116
|
const values = names.map((name) => {
|
|
114
117
|
const fieldDefinition = fieldDefinitions.find((def) => def.name === name);
|
|
118
|
+
visitedFields.add(fieldDefinition);
|
|
115
119
|
const formatFunction = formatFunctions[fieldDefinition.fieldType];
|
|
116
120
|
return {
|
|
117
121
|
modelId,
|
|
@@ -120,6 +124,16 @@ const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, opt
|
|
|
120
124
|
customFieldDefinitionId: fieldDefinition.id,
|
|
121
125
|
};
|
|
122
126
|
});
|
|
127
|
+
if (defineAllDefaults) {
|
|
128
|
+
fieldDefinitions.filter((def) => !visitedFields.has(def) && ![null, undefined].includes(def.defaultValue)).forEach(({ id, defaultValue }) => {
|
|
129
|
+
values.push({
|
|
130
|
+
modelId,
|
|
131
|
+
value: defaultValue,
|
|
132
|
+
updatedAt: new Date(),
|
|
133
|
+
customFieldDefinitionId: id,
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
123
137
|
return Promise.all(values.map(async (value) => {
|
|
124
138
|
const [cfv] = await models_1.CustomFieldValue.upsert(value, {
|
|
125
139
|
transaction: options.transaction,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type WhereOptions, type
|
|
1
|
+
import { type WhereOptions, type IncludeOptions } from 'sequelize';
|
|
2
2
|
import { type ModelStatic } from 'sequelize-typescript';
|
|
3
3
|
import { CustomFieldDefinitionType } from '../constants';
|
|
4
4
|
/**
|
|
@@ -14,12 +14,12 @@ import { CustomFieldDefinitionType } from '../constants';
|
|
|
14
14
|
* @param {CustomFieldDefinitionType[]} excludedCustomFieldsTypes - An array of custom field types
|
|
15
15
|
* to exclude from the search
|
|
16
16
|
*
|
|
17
|
-
* @returns {CustomFieldsSearchPayload} - An object containing the
|
|
17
|
+
* @returns {CustomFieldsSearchPayload} - An object containing the INCLUDE clause and WHERE clause
|
|
18
18
|
* for Sequelize.
|
|
19
19
|
*/
|
|
20
20
|
interface CustomFieldsSearchPayload {
|
|
21
21
|
where: WhereOptions;
|
|
22
|
-
|
|
22
|
+
include: IncludeOptions;
|
|
23
23
|
}
|
|
24
24
|
export declare const generateRandomString: (length?: number) => string;
|
|
25
25
|
export declare const generateCustomFieldSearchQueryPayload: (searchTerm: string, model: ModelStatic, entityId: string, customFieldsTypesToExclude?: CustomFieldDefinitionType[]) => CustomFieldsSearchPayload;
|
|
@@ -6,6 +6,7 @@ const sequelize_1 = require("sequelize");
|
|
|
6
6
|
const sequelize_typescript_1 = require("sequelize-typescript");
|
|
7
7
|
const node_crypto_1 = require("node:crypto");
|
|
8
8
|
const constants_1 = require("../constants");
|
|
9
|
+
const models_1 = require("../../models");
|
|
9
10
|
const generateRandomString = (length = 5) => {
|
|
10
11
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
11
12
|
return Array.from({ length }, () => characters.charAt((0, node_crypto_1.randomInt)(characters.length))).join('');
|
|
@@ -14,27 +15,28 @@ exports.generateRandomString = generateRandomString;
|
|
|
14
15
|
const generateCustomFieldSearchQueryPayload = (searchTerm, model, entityId, customFieldsTypesToExclude = [
|
|
15
16
|
constants_1.CustomFieldDefinitionType.DATETIME,
|
|
16
17
|
constants_1.CustomFieldDefinitionType.DATE,
|
|
17
|
-
]) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
]) => ({
|
|
19
|
+
include: {
|
|
20
|
+
model: models_1.CustomFieldValue,
|
|
21
|
+
as: 'cff',
|
|
22
|
+
required: false,
|
|
23
|
+
include: [
|
|
24
|
+
{
|
|
25
|
+
model: models_1.CustomFieldDefinition,
|
|
26
|
+
attributes: [],
|
|
27
|
+
required: true,
|
|
28
|
+
where: {
|
|
29
|
+
entityId,
|
|
30
|
+
modelType: model,
|
|
31
|
+
fieldType: { [sequelize_1.Op.notIn]: customFieldsTypesToExclude },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
],
|
|
32
35
|
where: {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
],
|
|
36
|
+
deletedAt: null,
|
|
37
|
+
value: { [sequelize_1.Op.iLike]: `%${searchTerm}%` },
|
|
36
38
|
},
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
};
|
|
39
|
+
},
|
|
40
|
+
where: sequelize_typescript_1.Sequelize.where(sequelize_typescript_1.Sequelize.col('cff.model_id'), { [sequelize_1.Op.not]: null }),
|
|
41
|
+
});
|
|
40
42
|
exports.generateCustomFieldSearchQueryPayload = generateCustomFieldSearchQueryPayload;
|
package/package.json
CHANGED
package/src/hooks/create.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { WhereOptions } from 'sequelize';
|
|
2
1
|
import logger from '../utils/logger';
|
|
3
2
|
import * as ValueRepo from '../repository/value';
|
|
4
3
|
import * as DefinitionRepo from '../repository/definition';
|
|
@@ -24,54 +23,37 @@ export const beforeCreate = (scopeAttributes: string[], modelOptions: ModelOptio
|
|
|
24
23
|
): Promise<void> => {
|
|
25
24
|
logger.debug('sadot - before create hook');
|
|
26
25
|
const { fields } = options;
|
|
27
|
-
const { include, useEntityIdFromInclude } = modelOptions;
|
|
28
26
|
const modelType = instance.constructor.name;
|
|
29
27
|
|
|
30
28
|
const identifiers = applyScopeToInstance(instance, scopeAttributes);
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
disabled: false,
|
|
35
|
-
...(!useEntityIdFromInclude && { entityId: identifiers }),
|
|
36
|
-
};
|
|
37
|
-
const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: false, transaction: options.transaction, include: include?.(identifiers) });
|
|
38
|
-
const requiredFieldsNames = Array.from(new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)));
|
|
30
|
+
// get all model's required definitions
|
|
31
|
+
const requiredFieldsNames = await DefinitionRepo.getRequiredFields(modelType, instance.id, identifiers, modelOptions);
|
|
39
32
|
|
|
40
33
|
const customFieldsIdx = fields.indexOf('customFields');
|
|
41
|
-
|
|
42
|
-
if ((customFieldsIdx === -1 || !instance.customFields) && requiredFieldsNames?.length > 0) {
|
|
43
|
-
throw new MissingRequiredCustomFieldError(requiredFieldsNames);
|
|
44
|
-
}
|
|
45
|
-
const fieldsWithDefaultValue = fieldDefinitions.filter((def) => ![null, undefined].includes(def.defaultValue));
|
|
46
|
-
if (fieldsWithDefaultValue.length) {
|
|
47
|
-
// eslint-disable-next-line no-param-reassign
|
|
48
|
-
instance.customFields ||= {};
|
|
49
|
-
fieldDefinitions.filter((def) => !(def.name in instance.customFields) && ![null, undefined].includes(def.defaultValue)).forEach(({ name, defaultValue }) => {
|
|
50
|
-
// eslint-disable-next-line no-param-reassign
|
|
51
|
-
instance.customFields[name] = defaultValue;
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
34
|
const { customFields } = instance;
|
|
55
|
-
if (customFieldsIdx
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw new MissingRequiredCustomFieldError(missingFields);
|
|
62
|
-
}
|
|
35
|
+
if (customFieldsIdx > -1 && customFields) {
|
|
36
|
+
const fieldsNames = Object.keys(customFields);
|
|
37
|
+
const missingFields = requiredFieldsNames.filter((name) => !fieldsNames.includes(name));
|
|
38
|
+
if (missingFields?.length > 0) {
|
|
39
|
+
throw new MissingRequiredCustomFieldError(missingFields);
|
|
40
|
+
}
|
|
63
41
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
42
|
+
await ValueRepo.updateValues(
|
|
43
|
+
modelType,
|
|
44
|
+
instance.id,
|
|
45
|
+
identifiers,
|
|
46
|
+
customFields,
|
|
47
|
+
{
|
|
48
|
+
transaction: options.transaction,
|
|
49
|
+
modelOptions,
|
|
50
|
+
},
|
|
51
|
+
true,
|
|
52
|
+
);
|
|
74
53
|
|
|
75
|
-
|
|
76
|
-
|
|
54
|
+
// eslint-disable-next-line no-param-reassign
|
|
55
|
+
fields.splice(customFieldsIdx, 1);
|
|
56
|
+
} else if (requiredFieldsNames?.length > 0) {
|
|
57
|
+
throw new MissingRequiredCustomFieldError(requiredFieldsNames);
|
|
58
|
+
}
|
|
77
59
|
};
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Op,
|
|
3
|
-
type Includeable, type Transaction, type FindOptions, type 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
4
|
import type { ModelOptions } from '../types';
|
|
@@ -9,15 +6,9 @@ import type { ModelOptions } from '../types';
|
|
|
9
6
|
export const create = (data: CreateCustomFieldDefinition): Promise<CustomFieldDefinition> =>
|
|
10
7
|
CustomFieldDefinition.create(data);
|
|
11
8
|
|
|
12
|
-
interface SadotFindOptions {
|
|
13
|
-
withDisabled?: boolean;
|
|
14
|
-
transaction?: Transaction;
|
|
15
|
-
include?: Includeable | Includeable[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
9
|
export const findAll = (
|
|
19
10
|
where: WhereOptions,
|
|
20
|
-
options:
|
|
11
|
+
options: any = { withDisabled: false },
|
|
21
12
|
): Promise<CustomFieldDefinition[]> => {
|
|
22
13
|
const queryModel = options.withDisabled
|
|
23
14
|
? CustomFieldDefinition.unscoped()
|
|
@@ -33,12 +24,12 @@ export const findAll = (
|
|
|
33
24
|
|
|
34
25
|
export const findByIds = (
|
|
35
26
|
ids: string[],
|
|
36
|
-
options:
|
|
27
|
+
options: any = { withDisabled: false },
|
|
37
28
|
): Promise<CustomFieldDefinition[]> => findAll({ id: { [Op.in]: ids } }, options);
|
|
38
29
|
|
|
39
30
|
export const findById = (
|
|
40
31
|
id: string,
|
|
41
|
-
options:
|
|
32
|
+
options: any = { withDisabled: false },
|
|
42
33
|
): Promise<CustomFieldDefinition | null> => {
|
|
43
34
|
const { withDisabled } = options;
|
|
44
35
|
if (withDisabled) {
|
|
@@ -96,13 +87,13 @@ export const update = async (
|
|
|
96
87
|
return updatedDefinition;
|
|
97
88
|
};
|
|
98
89
|
|
|
99
|
-
export const disable = (id: string): Promise<
|
|
90
|
+
export const disable = (id: string): Promise<any> =>
|
|
100
91
|
CustomFieldDefinition.update(
|
|
101
92
|
{ disabled: true },
|
|
102
93
|
{ where: { id } },
|
|
103
94
|
);
|
|
104
95
|
|
|
105
|
-
export const destroy = (id: string): Promise<
|
|
96
|
+
export const destroy = (id: string): Promise<any> =>
|
|
106
97
|
CustomFieldDefinition.destroy({ where: { id } });
|
|
107
98
|
|
|
108
99
|
/**
|
package/src/repository/value.ts
CHANGED
|
@@ -67,6 +67,7 @@ export const updateValues = async (
|
|
|
67
67
|
identifiers: string[],
|
|
68
68
|
valuesToUpdate: ValuesToUpdate,
|
|
69
69
|
options: FindOptions & { modelOptions?: ModelOptions } = {},
|
|
70
|
+
defineAllDefaults = false,
|
|
70
71
|
): Promise<CustomFieldValue[]> => {
|
|
71
72
|
const names = Object.keys(valuesToUpdate);
|
|
72
73
|
logger.debug(`custom-fields: updating values for ${modelType} ${modelId}`, {
|
|
@@ -80,10 +81,12 @@ export const updateValues = async (
|
|
|
80
81
|
const where: WhereOptions = {
|
|
81
82
|
modelType,
|
|
82
83
|
name: names,
|
|
83
|
-
...(!options.modelOptions?.useEntityIdFromInclude && { entityId: identifiers }),
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
if (!options.modelOptions?.useEntityIdFromInclude) {
|
|
87
|
+
where.entityId = identifiers;
|
|
88
|
+
}
|
|
89
|
+
const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true, transaction, include: modelOptions.include?.(identifiers) }) || [];
|
|
87
90
|
|
|
88
91
|
const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
|
|
89
92
|
if (fieldDefinitions.length !== names.length) {
|
|
@@ -98,8 +101,11 @@ export const updateValues = async (
|
|
|
98
101
|
logger.warn(`custom-fields: trying to update disabled values: ${valuesWithDisabledDefinitions.join(', ')}`);
|
|
99
102
|
}
|
|
100
103
|
|
|
104
|
+
const visitedFields = new Set<CustomFieldDefinition>();
|
|
105
|
+
|
|
101
106
|
const values: CreateCustomFieldValue[] = names.map((name) => {
|
|
102
107
|
const fieldDefinition = fieldDefinitions.find((def) => def.name === name);
|
|
108
|
+
visitedFields.add(fieldDefinition);
|
|
103
109
|
const formatFunction = formatFunctions[fieldDefinition.fieldType];
|
|
104
110
|
return {
|
|
105
111
|
modelId,
|
|
@@ -109,6 +115,17 @@ export const updateValues = async (
|
|
|
109
115
|
};
|
|
110
116
|
});
|
|
111
117
|
|
|
118
|
+
if (defineAllDefaults) {
|
|
119
|
+
fieldDefinitions.filter((def) => !visitedFields.has(def) && ![null, undefined].includes(def.defaultValue)).forEach(({ id, defaultValue }) => {
|
|
120
|
+
values.push({
|
|
121
|
+
modelId,
|
|
122
|
+
value: defaultValue,
|
|
123
|
+
updatedAt: new Date(),
|
|
124
|
+
customFieldDefinitionId: id,
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
112
129
|
return Promise.all(values.map(async (value) => {
|
|
113
130
|
const [cfv] = await CustomFieldValue.upsert(value, {
|
|
114
131
|
transaction: options.transaction,
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/* eslint-disable import/prefer-default-export */
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type WhereOptions, Op, type IncludeOptions,
|
|
4
|
+
} from 'sequelize';
|
|
3
5
|
import { type ModelStatic, Sequelize } from 'sequelize-typescript';
|
|
4
6
|
import { randomInt } from 'node:crypto';
|
|
5
7
|
import { CustomFieldDefinitionType } from '../constants';
|
|
8
|
+
import { CustomFieldDefinition, CustomFieldValue } from '../../models';
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Builds a WHERE clause and replacements for free-text search by custom fields.
|
|
@@ -17,13 +20,13 @@ import { CustomFieldDefinitionType } from '../constants';
|
|
|
17
20
|
* @param {CustomFieldDefinitionType[]} excludedCustomFieldsTypes - An array of custom field types
|
|
18
21
|
* to exclude from the search
|
|
19
22
|
*
|
|
20
|
-
* @returns {CustomFieldsSearchPayload} - An object containing the
|
|
23
|
+
* @returns {CustomFieldsSearchPayload} - An object containing the INCLUDE clause and WHERE clause
|
|
21
24
|
* for Sequelize.
|
|
22
25
|
*/
|
|
23
26
|
|
|
24
27
|
interface CustomFieldsSearchPayload {
|
|
25
28
|
where: WhereOptions;
|
|
26
|
-
|
|
29
|
+
include: IncludeOptions;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
export const generateRandomString = (length = 5): string => {
|
|
@@ -39,28 +42,27 @@ export const generateCustomFieldSearchQueryPayload = (
|
|
|
39
42
|
CustomFieldDefinitionType.DATETIME,
|
|
40
43
|
CustomFieldDefinitionType.DATE,
|
|
41
44
|
],
|
|
42
|
-
): CustomFieldsSearchPayload => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
): CustomFieldsSearchPayload => ({
|
|
46
|
+
include: {
|
|
47
|
+
model: CustomFieldValue,
|
|
48
|
+
as: 'cff',
|
|
49
|
+
required: false,
|
|
50
|
+
include: [
|
|
51
|
+
{
|
|
52
|
+
model: CustomFieldDefinition,
|
|
53
|
+
attributes: [],
|
|
54
|
+
required: true,
|
|
55
|
+
where: {
|
|
56
|
+
entityId,
|
|
57
|
+
modelType: model,
|
|
58
|
+
fieldType: { [Op.notIn]: customFieldsTypesToExclude },
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
],
|
|
59
62
|
where: {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
],
|
|
63
|
+
deletedAt: null,
|
|
64
|
+
value: { [Op.iLike]: `%${searchTerm}%` },
|
|
63
65
|
},
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
};
|
|
66
|
+
},
|
|
67
|
+
where: Sequelize.where(Sequelize.col('cff.model_id'), { [Op.not]: null }),
|
|
68
|
+
});
|