@autofleet/sadot 1.0.3 → 1.0.6
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/index.js +1 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/v1/definition/index.js +1 -0
- package/dist/api/v1/definition/index.js.map +1 -0
- package/dist/api/v1/definition/validations.js +1 -0
- package/dist/api/v1/definition/validations.js.map +1 -0
- package/dist/api/v1/errors.js +1 -0
- package/dist/api/v1/errors.js.map +1 -0
- package/dist/api/v1/index.js +1 -0
- package/dist/api/v1/index.js.map +1 -0
- package/dist/api/v1/validator/index.js +1 -0
- package/dist/api/v1/validator/index.js.map +1 -0
- package/dist/api/v1/validator/validations.js +1 -0
- package/dist/api/v1/validator/validations.js.map +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/events/index.js +1 -0
- package/dist/events/index.js.map +1 -0
- package/dist/hooks/create.js +2 -1
- package/dist/hooks/create.js.map +1 -0
- package/dist/hooks/enrich.js +4 -4
- package/dist/hooks/enrich.js.map +1 -0
- package/dist/hooks/find.js +1 -0
- package/dist/hooks/find.js.map +1 -0
- package/dist/hooks/hooks.js +3 -3
- package/dist/hooks/hooks.js.map +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/update.js +1 -0
- package/dist/hooks/update.js.map +1 -0
- package/dist/hooks/utils/updateInstanceValues.js +1 -0
- package/dist/hooks/utils/updateInstanceValues.js.map +1 -0
- package/dist/hooks/workaround.js +1 -0
- package/dist/hooks/workaround.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/models/CustomFieldDefinition.js +13 -3
- package/dist/models/CustomFieldDefinition.js.map +1 -0
- package/dist/models/CustomFieldEntries.js +9 -2
- package/dist/models/CustomFieldEntries.js.map +1 -0
- package/dist/models/CustomFieldValue.js +13 -3
- package/dist/models/CustomFieldValue.js.map +1 -0
- package/dist/models/CustomValidator.js +9 -2
- package/dist/models/CustomValidator.js.map +1 -0
- package/dist/models/index.js +1 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/tests/AssociatedTestModel.js +4 -1
- package/dist/models/tests/AssociatedTestModel.js.map +1 -0
- package/dist/models/tests/TestModel.js +4 -1
- package/dist/models/tests/TestModel.js.map +1 -0
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.js +1 -0
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.js.map +1 -0
- package/dist/models/tests/contextAwareModels/ContextTestModel.js +1 -0
- package/dist/models/tests/contextAwareModels/ContextTestModel.js.map +1 -0
- package/dist/repository/definition.d.ts +2 -1
- package/dist/repository/definition.js +5 -2
- package/dist/repository/definition.js.map +1 -0
- package/dist/repository/entries.js +1 -0
- package/dist/repository/entries.js.map +1 -0
- package/dist/repository/utils/formatValues.js +1 -0
- package/dist/repository/utils/formatValues.js.map +1 -0
- package/dist/repository/validator.js +1 -0
- package/dist/repository/validator.js.map +1 -0
- package/dist/repository/value.js +1 -0
- package/dist/repository/value.js.map +1 -0
- package/dist/scopes/filter.d.ts +1 -1
- package/dist/scopes/filter.js +1 -0
- package/dist/scopes/filter.js.map +1 -0
- package/dist/scopes/helpers/filter.helpers.js +1 -0
- package/dist/scopes/helpers/filter.helpers.js.map +1 -0
- package/dist/scopes/index.js +1 -0
- package/dist/scopes/index.js.map +1 -0
- package/dist/types/definition/index.js +1 -0
- package/dist/types/definition/index.js.map +1 -0
- package/dist/types/entries/index.js +1 -0
- package/dist/types/entries/index.js.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/value/index.js +1 -0
- package/dist/types/value/index.js.map +1 -0
- package/dist/utils/constants/index.d.ts +1 -1
- package/dist/utils/constants/index.js +1 -0
- package/dist/utils/constants/index.js.map +1 -0
- package/dist/utils/db/index.js +1 -0
- package/dist/utils/db/index.js.map +1 -0
- package/dist/utils/helpers/index.js +1 -0
- package/dist/utils/helpers/index.js.map +1 -0
- package/dist/utils/init.js +1 -0
- package/dist/utils/init.js.map +1 -0
- package/dist/utils/logger/index.js +1 -0
- package/dist/utils/logger/index.js.map +1 -0
- package/dist/utils/scopeAttributes.js +1 -0
- package/dist/utils/scopeAttributes.js.map +1 -0
- package/dist/utils/validations/index.d.ts +1 -1
- package/dist/utils/validations/index.js +3 -2
- package/dist/utils/validations/index.js.map +1 -0
- package/dist/utils/validations/schema/custom-fields.js +1 -0
- package/dist/utils/validations/schema/custom-fields.js.map +1 -0
- package/dist/utils/validations/schema/validator-schema.js +1 -0
- package/dist/utils/validations/schema/validator-schema.js.map +1 -0
- package/dist/utils/validations/type.js +1 -0
- package/dist/utils/validations/type.js.map +1 -0
- package/dist/utils/validations/validators/index.js +1 -0
- package/dist/utils/validations/validators/index.js.map +1 -0
- package/dist/utils/validations/validators/select.validator.js +1 -0
- package/dist/utils/validations/validators/select.validator.js.map +1 -0
- package/dist/utils/validations/validators/status.validator.js +1 -0
- package/dist/utils/validations/validators/status.validator.js.map +1 -0
- package/package.json +22 -23
- package/.nvmrc +0 -1
- package/dist/tests/api/test-api.d.ts +0 -2
- package/dist/tests/api/test-api.js +0 -38
- package/dist/tests/functional/searching/index.d.ts +0 -8
- package/dist/tests/functional/searching/index.js +0 -44
- package/dist/tests/helpers/commonHooks.d.ts +0 -6
- package/dist/tests/helpers/commonHooks.js +0 -62
- package/dist/tests/helpers/database-config.d.ts +0 -16
- package/dist/tests/helpers/database-config.js +0 -17
- package/dist/tests/helpers/index.d.ts +0 -7
- package/dist/tests/helpers/index.js +0 -33
- package/dist/tests/mocks/definition.mock.d.ts +0 -48
- package/dist/tests/mocks/definition.mock.js +0 -78
- package/dist/tests/mocks/events.mock.d.ts +0 -4
- package/dist/tests/mocks/events.mock.js +0 -21
- package/dist/tests/mocks/testModel.d.ts +0 -12
- package/dist/tests/mocks/testModel.js +0 -35
- package/src/api/index.ts +0 -10
- package/src/api/v1/definition/index.ts +0 -104
- package/src/api/v1/definition/validations.ts +0 -75
- package/src/api/v1/errors.ts +0 -13
- package/src/api/v1/index.ts +0 -11
- package/src/api/v1/validator/index.ts +0 -141
- package/src/api/v1/validator/validations.ts +0 -38
- package/src/errors/index.ts +0 -70
- package/src/events/index.ts +0 -63
- package/src/hooks/create.ts +0 -81
- package/src/hooks/enrich.ts +0 -255
- package/src/hooks/find.ts +0 -27
- package/src/hooks/hooks.ts +0 -479
- package/src/hooks/index.ts +0 -20
- package/src/hooks/update.ts +0 -55
- package/src/hooks/utils/updateInstanceValues.ts +0 -63
- package/src/hooks/workaround.ts +0 -47
- package/src/index.ts +0 -52
- package/src/models/CustomFieldDefinition.ts +0 -162
- package/src/models/CustomFieldEntries.ts +0 -81
- package/src/models/CustomFieldValue.ts +0 -118
- package/src/models/CustomValidator.ts +0 -78
- package/src/models/index.ts +0 -165
- package/src/models/tests/AssociatedTestModel.ts +0 -57
- package/src/models/tests/TestModel.ts +0 -54
- package/src/models/tests/contextAwareModels/ContextAwareTestModel.ts +0 -43
- package/src/models/tests/contextAwareModels/ContextTestModel.ts +0 -38
- package/src/repository/definition.ts +0 -175
- package/src/repository/entries.ts +0 -88
- package/src/repository/utils/formatValues.ts +0 -14
- package/src/repository/validator.ts +0 -119
- package/src/repository/value.ts +0 -116
- package/src/scopes/filter.ts +0 -100
- package/src/scopes/helpers/filter.helpers.ts +0 -227
- package/src/scopes/index.ts +0 -6
- package/src/tests/api/test-api.ts +0 -40
- package/src/tests/functional/searching/index.ts +0 -39
- package/src/tests/helpers/commonHooks.ts +0 -43
- package/src/tests/helpers/database-config.ts +0 -15
- package/src/tests/helpers/index.ts +0 -35
- package/src/tests/mocks/definition.mock.ts +0 -84
- package/src/tests/mocks/events.mock.ts +0 -21
- package/src/tests/mocks/testModel.ts +0 -37
- package/src/types/definition/index.ts +0 -24
- package/src/types/entries/index.ts +0 -27
- package/src/types/index.ts +0 -52
- package/src/types/value/index.ts +0 -14
- package/src/utils/constants/index.ts +0 -25
- package/src/utils/db/index.ts +0 -21
- package/src/utils/helpers/index.ts +0 -66
- package/src/utils/init.ts +0 -122
- package/src/utils/logger/index.ts +0 -14
- package/src/utils/scopeAttributes.ts +0 -12
- package/src/utils/validations/index.ts +0 -46
- package/src/utils/validations/schema/README.md +0 -93
- package/src/utils/validations/schema/custom-fields.ts +0 -8
- package/src/utils/validations/schema/validator-schema.ts +0 -106
- package/src/utils/validations/type.ts +0 -20
- package/src/utils/validations/validators/index.ts +0 -38
- package/src/utils/validations/validators/select.validator.ts +0 -12
- package/src/utils/validations/validators/status.validator.ts +0 -22
- package/tsconfig.build.json +0 -7
- package/tsconfig.json +0 -16
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { ResourceNotFoundError } from '@autofleet/errors';
|
|
2
|
-
import { Router } from '@autofleet/node-common';
|
|
3
|
-
import { StatusCodes } from 'http-status-codes';
|
|
4
|
-
import handleError from '../errors';
|
|
5
|
-
import * as ValidatorRepo from '../../../repository/validator';
|
|
6
|
-
import validations from './validations';
|
|
7
|
-
import logger from '../../../utils/logger';
|
|
8
|
-
import { validateValidatorSchema } from '../../../utils/validations/schema/validator-schema';
|
|
9
|
-
import type { CustomValidator } from '../../../models';
|
|
10
|
-
|
|
11
|
-
const router = Router({ logger });
|
|
12
|
-
const ENTITY = 'CustomValidator';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Create
|
|
16
|
-
*/
|
|
17
|
-
router.post<{ modelName: string }>('/', async (req, res) => {
|
|
18
|
-
const { modelName } = req.params;
|
|
19
|
-
try {
|
|
20
|
-
// Validate the request body
|
|
21
|
-
const validatedPayload = await validations.create.validateAsync(req.body);
|
|
22
|
-
|
|
23
|
-
// Validate that the schema is a valid AJV schema
|
|
24
|
-
validateValidatorSchema(validatedPayload.schema);
|
|
25
|
-
|
|
26
|
-
const validator = await ValidatorRepo.create({
|
|
27
|
-
...validatedPayload,
|
|
28
|
-
modelType: modelName,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
return res.status(StatusCodes.CREATED).json(validator);
|
|
32
|
-
} catch (err) {
|
|
33
|
-
return handleError(err, res, { logger, message: `Error in create ${ENTITY} request` });
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Get all
|
|
39
|
-
*/
|
|
40
|
-
router.get<
|
|
41
|
-
{ modelName: string },
|
|
42
|
-
{ validators: CustomValidator[] },
|
|
43
|
-
never,
|
|
44
|
-
{ entityId?: string; entityType?: string }
|
|
45
|
-
>('/', async (req, res) => {
|
|
46
|
-
try {
|
|
47
|
-
const { modelName } = req.params;
|
|
48
|
-
const { entityId, entityType } = req.query;
|
|
49
|
-
|
|
50
|
-
const where = {
|
|
51
|
-
modelType: modelName,
|
|
52
|
-
...(entityId && { entityId }),
|
|
53
|
-
...(entityType && { entityType }),
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const validators = await ValidatorRepo.findAll(where);
|
|
57
|
-
|
|
58
|
-
return res.status(StatusCodes.OK).json({ validators });
|
|
59
|
-
} catch (err) {
|
|
60
|
-
return handleError(err, res, { logger, message: `Error in get all ${ENTITY} request` });
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Get by id
|
|
66
|
-
*/
|
|
67
|
-
router.get<{ modelName: string; validatorId: string }, CustomValidator>('/:validatorId', async (req, res) => {
|
|
68
|
-
try {
|
|
69
|
-
const { validatorId, modelName } = req.params;
|
|
70
|
-
// Include disabled validators when fetching by ID
|
|
71
|
-
const validators = await ValidatorRepo.findAll({ id: validatorId, modelType: modelName }, { withDisabled: true });
|
|
72
|
-
|
|
73
|
-
if (!validators.length) {
|
|
74
|
-
throw new ResourceNotFoundError('Validator not found');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return res.status(StatusCodes.OK).json(validators[0]);
|
|
78
|
-
} catch (err) {
|
|
79
|
-
return handleError(err, res, { logger, message: `Error in get ${ENTITY} request` });
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Update
|
|
85
|
-
*/
|
|
86
|
-
router.patch<{ modelName: string; validatorId: string }, CustomValidator>('/:validatorId', async (req, res) => {
|
|
87
|
-
try {
|
|
88
|
-
const { validatorId } = req.params;
|
|
89
|
-
|
|
90
|
-
// Validate the request body
|
|
91
|
-
const validatedPayload = await validations.update.validateAsync(req.body);
|
|
92
|
-
|
|
93
|
-
// If schema is included in the update, validate that it's a valid AJV schema
|
|
94
|
-
if (validatedPayload.schema) {
|
|
95
|
-
validateValidatorSchema(validatedPayload.schema);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// First verify the validator exists, including disabled ones
|
|
99
|
-
const existingValidators = await ValidatorRepo.findAll({ id: validatorId }, { withDisabled: true });
|
|
100
|
-
if (!existingValidators.length) {
|
|
101
|
-
throw new ResourceNotFoundError('Validator not found');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const [count, validators] = await ValidatorRepo.update(validatorId, validatedPayload);
|
|
105
|
-
|
|
106
|
-
if (!count) {
|
|
107
|
-
throw new ResourceNotFoundError('Validator not found');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return res.status(StatusCodes.OK).json(validators[0]);
|
|
111
|
-
} catch (err) {
|
|
112
|
-
return handleError(err, res, { logger, message: `Error in update ${ENTITY} request` });
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Delete (disable)
|
|
118
|
-
*/
|
|
119
|
-
router.delete<{ modelName: string; validatorId: string }>('/:validatorId', async (req, res) => {
|
|
120
|
-
try {
|
|
121
|
-
const { validatorId } = req.params;
|
|
122
|
-
|
|
123
|
-
// First verify the validator exists, including disabled ones
|
|
124
|
-
const existingValidators = await ValidatorRepo.findAll({ id: validatorId }, { withDisabled: true });
|
|
125
|
-
if (!existingValidators.length) {
|
|
126
|
-
throw new ResourceNotFoundError('Validator not found');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const [count] = await ValidatorRepo.disable(validatorId);
|
|
130
|
-
|
|
131
|
-
if (!count) {
|
|
132
|
-
throw new ResourceNotFoundError('Validator failed to be disabled');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return res.status(StatusCodes.NO_CONTENT).send();
|
|
136
|
-
} catch (err) {
|
|
137
|
-
return handleError(err, res, { logger, message: `Error in delete ${ENTITY} request` });
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
export default router;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import Joi from 'joi';
|
|
2
|
-
|
|
3
|
-
const jsonSchemaValidation = Joi.object().unknown(true);
|
|
4
|
-
|
|
5
|
-
const schemaObject = Joi.object({
|
|
6
|
-
type: Joi.string().valid('object'),
|
|
7
|
-
properties: Joi.object({
|
|
8
|
-
before: jsonSchemaValidation,
|
|
9
|
-
after: jsonSchemaValidation,
|
|
10
|
-
}).required(),
|
|
11
|
-
required: Joi.array().items(Joi.string()),
|
|
12
|
-
allOf: Joi.array().items(Joi.object()),
|
|
13
|
-
anyOf: Joi.array().items(Joi.object()),
|
|
14
|
-
oneOf: Joi.array().items(Joi.object()),
|
|
15
|
-
additionalProperties: Joi.alternatives().try(Joi.boolean(), Joi.object()),
|
|
16
|
-
$id: Joi.string(),
|
|
17
|
-
$schema: Joi.string(),
|
|
18
|
-
if: Joi.object(),
|
|
19
|
-
then: Joi.object(),
|
|
20
|
-
else: Joi.object(),
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const validationSchemas = {
|
|
24
|
-
create: Joi.object({
|
|
25
|
-
entityId: Joi.string().uuid().required(),
|
|
26
|
-
entityType: Joi.string().required(),
|
|
27
|
-
schema: schemaObject.required(),
|
|
28
|
-
}),
|
|
29
|
-
|
|
30
|
-
update: Joi.object({
|
|
31
|
-
entityId: Joi.string().uuid(),
|
|
32
|
-
entityType: Joi.string(),
|
|
33
|
-
schema: schemaObject,
|
|
34
|
-
disabled: Joi.boolean(),
|
|
35
|
-
}).min(1),
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export default validationSchemas;
|
package/src/errors/index.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/* eslint-disable max-classes-per-file */
|
|
2
|
-
import { BadRequest } from '@autofleet/errors';
|
|
3
|
-
import type { ValidationError } from 'joi';
|
|
4
|
-
import type { EntriesValidationError } from '../types/entries';
|
|
5
|
-
|
|
6
|
-
export class MissingRequiredCustomFieldError extends BadRequest {
|
|
7
|
-
constructor(missingFields: string[]) {
|
|
8
|
-
const err = new Error(`The following custom fields are required: ${missingFields.join(',')}`);
|
|
9
|
-
super([err], null, missingFields);
|
|
10
|
-
this.message = 'MISSING_REQUIRED_CUSTOM_FIELDS';
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class UnsupportedCustomFieldTypeError extends BadRequest {
|
|
15
|
-
constructor(fieldType: string) {
|
|
16
|
-
const err = new Error(`Type "${fieldType}" is not supported`);
|
|
17
|
-
super([err], null, null);
|
|
18
|
-
this.message = 'UNSUPPORTED_CUSTOM_FIELD_TYPE';
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export class UnsupportedCustomValidationError extends BadRequest {
|
|
23
|
-
constructor(fieldType: string) {
|
|
24
|
-
const err = new Error(`Validation for "${fieldType}" is not supported`);
|
|
25
|
-
super([err], null, null);
|
|
26
|
-
this.message = 'UNSUPPORTED_CUSTOM_VALIDATION_TYPE';
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export class InvalidFieldTypeError extends BadRequest {
|
|
31
|
-
constructor(fieldType: string) {
|
|
32
|
-
const err = new Error(`Invalid field type ${fieldType}`);
|
|
33
|
-
super([err], null, null);
|
|
34
|
-
this.message = 'INVALID_FIELD_TYPE';
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export class InvalidValueError extends BadRequest {
|
|
39
|
-
constructor(value: any, fieldDefinitionName: string, joiValidationError: ValidationError) {
|
|
40
|
-
const formattedErrorMessage = joiValidationError.message
|
|
41
|
-
.replace(/"/g, '')
|
|
42
|
-
.replace('value', `'${fieldDefinitionName}'`);
|
|
43
|
-
|
|
44
|
-
const formattedValue = typeof value === 'object' ? JSON.stringify(value) : value;
|
|
45
|
-
|
|
46
|
-
const invalidValueMessage = `Invalid Value on field '${fieldDefinitionName}'. ${formattedErrorMessage}. received: '${formattedValue}'`;
|
|
47
|
-
|
|
48
|
-
const err = new Error(invalidValueMessage);
|
|
49
|
-
super([err], null, null);
|
|
50
|
-
this.message = invalidValueMessage;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export class InvalidEntriesError extends BadRequest {
|
|
55
|
-
constructor(modelId: string, validationErrors: EntriesValidationError[]) {
|
|
56
|
-
const errors = validationErrors.map((validationError) => new InvalidValueError(validationError.value, validationError.fieldDefinitionName, validationError.joiValidationError));
|
|
57
|
-
super(errors, null, null);
|
|
58
|
-
this.message = `Invalid entries on ${modelId}\n${validationErrors.map((validationError) => (
|
|
59
|
-
`${validationError.fieldDefinitionName} - ${validationError.joiValidationError.message}`
|
|
60
|
-
)).join('\n')}`;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export class MissingDefinitionError extends BadRequest {
|
|
65
|
-
constructor(fieldNames: string[]) {
|
|
66
|
-
const err = new Error(`Missing custom field definition for field ${fieldNames.join(',')}`);
|
|
67
|
-
super([err], null, null);
|
|
68
|
-
this.message = 'MISSING_DEFINITION';
|
|
69
|
-
}
|
|
70
|
-
}
|
package/src/events/index.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import Events from '@autofleet/events';
|
|
2
|
-
import { getUser } from '@autofleet/zehut';
|
|
3
|
-
import logger from '../utils/logger';
|
|
4
|
-
import type {
|
|
5
|
-
CustomFieldDefinition,
|
|
6
|
-
CustomFieldEntries,
|
|
7
|
-
CustomFieldValue,
|
|
8
|
-
CustomValidator,
|
|
9
|
-
} from '../models';
|
|
10
|
-
|
|
11
|
-
const events = new Events({
|
|
12
|
-
logger,
|
|
13
|
-
getUserId: (payload) => payload?.user_id ?? getUser()?.id ?? null,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const KEYS_TO_CONVERT = ['value', 'defaultValue', 'blockEditingFromUI'];
|
|
17
|
-
|
|
18
|
-
const stringifyBooleans = (savedObject: any, keysToConvert: string[]) => {
|
|
19
|
-
const savedObjectKeySet = new Set(Object.keys(savedObject));
|
|
20
|
-
if (keysToConvert.every((key) => !savedObjectKeySet.has(key))) {
|
|
21
|
-
return savedObject;
|
|
22
|
-
}
|
|
23
|
-
return {
|
|
24
|
-
...savedObject,
|
|
25
|
-
...Object.fromEntries(keysToConvert.map((key) => [key, typeof savedObject[key] === 'boolean' ? savedObject[key].toString() : savedObject[key]])),
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const modelTableMapping = {
|
|
30
|
-
CustomFieldDefinition: {
|
|
31
|
-
tableName: 'dim_custom_field_definition',
|
|
32
|
-
eventVersion: '1',
|
|
33
|
-
},
|
|
34
|
-
CustomFieldValue: {
|
|
35
|
-
tableName: 'dim_custom_field_value',
|
|
36
|
-
eventVersion: '1',
|
|
37
|
-
},
|
|
38
|
-
CustomFieldEntries: {
|
|
39
|
-
tableName: 'dim_custom_field_entries',
|
|
40
|
-
eventVersion: '1',
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export const sendDimEvent = (instance: CustomFieldDefinition | CustomFieldValue | CustomFieldEntries | CustomValidator): void => {
|
|
45
|
-
const mapping = modelTableMapping[instance.constructor.name];
|
|
46
|
-
if (!mapping) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
let objectToSend = instance.get();
|
|
50
|
-
try {
|
|
51
|
-
objectToSend = stringifyBooleans(instance.get(), KEYS_TO_CONVERT);
|
|
52
|
-
} catch (err) {
|
|
53
|
-
logger.error('Failed to convert booleans in dim event payload', err);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
events.sendObject(
|
|
57
|
-
mapping.tableName,
|
|
58
|
-
mapping.eventVersion,
|
|
59
|
-
objectToSend,
|
|
60
|
-
).catch(() => {});
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export default events;
|
package/src/hooks/create.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import type { WhereOptions } from 'sequelize';
|
|
2
|
-
import logger from '../utils/logger';
|
|
3
|
-
import * as DefinitionRepo from '../repository/definition';
|
|
4
|
-
import { MissingRequiredCustomFieldError } from '../errors';
|
|
5
|
-
import type { CustomFieldOptions, ModelOptions } from '../types';
|
|
6
|
-
import applyScopeToInstance from '../utils/scopeAttributes';
|
|
7
|
-
import updateInstanceValues from './utils/updateInstanceValues';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* A hook to create the custom fields when updating a model (more then one instance).
|
|
11
|
-
*/
|
|
12
|
-
export const beforeBulkCreate = (options): void => {
|
|
13
|
-
// This will activate the beforeCreate hook on each updating instance.
|
|
14
|
-
// eslint-disable-next-line no-param-reassign
|
|
15
|
-
options.individualHooks = true;
|
|
16
|
-
};
|
|
17
|
-
/**
|
|
18
|
-
* A hook to create the custom fields when updating a model instance.
|
|
19
|
-
* TODO - cleanup if update fail
|
|
20
|
-
*/
|
|
21
|
-
export const beforeCreate = (
|
|
22
|
-
scopeAttributes: string[],
|
|
23
|
-
modelOptions: ModelOptions = {},
|
|
24
|
-
sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false },
|
|
25
|
-
) => async (
|
|
26
|
-
instance,
|
|
27
|
-
options,
|
|
28
|
-
): Promise<void> => {
|
|
29
|
-
logger.debug('sadot - before create hook');
|
|
30
|
-
const { fields } = options;
|
|
31
|
-
const { include, useEntityIdFromInclude } = modelOptions;
|
|
32
|
-
const modelType = instance.constructor.name;
|
|
33
|
-
|
|
34
|
-
const identifiers = applyScopeToInstance(instance, scopeAttributes);
|
|
35
|
-
|
|
36
|
-
const where: WhereOptions = {
|
|
37
|
-
modelType,
|
|
38
|
-
disabled: false,
|
|
39
|
-
...(!useEntityIdFromInclude && { entityId: identifiers }),
|
|
40
|
-
};
|
|
41
|
-
const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: false, transaction: options.transaction, include: include?.(identifiers) });
|
|
42
|
-
const requiredFieldsNames = Array.from(new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)));
|
|
43
|
-
|
|
44
|
-
const fieldsWithDefaultValue = fieldDefinitions.filter((def) => ![null, undefined].includes(def.defaultValue));
|
|
45
|
-
if (fieldsWithDefaultValue.length) {
|
|
46
|
-
// eslint-disable-next-line no-param-reassign
|
|
47
|
-
instance.customFields ||= {};
|
|
48
|
-
fieldsWithDefaultValue.filter((def) => (instance.customFields?.[def.name] === undefined)).forEach(({ name, defaultValue }) => {
|
|
49
|
-
// eslint-disable-next-line no-param-reassign
|
|
50
|
-
instance.customFields[name] = defaultValue;
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const { customFields } = instance;
|
|
55
|
-
const fieldsNames = Object.keys(customFields ?? {});
|
|
56
|
-
const missingFields = requiredFieldsNames.filter((name) => !fieldsNames.includes(name));
|
|
57
|
-
if (missingFields?.length) {
|
|
58
|
-
throw new MissingRequiredCustomFieldError(missingFields);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const customFieldsIdx = fields.indexOf('customFields');
|
|
62
|
-
if (customFieldsIdx === -1 || !customFields || !Object.keys(customFields).length) {
|
|
63
|
-
// After checking for required fields and fields with default values, and we have no custom fields.
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
await updateInstanceValues({
|
|
68
|
-
modelId: instance.id,
|
|
69
|
-
modelType,
|
|
70
|
-
identifiers,
|
|
71
|
-
customFields,
|
|
72
|
-
options: {
|
|
73
|
-
useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,
|
|
74
|
-
transaction: options.transaction,
|
|
75
|
-
modelOptions,
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// eslint-disable-next-line no-param-reassign
|
|
80
|
-
fields.splice(customFieldsIdx, 1);
|
|
81
|
-
};
|
package/src/hooks/enrich.ts
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-param-reassign */
|
|
2
|
-
import * as ValueRepo from '../repository/value';
|
|
3
|
-
import * as DefinitionRepo from '../repository/definition';
|
|
4
|
-
import * as EntriesRepo from '../repository/entries';
|
|
5
|
-
import type CustomFieldValue from '../models/CustomFieldValue';
|
|
6
|
-
import type CustomFieldDefinition from '../models/CustomFieldDefinition';
|
|
7
|
-
import type { SerializedCustomFields } from '../types/definition';
|
|
8
|
-
import type { CustomFieldOptions, ModelOptions, TransactionOptions } from '../types';
|
|
9
|
-
import applyScopeToInstance from '../utils/scopeAttributes';
|
|
10
|
-
|
|
11
|
-
// Include all required fields for proper functioning
|
|
12
|
-
const CUSTOM_FIELD_DEFINITION_ATTRIBUTES_TO_PULL = [
|
|
13
|
-
'id',
|
|
14
|
-
'name',
|
|
15
|
-
'entityId',
|
|
16
|
-
'fieldType',
|
|
17
|
-
'displayName',
|
|
18
|
-
'validation',
|
|
19
|
-
'entityType',
|
|
20
|
-
'modelType',
|
|
21
|
-
'required',
|
|
22
|
-
'disabled',
|
|
23
|
-
'defaultValue',
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
type SupportedHookTypes = 'afterFind' | 'afterCreate' | 'afterUpdate';
|
|
27
|
-
|
|
28
|
-
type CustomFieldEntries = Record<string, any>;
|
|
29
|
-
|
|
30
|
-
interface GetValuesGroupByInstanceResponse {
|
|
31
|
-
[modelId: string]: CustomFieldValue[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface GetCustomFieldEntriesByInstanceIdResponse {
|
|
35
|
-
[modelId: string]: CustomFieldEntries;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export const getCustomFieldEntriesByInstanceId = async ({
|
|
39
|
-
instancesIds,
|
|
40
|
-
options,
|
|
41
|
-
sadotOptions,
|
|
42
|
-
}: {
|
|
43
|
-
instancesIds: string[],
|
|
44
|
-
options?: TransactionOptions,
|
|
45
|
-
sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries'>,
|
|
46
|
-
}): Promise<GetCustomFieldEntriesByInstanceIdResponse> => {
|
|
47
|
-
if (!sadotOptions.useCustomFieldsEntries) {
|
|
48
|
-
return {};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const customFieldEntries = await EntriesRepo.findEntriesByModelIds(
|
|
52
|
-
instancesIds,
|
|
53
|
-
options ?? {},
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
const customFieldEntriesByInstanceId = Object.fromEntries(customFieldEntries.map((instanceEntries) => {
|
|
57
|
-
const { modelId, customFields } = instanceEntries?.dataValues ?? {};
|
|
58
|
-
if (!modelId) {
|
|
59
|
-
return undefined;
|
|
60
|
-
}
|
|
61
|
-
return [modelId, customFields];
|
|
62
|
-
}).filter(Boolean));
|
|
63
|
-
|
|
64
|
-
instancesIds.forEach((instanceId) => {
|
|
65
|
-
customFieldEntriesByInstanceId[instanceId] ??= {};
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
return customFieldEntriesByInstanceId;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export const getValuesGroupByInstance = async ({
|
|
72
|
-
instancesIds,
|
|
73
|
-
options,
|
|
74
|
-
sadotOptions,
|
|
75
|
-
}: {
|
|
76
|
-
instancesIds: string[],
|
|
77
|
-
options?: TransactionOptions,
|
|
78
|
-
sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries'>,
|
|
79
|
-
}): Promise<GetValuesGroupByInstanceResponse> => {
|
|
80
|
-
if (sadotOptions.useCustomFieldsEntries) {
|
|
81
|
-
return {};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const customFieldValues = await ValueRepo.findValuesByModelIds(
|
|
85
|
-
instancesIds,
|
|
86
|
-
options ?? {},
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
// Group fields by modelId
|
|
90
|
-
return customFieldValues.reduce((acc, v) => {
|
|
91
|
-
const { modelId } = v;
|
|
92
|
-
acc[modelId] ??= [];
|
|
93
|
-
acc[modelId].push(v);
|
|
94
|
-
return acc;
|
|
95
|
-
}, {});
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Serialize custom fields value into the format of {[name] -> [fieldData]}
|
|
100
|
-
*/
|
|
101
|
-
const serializeCustomFields = (
|
|
102
|
-
customFieldValues: CustomFieldValue[],
|
|
103
|
-
customFieldDefinitionsHash: Record<string, CustomFieldDefinition>,
|
|
104
|
-
): SerializedCustomFields => {
|
|
105
|
-
const customFields = customFieldValues.reduce((acc, cfv) => ({
|
|
106
|
-
...acc,
|
|
107
|
-
...(
|
|
108
|
-
customFieldDefinitionsHash[cfv.customFieldDefinitionId]
|
|
109
|
-
&& { [customFieldDefinitionsHash[cfv.customFieldDefinitionId].name]: cfv.value }
|
|
110
|
-
),
|
|
111
|
-
}), {});
|
|
112
|
-
return customFields;
|
|
113
|
-
};
|
|
114
|
-
/**
|
|
115
|
-
* A hook to attach the custom fields when fetching a model instances.
|
|
116
|
-
*/
|
|
117
|
-
const enrichResults = (
|
|
118
|
-
modelType: string,
|
|
119
|
-
scopeAttributes: string[],
|
|
120
|
-
hookType?: SupportedHookTypes,
|
|
121
|
-
modelOptions: ModelOptions = {},
|
|
122
|
-
sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false },
|
|
123
|
-
) => async (
|
|
124
|
-
instancesOrInstance: any | any[],
|
|
125
|
-
options: TransactionOptions,
|
|
126
|
-
): Promise<void> => {
|
|
127
|
-
if (
|
|
128
|
-
options.originalAttributes?.length > 0
|
|
129
|
-
&& !options.originalAttributes?.includes?.('customFields')
|
|
130
|
-
) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const primaryKey = 'id';
|
|
135
|
-
let instances = Array.isArray(instancesOrInstance)
|
|
136
|
-
? instancesOrInstance
|
|
137
|
-
: [instancesOrInstance];
|
|
138
|
-
|
|
139
|
-
instances = instances.filter(Boolean);
|
|
140
|
-
|
|
141
|
-
const identifiers = applyScopeToInstance(instances, scopeAttributes);
|
|
142
|
-
|
|
143
|
-
const uniqueIdentifiers = [...new Set(identifiers)].filter(Boolean);
|
|
144
|
-
|
|
145
|
-
const identifierCustomFieldDefinitionsMapping = uniqueIdentifiers.reduce((map, identifier) => ({
|
|
146
|
-
...map,
|
|
147
|
-
[identifier]: [],
|
|
148
|
-
}), {});
|
|
149
|
-
|
|
150
|
-
// Cache for definitions by model type and transaction to avoid redundant DB queries
|
|
151
|
-
let customFieldDefinitionsPromise;
|
|
152
|
-
let cacheKey;
|
|
153
|
-
|
|
154
|
-
if (options.transaction) {
|
|
155
|
-
// Initialize definition cache Map if not already present directly on the transaction object
|
|
156
|
-
options.transaction.definitionCache ||= new Map();
|
|
157
|
-
cacheKey = `${modelType}:${uniqueIdentifiers.slice().sort().join(',')}`;
|
|
158
|
-
customFieldDefinitionsPromise = options.transaction.definitionCache.get(cacheKey);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (!customFieldDefinitionsPromise) {
|
|
162
|
-
// Fetch from database (either first time in this transaction or no transaction)
|
|
163
|
-
customFieldDefinitionsPromise = DefinitionRepo.findByEntityIds(
|
|
164
|
-
modelType,
|
|
165
|
-
uniqueIdentifiers,
|
|
166
|
-
{ transaction: options.transaction, modelOptions, attributes: CUSTOM_FIELD_DEFINITION_ATTRIBUTES_TO_PULL },
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
options.transaction?.definitionCache?.set(cacheKey, customFieldDefinitionsPromise);
|
|
170
|
-
}
|
|
171
|
-
const customFieldDefinitions = await customFieldDefinitionsPromise;
|
|
172
|
-
|
|
173
|
-
if (customFieldDefinitions.length === 0) {
|
|
174
|
-
// if no custom fields, we can return
|
|
175
|
-
instances.forEach((instance) => {
|
|
176
|
-
instance.customFields = {};
|
|
177
|
-
});
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (modelOptions?.include && modelOptions.useEntityIdFromInclude) {
|
|
182
|
-
// if we pass useEntityIdFromInclude,
|
|
183
|
-
// map the entity from the options to the identifierCustomFieldDefinitionsMapping
|
|
184
|
-
modelOptions.include(identifiers).forEach(({ model }) => {
|
|
185
|
-
customFieldDefinitions.forEach((cfd) => {
|
|
186
|
-
const entityId = cfd[`${model.name}.entityId`];
|
|
187
|
-
identifierCustomFieldDefinitionsMapping[entityId] = [];
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const definitionsMap = customFieldDefinitions.reduce((map, definition) => ({
|
|
193
|
-
...map,
|
|
194
|
-
[definition.id]: definition,
|
|
195
|
-
}), {});
|
|
196
|
-
|
|
197
|
-
customFieldDefinitions.forEach((cfd) => {
|
|
198
|
-
identifierCustomFieldDefinitionsMapping[cfd.entityId].push(cfd);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// Get the values per instates ids:
|
|
202
|
-
const instancesIds = instances.map((i) => i[primaryKey]);
|
|
203
|
-
|
|
204
|
-
// Group fields by modelId
|
|
205
|
-
const [valuesGroupByInstance, customFieldEntriesByInstanceId] = await Promise.all([
|
|
206
|
-
getValuesGroupByInstance({
|
|
207
|
-
instancesIds,
|
|
208
|
-
options,
|
|
209
|
-
sadotOptions,
|
|
210
|
-
}),
|
|
211
|
-
getCustomFieldEntriesByInstanceId({
|
|
212
|
-
instancesIds,
|
|
213
|
-
options,
|
|
214
|
-
sadotOptions,
|
|
215
|
-
}),
|
|
216
|
-
]);
|
|
217
|
-
|
|
218
|
-
// Attach custom fields to the instances
|
|
219
|
-
instances.forEach((instance) => {
|
|
220
|
-
const { id } = instance;
|
|
221
|
-
|
|
222
|
-
const instanceValues = valuesGroupByInstance[id];
|
|
223
|
-
const serializedCustomFieldsValues = instanceValues ? serializeCustomFields(instanceValues, definitionsMap) : {};
|
|
224
|
-
|
|
225
|
-
const customFields = sadotOptions.useCustomFieldsEntries
|
|
226
|
-
? customFieldEntriesByInstanceId[id]
|
|
227
|
-
: serializedCustomFieldsValues;
|
|
228
|
-
|
|
229
|
-
scopeAttributes.forEach((attribute) => {
|
|
230
|
-
const identifier = instance[attribute];
|
|
231
|
-
|
|
232
|
-
const entityCustomFieldDefinitions = identifierCustomFieldDefinitionsMapping[identifier];
|
|
233
|
-
if (entityCustomFieldDefinitions?.length > 0) {
|
|
234
|
-
entityCustomFieldDefinitions.forEach((customFieldDefinition) => {
|
|
235
|
-
if (customFields[customFieldDefinition.name] === undefined) {
|
|
236
|
-
customFields[customFieldDefinition.name] = null;
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
instance.customFields = customFields;
|
|
242
|
-
options.attributesToRemove?.forEach?.((attribute) => {
|
|
243
|
-
delete instance.dataValues?.[attribute];
|
|
244
|
-
// if raw:
|
|
245
|
-
delete instance?.[attribute];
|
|
246
|
-
});
|
|
247
|
-
// sequelize will think customFields changed also in 'find', so we need to mark it as unchanged
|
|
248
|
-
if (hookType === 'afterFind') {
|
|
249
|
-
// changed() could be undefined, i.e in raw: true
|
|
250
|
-
instance?.changed?.('customFields', false);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
export default enrichResults;
|
package/src/hooks/find.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-param-reassign */
|
|
2
|
-
import logger from '../utils/logger';
|
|
3
|
-
|
|
4
|
-
const doScopeAttributesMissing = (
|
|
5
|
-
scopeAttributes: string[],
|
|
6
|
-
queryAttributes: (string | string[])[],
|
|
7
|
-
): string[] => {
|
|
8
|
-
const attributes = scopeAttributes
|
|
9
|
-
.filter((attribute) => !queryAttributes.includes(attribute));
|
|
10
|
-
if (!queryAttributes.includes?.('id')) {
|
|
11
|
-
attributes.push('id');
|
|
12
|
-
}
|
|
13
|
-
return attributes;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
// eslint-disable-next-line import/prefer-default-export
|
|
17
|
-
export const beforeFind = (scopeAttributes: string[]) => (options): void => {
|
|
18
|
-
const { attributes: queryAttributes } = options;
|
|
19
|
-
if (queryAttributes?.includes?.('customFields')) {
|
|
20
|
-
const missingScopeAttributes = doScopeAttributesMissing(scopeAttributes, queryAttributes);
|
|
21
|
-
logger.debug('sadot - before find hook');
|
|
22
|
-
if (missingScopeAttributes?.length > 0) {
|
|
23
|
-
queryAttributes.push(...missingScopeAttributes);
|
|
24
|
-
options.attributesToRemove = missingScopeAttributes;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
};
|