@autofleet/sadot 0.0.1-beta.8 → 0.0.2-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/jest.config.d.ts +12 -0
- package/dist/src/api/index.d.ts +2 -0
- package/dist/{api → src/api}/index.js +0 -1
- package/dist/src/api/v1/definition/index.d.ts +2 -0
- package/dist/{api → src/api}/v1/definition/index.js +23 -27
- package/dist/src/api/v1/definition/validations.d.ts +2 -0
- package/dist/{api → src/api}/v1/definition/validations.js +10 -10
- package/dist/src/api/v1/errors.d.ts +2 -0
- package/dist/{api → src/api}/v1/errors.js +0 -1
- package/dist/src/api/v1/index.d.ts +2 -0
- package/dist/{api → src/api}/v1/index.js +1 -2
- package/dist/src/errors/index.d.ts +16 -0
- package/dist/src/errors/index.js +45 -0
- package/dist/src/events/index.d.ts +4 -0
- package/dist/{events → src/events}/index.js +21 -2
- package/dist/src/hooks/create.d.ts +9 -0
- package/dist/{hooks → src/hooks}/create.js +14 -17
- package/dist/src/hooks/enrich.d.ts +5 -0
- package/dist/src/hooks/enrich.js +118 -0
- package/dist/src/hooks/find.d.ts +1 -0
- package/dist/src/hooks/find.js +29 -0
- package/dist/src/hooks/index.d.ts +6 -0
- package/dist/{hooks → src/hooks}/index.js +3 -3
- package/dist/src/hooks/update.d.ts +9 -0
- package/dist/{hooks → src/hooks}/update.js +7 -22
- package/dist/src/hooks/workaround.d.ts +10 -0
- package/dist/{hooks → src/hooks}/workaround.js +3 -13
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.js +105 -0
- package/dist/src/models/CustomFieldDefinition.d.ts +23 -0
- package/dist/{models → src/models}/CustomFieldDefinition.js +22 -18
- package/dist/src/models/CustomFieldValue.d.ts +15 -0
- package/dist/{models → src/models}/CustomFieldValue.js +24 -40
- package/dist/src/models/index.d.ts +8 -0
- package/dist/src/models/index.js +87 -0
- package/dist/src/models/tests/AssociatedTestModel.d.ts +12 -0
- package/dist/{models → src/models}/tests/AssociatedTestModel.js +0 -1
- package/dist/src/models/tests/TestModel.d.ts +11 -0
- package/dist/{models → src/models}/tests/TestModel.js +0 -1
- package/dist/src/repository/definition.d.ts +17 -0
- package/dist/src/repository/definition.js +80 -0
- package/dist/src/repository/value.d.ts +24 -0
- package/dist/{repository → src/repository}/value.js +22 -31
- package/dist/src/tests/api/test-api.d.ts +2 -0
- package/dist/{tests → src/tests}/api/test-api.js +12 -22
- package/dist/src/tests/helpers/database-config.d.ts +15 -0
- package/dist/{tests → src/tests}/helpers/database-config.js +0 -1
- package/dist/src/tests/helpers/index.d.ts +2 -0
- package/dist/src/tests/helpers/index.js +18 -0
- package/dist/src/tests/mocks/definition.mock.d.ts +37 -0
- package/dist/{tests/mocks/index.js → src/tests/mocks/definition.mock.js} +20 -17
- package/dist/src/tests/mocks/events.mock.d.ts +3 -0
- package/dist/{tests → src/tests}/mocks/events.mock.js +0 -1
- package/dist/src/tests/mocks/testModel.d.ts +12 -0
- package/dist/{tests → src/tests}/mocks/testModel.js +6 -16
- package/dist/src/types/definition/index.d.ts +23 -0
- package/dist/{types → src/types}/definition/index.js +0 -1
- package/dist/src/types/index.d.ts +13 -0
- package/dist/{types → src/types}/index.js +0 -1
- package/dist/src/types/value/index.d.ts +15 -0
- package/dist/{types → src/types}/value/index.js +0 -1
- package/dist/src/utils/constants/index.d.ts +1 -0
- package/dist/src/utils/constants/index.js +5 -0
- package/dist/src/utils/db/index.d.ts +4 -0
- package/dist/{utils → src/utils}/db/index.js +8 -1
- package/dist/src/utils/logger/index.d.ts +2 -0
- package/dist/{utils → src/utils}/logger/index.js +2 -2
- package/dist/src/utils/validations/custom-fields.d.ts +2 -0
- package/dist/{utils → src/utils}/validations/custom-fields.js +0 -1
- package/dist/src/utils/validations/custom.d.ts +15 -0
- package/dist/src/utils/validations/custom.js +42 -0
- package/dist/src/utils/validations/index.d.ts +2 -0
- package/dist/{utils → src/utils}/validations/index.js +0 -1
- package/dist/src/utils/validations/type.d.ts +18 -0
- package/dist/src/utils/validations/type.js +50 -0
- package/dist/src/utils/validations/validators.d.ts +12 -0
- package/dist/src/utils/validations/validators.js +33 -0
- package/package.json +3 -1
- package/src/api/v1/definition/index.ts +15 -8
- package/src/api/v1/definition/validations.ts +11 -25
- package/src/api/v1/index.ts +1 -1
- package/src/errors/index.ts +42 -0
- package/src/events/index.ts +23 -1
- package/src/hooks/create.ts +8 -3
- package/src/hooks/enrich.ts +125 -0
- package/src/hooks/find.ts +9 -102
- package/src/hooks/index.ts +3 -2
- package/src/hooks/update.ts +4 -14
- package/src/index.ts +53 -17
- package/src/models/CustomFieldDefinition.ts +23 -16
- package/src/models/CustomFieldValue.ts +7 -7
- package/src/models/index.ts +72 -16
- package/src/repository/definition.ts +26 -39
- package/src/repository/value.ts +6 -5
- package/src/tests/mocks/{index.ts → definition.mock.ts} +2 -4
- package/src/tests/mocks/testModel.ts +8 -3
- package/src/types/index.ts +4 -6
- package/src/utils/constants/index.ts +2 -0
- package/src/utils/db/index.ts +7 -0
- package/src/utils/logger/index.ts +3 -1
- package/src/utils/validations/custom.ts +26 -44
- package/src/utils/validations/type.ts +23 -6
- package/src/utils/validations/validators.ts +34 -0
- package/tsconfig.json +9 -25
- package/dist/api/index.js.map +0 -1
- package/dist/api/v1/definition/index.js.map +0 -1
- package/dist/api/v1/definition/validations.js.map +0 -1
- package/dist/api/v1/errors.js.map +0 -1
- package/dist/api/v1/index.js.map +0 -1
- package/dist/events/index.js.map +0 -1
- package/dist/hooks/create.js.map +0 -1
- package/dist/hooks/find.js +0 -127
- package/dist/hooks/find.js.map +0 -1
- package/dist/hooks/index.js.map +0 -1
- package/dist/hooks/update.js.map +0 -1
- package/dist/hooks/workaround.js.map +0 -1
- package/dist/index.js +0 -80
- package/dist/index.js.map +0 -1
- package/dist/models/CustomFieldDefinition.js.map +0 -1
- package/dist/models/CustomFieldValue.js.map +0 -1
- package/dist/models/index.js +0 -50
- package/dist/models/index.js.map +0 -1
- package/dist/models/tests/AssociatedTestModel.js.map +0 -1
- package/dist/models/tests/TestModel.js.map +0 -1
- package/dist/repository/definition.js +0 -107
- package/dist/repository/definition.js.map +0 -1
- package/dist/repository/value.js.map +0 -1
- package/dist/tests/api/test-api.js.map +0 -1
- package/dist/tests/helpers/database-config.js.map +0 -1
- package/dist/tests/helpers/index.js +0 -28
- package/dist/tests/helpers/index.js.map +0 -1
- package/dist/tests/mocks/events.mock.js.map +0 -1
- package/dist/tests/mocks/index.js.map +0 -1
- package/dist/tests/mocks/testModel.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/types/definition/index.js.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/types/value/index.js.map +0 -1
- package/dist/utils/db/index.js.map +0 -1
- package/dist/utils/logger/index.js.map +0 -1
- package/dist/utils/validations/custom-fields.js.map +0 -1
- package/dist/utils/validations/custom.js +0 -59
- package/dist/utils/validations/custom.js.map +0 -1
- package/dist/utils/validations/index.js.map +0 -1
- package/dist/utils/validations/type.js +0 -32
- package/dist/utils/validations/type.js.map +0 -1
package/src/index.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { Application } from 'express';
|
|
2
2
|
import { DataTypes } from 'sequelize';
|
|
3
|
-
import {
|
|
3
|
+
import { Sequelize } from 'sequelize-typescript';
|
|
4
|
+
import { initTables, initTestModels } from './models';
|
|
4
5
|
import api from './api';
|
|
5
6
|
import {
|
|
6
7
|
beforeFind,
|
|
7
|
-
|
|
8
|
+
enrichResults,
|
|
8
9
|
beforeBulkUpdate,
|
|
9
10
|
beforeUpdate,
|
|
10
11
|
beforeCreate,
|
|
@@ -20,19 +21,27 @@ const addHooks = (models: ModelOptions[], getModel: ModelFetcher) => {
|
|
|
20
21
|
models.forEach(async ({ name, scopeAttributes }) => {
|
|
21
22
|
try {
|
|
22
23
|
const model = getModel(name);
|
|
23
|
-
if (!model)
|
|
24
|
+
if (!model) {
|
|
25
|
+
logger.warn('sadot - tried to addHooks to a model that does not exist yet', {
|
|
26
|
+
name,
|
|
27
|
+
scopeAttributes,
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
24
31
|
model.rawAttributes.customFields = {
|
|
25
32
|
type: DataTypes.VIRTUAL,
|
|
26
33
|
};
|
|
27
34
|
model.refreshAttributes();
|
|
28
35
|
// TODO: Uncomment after tests are passed
|
|
29
36
|
// model.addHook('afterFind', workaround);
|
|
30
|
-
model.addHook('beforeFind', beforeFind(scopeAttributes));
|
|
31
|
-
model.addHook('beforeBulkCreate', beforeBulkCreate);
|
|
32
|
-
model.addHook('
|
|
33
|
-
model.addHook('
|
|
34
|
-
model.addHook('
|
|
35
|
-
model.addHook('
|
|
37
|
+
model.addHook('beforeFind', 'sadot-beforeFind', beforeFind(scopeAttributes));
|
|
38
|
+
model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', beforeBulkCreate);
|
|
39
|
+
model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', beforeBulkUpdate);
|
|
40
|
+
model.addHook('beforeCreate', 'sadot-beforeCreate', beforeCreate(scopeAttributes));
|
|
41
|
+
model.addHook('beforeUpdate', 'sadot-beforeUpdate', beforeUpdate(scopeAttributes));
|
|
42
|
+
model.addHook('afterFind', 'sadot-afterFind', enrichResults(name, scopeAttributes));
|
|
43
|
+
model.addHook('afterUpdate', 'sadot-afterUpdate', enrichResults(name, scopeAttributes));
|
|
44
|
+
model.addHook('afterCreate', 'sadot-afterCreate', enrichResults(name, scopeAttributes));
|
|
36
45
|
} catch (e) {
|
|
37
46
|
logger.error(`Could not add custom fields hook to model ${name}. `, e);
|
|
38
47
|
}
|
|
@@ -47,19 +56,46 @@ const useCustomFields = async (
|
|
|
47
56
|
app: Application | null,
|
|
48
57
|
getModel: ModelFetcher,
|
|
49
58
|
options: CustomFieldOptions,
|
|
50
|
-
): Promise<
|
|
51
|
-
const {
|
|
52
|
-
// deepAfterFindWorkAround(sequelize);
|
|
59
|
+
): Promise<Sequelize> => {
|
|
60
|
+
const { models } = options;
|
|
53
61
|
if (app) {
|
|
54
62
|
app.use('/api', api);
|
|
55
63
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
await
|
|
59
|
-
} else {
|
|
60
|
-
await initTables(innerSequelize);
|
|
64
|
+
const sequelize = initDB(options.databaseConfig);
|
|
65
|
+
if (process.env.NODE_ENV === 'test') {
|
|
66
|
+
await initTestModels(sequelize);
|
|
61
67
|
}
|
|
62
68
|
addHooks(models, getModel);
|
|
69
|
+
await initTables(sequelize, options.getUser);
|
|
70
|
+
logger.debug('sadot - custom fields finished initializing with models', models);
|
|
71
|
+
return sequelize;
|
|
63
72
|
};
|
|
64
73
|
|
|
65
74
|
export default useCustomFields;
|
|
75
|
+
|
|
76
|
+
const removeHooks = (models: ModelOptions[], getModel: ModelFetcher) => {
|
|
77
|
+
models.forEach(async ({ name }) => {
|
|
78
|
+
try {
|
|
79
|
+
const model = getModel(name);
|
|
80
|
+
if (!model) return;
|
|
81
|
+
if (model.rawAttributes.customFields) {
|
|
82
|
+
delete model.rawAttributes.customFields;
|
|
83
|
+
model.refreshAttributes();
|
|
84
|
+
}
|
|
85
|
+
// model.removeHook('afterFind', 'sadot-workaround');
|
|
86
|
+
model.removeHook('beforeFind', 'sadot-beforeFind');
|
|
87
|
+
model.removeHook('beforeBulkCreate', 'sadot-beforeBulkCreate');
|
|
88
|
+
model.removeHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate');
|
|
89
|
+
model.removeHook('beforeCreate', 'sadot-beforeCreate');
|
|
90
|
+
model.removeHook('beforeUpdate', 'sadot-beforeUpdate');
|
|
91
|
+
model.removeHook('afterFind', 'sadot-afterFind');
|
|
92
|
+
model.removeHook('afterUpdate', 'sadot-afterUpdate');
|
|
93
|
+
} catch (e) {
|
|
94
|
+
logger.error(`Could not add custom fields hook to model ${name}. `, e);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const disableCustomFields = (models, getModel) => {
|
|
100
|
+
removeHooks(models, getModel);
|
|
101
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
/* eslint-disable indent */
|
|
2
3
|
import {
|
|
3
4
|
Table,
|
|
4
5
|
Column,
|
|
@@ -9,31 +10,32 @@ import {
|
|
|
9
10
|
BeforeCreate,
|
|
10
11
|
DefaultScope,
|
|
11
12
|
AfterSave,
|
|
13
|
+
Is,
|
|
12
14
|
} from 'sequelize-typescript';
|
|
15
|
+
import { validateValidation } from '../utils/validations/custom';
|
|
16
|
+
import { CustomFieldDefinitionType } from '../utils/validations/type';
|
|
13
17
|
import { CustomFieldValue } from '.';
|
|
14
18
|
import { sendDimEvent } from '../events';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Supported custom field types
|
|
18
|
-
*/
|
|
19
|
-
// eslint-disable-next-line no-shadow
|
|
20
|
-
export enum CustomFieldDefinitionType {
|
|
21
|
-
NUMBER = 'number',
|
|
22
|
-
BOOLEAN = 'boolean',
|
|
23
|
-
DATE = 'date',
|
|
24
|
-
DATETIME = 'datetime',
|
|
25
|
-
TEXT = 'text',
|
|
26
|
-
IMAGE = 'image',
|
|
27
|
-
ENUM = 'enum',
|
|
28
|
-
}
|
|
19
|
+
import { UnsupportedCustomFieldTypeError, UnsupportedCustomValidationError } from '../errors';
|
|
29
20
|
|
|
30
21
|
@DefaultScope(() => ({ where: { disabled: false } }))
|
|
31
22
|
@Table({
|
|
32
23
|
indexes: [
|
|
33
|
-
|
|
24
|
+
{
|
|
25
|
+
name: 'unique_name_model_type',
|
|
26
|
+
fields: ['model_type', 'entity_id', 'name'],
|
|
27
|
+
unique: true,
|
|
28
|
+
},
|
|
34
29
|
],
|
|
35
30
|
timestamps: true,
|
|
36
|
-
|
|
31
|
+
validate: {
|
|
32
|
+
validationByType(this: CustomFieldDefinition) {
|
|
33
|
+
if (!validateValidation(this.fieldType, this.validation)) {
|
|
34
|
+
throw new UnsupportedCustomValidationError(`Validation provided for "${this.fieldType}" is not supported`);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
})
|
|
37
39
|
class CustomFieldDefinition extends Model {
|
|
38
40
|
@PrimaryKey
|
|
39
41
|
@Column({
|
|
@@ -54,6 +56,11 @@ class CustomFieldDefinition extends Model {
|
|
|
54
56
|
})
|
|
55
57
|
displayName?: string; // Defaulted to name with beforeCreate hook
|
|
56
58
|
|
|
59
|
+
@Is('SupportedType', (value) => {
|
|
60
|
+
if (!Object.values(CustomFieldDefinitionType).includes(value as CustomFieldDefinitionType)) {
|
|
61
|
+
throw new UnsupportedCustomFieldTypeError(`"${value}" is not a supported type.`);
|
|
62
|
+
}
|
|
63
|
+
})
|
|
57
64
|
@Column({
|
|
58
65
|
type: DataType.STRING,
|
|
59
66
|
allowNull: false,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
/* eslint-disable indent */
|
|
2
3
|
import {
|
|
3
4
|
Table,
|
|
4
5
|
Column,
|
|
@@ -13,16 +14,18 @@ import {
|
|
|
13
14
|
BeforeUpdate,
|
|
14
15
|
BeforeBulkCreate,
|
|
15
16
|
BeforeBulkUpdate,
|
|
17
|
+
Scopes,
|
|
16
18
|
} from 'sequelize-typescript';
|
|
17
19
|
import { sendDimEvent } from '../events';
|
|
18
20
|
import { CustomFieldDefinition } from '.';
|
|
19
21
|
import validateValue from '../utils/validations';
|
|
20
22
|
import * as CustomFieldDefinitionRepo from '../repository/definition';
|
|
21
23
|
import logger from '../utils/logger';
|
|
24
|
+
import { InvalidValueError } from '../errors';
|
|
22
25
|
|
|
23
26
|
@Table({
|
|
24
27
|
timestamps: true,
|
|
25
|
-
|
|
28
|
+
})
|
|
26
29
|
class CustomFieldValue extends Model {
|
|
27
30
|
@PrimaryKey
|
|
28
31
|
@Column({
|
|
@@ -41,7 +44,7 @@ class CustomFieldValue extends Model {
|
|
|
41
44
|
|
|
42
45
|
@Column({
|
|
43
46
|
type: DataType.JSONB,
|
|
44
|
-
allowNull:
|
|
47
|
+
allowNull: true,
|
|
45
48
|
})
|
|
46
49
|
value!: any;
|
|
47
50
|
|
|
@@ -60,7 +63,6 @@ class CustomFieldValue extends Model {
|
|
|
60
63
|
@BeforeBulkCreate
|
|
61
64
|
@BeforeBulkUpdate
|
|
62
65
|
static async validateValues(instances: CustomFieldValue[]): Promise<void> {
|
|
63
|
-
logger.info('CustomFieldValue BeforeBulkCreate/BeforeBulkUpdate', { instances });
|
|
64
66
|
const ids = instances.map((instance) => instance.customFieldDefinitionId);
|
|
65
67
|
const uniqueIds = [...new Set(ids)];
|
|
66
68
|
const definitions = await CustomFieldDefinitionRepo.findByIds(
|
|
@@ -80,7 +82,7 @@ class CustomFieldValue extends Model {
|
|
|
80
82
|
.find((definition) => definition.id === instance.customFieldDefinitionId);
|
|
81
83
|
const isValid = validateValue(instance.value, fieldType, validation);
|
|
82
84
|
if (!isValid) {
|
|
83
|
-
throw new
|
|
85
|
+
throw new InvalidValueError(instance.value, fieldType);
|
|
84
86
|
}
|
|
85
87
|
});
|
|
86
88
|
}
|
|
@@ -89,20 +91,18 @@ class CustomFieldValue extends Model {
|
|
|
89
91
|
@BeforeCreate
|
|
90
92
|
@BeforeUpsert
|
|
91
93
|
static async validateValue(instance: CustomFieldValue): Promise<void> {
|
|
92
|
-
logger.info('CustomFieldValue BeforeCreate/BeforeUpdate', { instance });
|
|
93
94
|
const { customFieldDefinitionId } = instance;
|
|
94
95
|
// eslint-disable-next-line max-len
|
|
95
96
|
const cfd = await CustomFieldDefinitionRepo.findById(customFieldDefinitionId, { withDisabled: true });
|
|
96
97
|
const { validation, fieldType } = cfd;
|
|
97
98
|
const isValid = validateValue(instance.value, fieldType, validation);
|
|
98
99
|
if (!isValid) {
|
|
99
|
-
throw new
|
|
100
|
+
throw new InvalidValueError(instance.value, fieldType);
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
@AfterUpsert
|
|
104
105
|
static afterSaveHandler(instance: CustomFieldValue, options): void {
|
|
105
|
-
logger.info('CustomFieldValue afterSave', { instance });
|
|
106
106
|
if (options.transaction) {
|
|
107
107
|
options.transaction.afterCommit(() => sendDimEvent(instance[0]));
|
|
108
108
|
} else {
|
package/src/models/index.ts
CHANGED
|
@@ -1,39 +1,94 @@
|
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
|
2
|
+
import { DataTypes } from 'sequelize';
|
|
2
3
|
import type { Sequelize } from 'sequelize-typescript';
|
|
3
4
|
import logger from '../utils/logger';
|
|
4
|
-
// import addPermanentHooks from '../hooks/permanent-hooks';
|
|
5
|
-
|
|
6
5
|
import CustomFieldDefinition from './CustomFieldDefinition';
|
|
7
6
|
import CustomFieldValue from './CustomFieldValue';
|
|
8
7
|
import TestModel from './tests/TestModel';
|
|
9
8
|
import AssociatedTestModel from './tests/AssociatedTestModel';
|
|
10
9
|
|
|
11
|
-
// export const CustomFieldDefinition = CustomFieldDefinitionModel;
|
|
12
|
-
// export { default as CustomFieldDefinition } from './custom-field-definition.model';
|
|
13
|
-
|
|
14
10
|
const productionModels = [CustomFieldDefinition, CustomFieldValue];
|
|
15
|
-
const testModels = [
|
|
11
|
+
const testModels = [TestModel, AssociatedTestModel];
|
|
12
|
+
|
|
13
|
+
const SADOT_MIGRATION_PREFIX = 'sadot-migration';
|
|
14
|
+
const SCHEMA_VERSION = 1;
|
|
15
|
+
const CUSTOM_FIELDS_SCHEMA_VERSION = `${SADOT_MIGRATION_PREFIX}_${SCHEMA_VERSION}`;
|
|
16
16
|
|
|
17
|
-
const initTables = async (sequelize: Sequelize): Promise<void> => {
|
|
17
|
+
const initTables = async (sequelize: Sequelize, getUser): Promise<void> => {
|
|
18
18
|
logger.info('custom-fields: initialize custom-fields tables');
|
|
19
19
|
// Detect models and import them to the orm
|
|
20
20
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
21
21
|
if (!sequelize.addModels) {
|
|
22
22
|
throw new Error('sequelize instance must have addModels function');
|
|
23
23
|
}
|
|
24
|
-
sequelize.addModels(
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
sequelize.addModels(productionModels);
|
|
25
|
+
|
|
26
|
+
CustomFieldDefinition.addScope('userScope', () => {
|
|
27
|
+
const user = getUser();
|
|
28
|
+
if (user?.permissions) {
|
|
29
|
+
return {
|
|
30
|
+
where: {
|
|
31
|
+
entityId: [
|
|
32
|
+
...Object.keys(user.permissions.fleets),
|
|
33
|
+
...Object.keys(user.permissions.businessModels),
|
|
34
|
+
...Object.keys(user.permissions.demandSources),
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {};
|
|
40
|
+
});
|
|
27
41
|
|
|
28
42
|
logger.info('custom-fields: models added');
|
|
29
|
-
await CustomFieldDefinition.sync({ alter: true });
|
|
30
|
-
await CustomFieldValue.sync({ alter: true });
|
|
31
43
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
const SequelizeMeta = sequelize.define(
|
|
45
|
+
'SequelizeMeta',
|
|
46
|
+
{
|
|
47
|
+
name: {
|
|
48
|
+
type: DataTypes.STRING,
|
|
49
|
+
allowNull: false,
|
|
50
|
+
unique: true,
|
|
51
|
+
primaryKey: true,
|
|
52
|
+
autoIncrement: false,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
tableName: 'SequelizeMeta',
|
|
57
|
+
timestamps: false,
|
|
58
|
+
schema: 'public',
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
const migrations = await SequelizeMeta.findAll({ raw: true });
|
|
62
|
+
const currentSadotSchemaVersion = migrations
|
|
63
|
+
.reverse().find((m: any) => m.name.includes(SADOT_MIGRATION_PREFIX));
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
!currentSadotSchemaVersion
|
|
67
|
+
|| (currentSadotSchemaVersion as any).name !== CUSTOM_FIELDS_SCHEMA_VERSION
|
|
68
|
+
) {
|
|
69
|
+
await CustomFieldDefinition.sync({ alter: true });
|
|
70
|
+
await CustomFieldValue.sync({ alter: true });
|
|
71
|
+
await SequelizeMeta.create({ name: CUSTOM_FIELDS_SCHEMA_VERSION });
|
|
72
|
+
logger.info('custom-fields: models synced');
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const initTestModels = async (sequelize: Sequelize): Promise<void> => {
|
|
77
|
+
logger.info('custom-fields: initialize custom-fields test models');
|
|
78
|
+
// Detect models and import them to the orm
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
80
|
+
if (!sequelize.addModels) {
|
|
81
|
+
throw new Error('sequelize instance must have addModels function');
|
|
35
82
|
}
|
|
36
|
-
|
|
83
|
+
|
|
84
|
+
sequelize.addModels(testModels);
|
|
85
|
+
await sequelize.dropSchema('custom-fields', { logging: false });
|
|
86
|
+
await sequelize.createSchema('custom-fields', { logging: false });
|
|
87
|
+
|
|
88
|
+
logger.info('custom-fields: test models added');
|
|
89
|
+
await TestModel.sync({ alter: true });
|
|
90
|
+
await AssociatedTestModel.sync({ alter: true });
|
|
91
|
+
logger.info('custom-fields: test models synced');
|
|
37
92
|
};
|
|
38
93
|
|
|
39
94
|
export {
|
|
@@ -42,4 +97,5 @@ export {
|
|
|
42
97
|
TestModel,
|
|
43
98
|
AssociatedTestModel,
|
|
44
99
|
initTables,
|
|
100
|
+
initTestModels,
|
|
45
101
|
};
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import { Op, WhereOptions } from 'sequelize';
|
|
2
|
-
import { CustomFieldDefinition
|
|
2
|
+
import { CustomFieldDefinition } from '../models';
|
|
3
3
|
import type { CreateCustomFieldDefinition, UpdateCustomFieldDefinition } from '../types/definition';
|
|
4
4
|
|
|
5
5
|
export const create = (data: CreateCustomFieldDefinition): Promise<CustomFieldDefinition> =>
|
|
6
6
|
CustomFieldDefinition.create(data);
|
|
7
7
|
|
|
8
8
|
export const findAll = (
|
|
9
|
-
|
|
9
|
+
where: any,
|
|
10
10
|
options: any = { withDisabled: false },
|
|
11
11
|
): Promise<CustomFieldDefinition[]> => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return CustomFieldDefinition.findAll({
|
|
20
|
-
where: query,
|
|
21
|
-
include: [CustomFieldValue],
|
|
12
|
+
const queryModel = options.withDisabled
|
|
13
|
+
? CustomFieldDefinition.unscoped()
|
|
14
|
+
: CustomFieldDefinition;
|
|
15
|
+
return queryModel.scope('userScope').findAll({
|
|
16
|
+
where,
|
|
17
|
+
transaction: options.transaction,
|
|
18
|
+
raw: true,
|
|
22
19
|
});
|
|
23
20
|
};
|
|
24
21
|
|
|
@@ -33,13 +30,9 @@ export const findById = (
|
|
|
33
30
|
): Promise<CustomFieldDefinition | null> => {
|
|
34
31
|
const { withDisabled } = options;
|
|
35
32
|
if (withDisabled) {
|
|
36
|
-
return CustomFieldDefinition.unscoped().findByPk(id
|
|
37
|
-
include: [CustomFieldValue],
|
|
38
|
-
});
|
|
33
|
+
return CustomFieldDefinition.unscoped().scope('userScope').findByPk(id);
|
|
39
34
|
}
|
|
40
|
-
return CustomFieldDefinition.findByPk(id
|
|
41
|
-
include: [CustomFieldValue],
|
|
42
|
-
});
|
|
35
|
+
return CustomFieldDefinition.scope('userScope').findByPk(id);
|
|
43
36
|
};
|
|
44
37
|
|
|
45
38
|
export const findByEntityId = async (
|
|
@@ -50,10 +43,22 @@ export const findByEntityId = async (
|
|
|
50
43
|
transaction: options.transaction,
|
|
51
44
|
});
|
|
52
45
|
|
|
46
|
+
export const findByEntityIds = async (
|
|
47
|
+
modelType: string,
|
|
48
|
+
entityIds: string[],
|
|
49
|
+
options: any = {},
|
|
50
|
+
): Promise<CustomFieldDefinition[]> => CustomFieldDefinition.findAll({
|
|
51
|
+
where: {
|
|
52
|
+
modelType,
|
|
53
|
+
entityId: entityIds,
|
|
54
|
+
},
|
|
55
|
+
transaction: options.transaction,
|
|
56
|
+
raw: true,
|
|
57
|
+
});
|
|
58
|
+
|
|
53
59
|
export const findByWhere = (where): Promise<CustomFieldDefinition | null> =>
|
|
54
|
-
CustomFieldDefinition.findOne({
|
|
60
|
+
CustomFieldDefinition.scope('userScope').findOne({
|
|
55
61
|
where,
|
|
56
|
-
include: [CustomFieldValue],
|
|
57
62
|
});
|
|
58
63
|
|
|
59
64
|
export const findDefinitionsByModels = async (
|
|
@@ -63,7 +68,6 @@ export const findDefinitionsByModels = async (
|
|
|
63
68
|
const query: WhereOptions<CreateCustomFieldDefinition> = { modelType: { [Op.in]: modelTypes } };
|
|
64
69
|
return CustomFieldDefinition.findAll({
|
|
65
70
|
where: query,
|
|
66
|
-
include: [CustomFieldValue],
|
|
67
71
|
transaction: options?.transaction,
|
|
68
72
|
});
|
|
69
73
|
};
|
|
@@ -72,7 +76,7 @@ export const update = async (
|
|
|
72
76
|
id: string,
|
|
73
77
|
data: UpdateCustomFieldDefinition,
|
|
74
78
|
): Promise<CustomFieldDefinition> => {
|
|
75
|
-
const updatedDefinition = (await CustomFieldDefinition.update(data, {
|
|
79
|
+
const updatedDefinition = (await CustomFieldDefinition.scope('userScope').update(data, {
|
|
76
80
|
where: { id },
|
|
77
81
|
returning: true,
|
|
78
82
|
individualHooks: true,
|
|
@@ -96,27 +100,10 @@ export const getRequiredFields = async (
|
|
|
96
100
|
modelType: string,
|
|
97
101
|
modelId: string | string[],
|
|
98
102
|
entityId: string | string[],
|
|
99
|
-
onlyNull = false,
|
|
100
103
|
): Promise<string[]> => {
|
|
101
104
|
const entityIds = Array.isArray(entityId) ? entityId : [entityId];
|
|
102
|
-
const modelIds = Array.isArray(modelId) ? modelId : [modelId];
|
|
103
|
-
// const valueQuery = Object.assign(
|
|
104
|
-
// {
|
|
105
|
-
// modelId: { [Op.in]: modelIds },
|
|
106
|
-
// },
|
|
107
|
-
// onlyNull ? { value: { [Op.eq]: null } } : null,
|
|
108
|
-
// );
|
|
109
|
-
const valueQuery = onlyNull ? {
|
|
110
|
-
modelId: { [Op.in]: modelIds },
|
|
111
|
-
value: { [Op.eq]: null },
|
|
112
|
-
} : {};
|
|
113
105
|
const requiredFields = await CustomFieldDefinition.findAll({
|
|
114
106
|
where: { required: true, modelType, entityId: { [Op.in]: entityIds } },
|
|
115
|
-
include: {
|
|
116
|
-
model: CustomFieldValue,
|
|
117
|
-
where: { ...valueQuery },
|
|
118
|
-
required: false,
|
|
119
|
-
},
|
|
120
107
|
logging: true,
|
|
121
108
|
});
|
|
122
109
|
const requiredFieldsNames = requiredFields.map((definition) => definition.name);
|
package/src/repository/value.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/* eslint-disable max-len */
|
|
2
|
-
import { Op } from 'sequelize';
|
|
3
|
-
|
|
4
2
|
import { CustomFieldValue, CustomFieldDefinition } from '../models';
|
|
5
3
|
import * as DefinitionRepo from './definition';
|
|
6
4
|
import { CreateCustomFieldValue, ValuesToUpdate } from '../types/value';
|
|
7
5
|
import logger from '../utils/logger';
|
|
6
|
+
import { MissingDefinitionError } from '../errors';
|
|
8
7
|
|
|
9
8
|
export const findByModelIdAndDefinition = async (modelId: string, customFieldDefinitionId: string) =>
|
|
10
9
|
CustomFieldValue.findAll({ where: { modelId, customFieldDefinitionId }, include: [CustomFieldDefinition] });
|
|
@@ -35,8 +34,9 @@ export const findValuesByModelIds = async (modelIds: string[], options?): Promis
|
|
|
35
34
|
const { transaction } = options;
|
|
36
35
|
return CustomFieldValue.findAll({
|
|
37
36
|
where: { modelId: modelIds },
|
|
38
|
-
include: [CustomFieldDefinition],
|
|
39
37
|
transaction,
|
|
38
|
+
raw: true,
|
|
39
|
+
nest: true,
|
|
40
40
|
});
|
|
41
41
|
};
|
|
42
42
|
|
|
@@ -48,6 +48,7 @@ export const findValuesByModelIds = async (modelIds: string[], options?): Promis
|
|
|
48
48
|
export const updateValues = async (
|
|
49
49
|
modelType: string,
|
|
50
50
|
modelId: string,
|
|
51
|
+
identifiers: string[],
|
|
51
52
|
valuesToUpdate: ValuesToUpdate,
|
|
52
53
|
options: any = {},
|
|
53
54
|
): Promise<CustomFieldValue[]> => {
|
|
@@ -55,14 +56,14 @@ export const updateValues = async (
|
|
|
55
56
|
|
|
56
57
|
const names = Object.keys(valuesToUpdate);
|
|
57
58
|
const fieldDefinitions = await DefinitionRepo.findAll(
|
|
58
|
-
{ modelType, name:
|
|
59
|
+
{ modelType, name: names, entityId: identifiers },
|
|
59
60
|
{ withDisabled: true, transaction: options.transaction },
|
|
60
61
|
) || [];
|
|
61
62
|
|
|
62
63
|
const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
|
|
63
64
|
if (fieldDefinitions.length !== names.length) {
|
|
64
65
|
const missingDefinitions = names.filter((name) => !fieldDefinitions.some((def) => def.name === name));
|
|
65
|
-
throw new
|
|
66
|
+
throw new MissingDefinitionError(missingDefinitions);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
const disabledNames = disabledDefinitions?.map((def) => def.name) || [];
|
|
@@ -30,10 +30,8 @@ export const booleanField = (modelType: string): CreateCustomFieldDefinition =>
|
|
|
30
30
|
export const enumField = (modelType: string, options): CreateCustomFieldDefinition => ({
|
|
31
31
|
name: 'choices',
|
|
32
32
|
modelType,
|
|
33
|
-
fieldType: '
|
|
34
|
-
validation:
|
|
35
|
-
enum: options,
|
|
36
|
-
},
|
|
33
|
+
fieldType: 'select',
|
|
34
|
+
validation: options,
|
|
37
35
|
entityId: uuidv4(),
|
|
38
36
|
entityType: 'fleetId',
|
|
39
37
|
});
|
|
@@ -14,9 +14,14 @@ export const upsertTestModel = (payload) => TestModel.upsert(payload);
|
|
|
14
14
|
|
|
15
15
|
export const destroyTestModels = () => TestModel.destroy({ truncate: true });
|
|
16
16
|
|
|
17
|
-
export const getTestModel = (
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
export const getTestModel = (
|
|
18
|
+
id: string,
|
|
19
|
+
options = {},
|
|
20
|
+
): Promise<TestModel> => TestModel.findByPk(id, options);
|
|
21
|
+
|
|
22
|
+
export const getSomeTestModels = (
|
|
23
|
+
options = { limit: 1 },
|
|
24
|
+
): Promise<TestModel[]> => TestModel.findAll(options);
|
|
20
25
|
|
|
21
26
|
export const updateTestModel = (payload, query) => TestModel.update(payload, query);
|
|
22
27
|
|
package/src/types/index.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type { Sequelize as InnerSequelize } from 'sequelize-typescript';
|
|
2
|
-
|
|
3
1
|
export type ModelFetcher = (name: string) => any;
|
|
4
2
|
|
|
5
3
|
export type ModelOptions = {
|
|
@@ -11,7 +9,7 @@ export type ModelOptions = {
|
|
|
11
9
|
}
|
|
12
10
|
|
|
13
11
|
export type CustomFieldOptions= {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
models: ModelOptions[];
|
|
13
|
+
databaseConfig: any;
|
|
14
|
+
getUser: () => any;
|
|
15
|
+
};
|
package/src/utils/db/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Sequelize } from 'sequelize-typescript';
|
|
2
|
+
import { QueryTypes } from 'sequelize';
|
|
2
3
|
|
|
3
4
|
export default (databaseConfig: any): Sequelize => {
|
|
4
5
|
const ENV_DEV = 'test';
|
|
@@ -12,3 +13,9 @@ export default (databaseConfig: any): Sequelize => {
|
|
|
12
13
|
}
|
|
13
14
|
return sequelize;
|
|
14
15
|
};
|
|
16
|
+
|
|
17
|
+
export const createSequelizeMeta = (sequelize: Sequelize) => sequelize.query(`
|
|
18
|
+
CREATE TABLE IF NOT EXISTS "SequelizeMeta" (
|
|
19
|
+
name character varying(255) PRIMARY KEY
|
|
20
|
+
);
|
|
21
|
+
`, { type: QueryTypes.SELECT });
|