@autofleet/sadot 1.6.18-beta.2 → 1.6.18-beta.21
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/_virtual/_@oxc-project_runtime@0.103.0/helpers/decorate.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/decorate.js +10 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/decorateMetadata.cjs +8 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/decorateMetadata.js +7 -0
- package/dist/_virtual/rolldown_runtime.cjs +43 -0
- package/dist/{chunk-DJulfCLt.js → _virtual/rolldown_runtime.js} +1 -2
- package/dist/api/index.cjs +12 -0
- package/dist/api/index.js +11 -0
- package/dist/api/v1/definition/index.cjs +113 -0
- package/dist/api/v1/definition/index.js +112 -0
- package/dist/api/v1/definition/validations.cjs +128 -0
- package/dist/api/v1/definition/validations.js +125 -0
- package/dist/api/v1/errors.cjs +14 -0
- package/dist/api/v1/errors.js +13 -0
- package/dist/api/v1/field-policies/index.cjs +104 -0
- package/dist/api/v1/field-policies/index.js +103 -0
- package/dist/api/v1/index.cjs +16 -0
- package/dist/api/v1/index.js +15 -0
- package/dist/api/v1/validator/index.cjs +132 -0
- package/dist/api/v1/validator/index.js +131 -0
- package/dist/api/v1/validator/validations.cjs +40 -0
- package/dist/api/v1/validator/validations.d.cts +22 -0
- package/dist/api/v1/validator/validations.d.ts +22 -0
- package/dist/api/v1/validator/validations.js +38 -0
- package/dist/errors/index.cjs +62 -0
- package/dist/errors/index.js +55 -0
- package/dist/events/index.cjs +52 -0
- package/dist/events/index.js +50 -0
- package/dist/hooks/enrich.cjs +175 -0
- package/dist/hooks/enrich.js +175 -0
- package/dist/hooks/find.cjs +22 -0
- package/dist/hooks/find.js +22 -0
- package/dist/hooks/hooks.cjs +310 -0
- package/dist/hooks/hooks.js +302 -0
- package/dist/hooks/index.cjs +3 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/utils/updateInstanceValues.cjs +27 -0
- package/dist/hooks/utils/updateInstanceValues.js +27 -0
- package/dist/index.cjs +48 -33508
- package/dist/index.d.cts +13 -396
- package/dist/index.d.ts +14 -394
- package/dist/index.js +21 -33435
- package/dist/init-state.cjs +56 -0
- package/dist/init-state.d.cts +35 -0
- package/dist/init-state.d.ts +35 -0
- package/dist/init-state.js +55 -0
- package/dist/mat-path-state.cjs +10 -0
- package/dist/mat-path-state.js +9 -0
- package/dist/migrations/001-create-core-tables.cjs +70 -0
- package/dist/migrations/001-create-core-tables.js +68 -0
- package/dist/migrations/002-create-custom-field-entries.cjs +27 -0
- package/dist/migrations/002-create-custom-field-entries.js +25 -0
- package/dist/migrations/003-create-custom-field-model-type-map.cjs +35 -0
- package/dist/migrations/003-create-custom-field-model-type-map.js +33 -0
- package/dist/migrations/004-create-field-policy-tables.cjs +29 -0
- package/dist/migrations/004-create-field-policy-tables.js +27 -0
- package/dist/migrations/005-add-entity-type-to-field-policies.cjs +20 -0
- package/dist/migrations/005-add-entity-type-to-field-policies.js +18 -0
- package/dist/migrations/index.cjs +94 -0
- package/dist/migrations/index.js +95 -0
- package/dist/models/CustomFieldDefinition.cjs +147 -0
- package/dist/models/CustomFieldDefinition.d.cts +40 -0
- package/dist/models/CustomFieldDefinition.d.ts +40 -0
- package/dist/models/CustomFieldDefinition.js +146 -0
- package/dist/models/CustomFieldEntries.cjs +71 -0
- package/dist/models/CustomFieldEntries.d.cts +15 -0
- package/dist/models/CustomFieldEntries.d.ts +15 -0
- package/dist/models/CustomFieldEntries.js +70 -0
- package/dist/models/CustomFieldModelTypeMap.cjs +62 -0
- package/dist/models/CustomFieldModelTypeMap.d.cts +14 -0
- package/dist/models/CustomFieldModelTypeMap.d.ts +14 -0
- package/dist/models/CustomFieldModelTypeMap.js +61 -0
- package/dist/models/CustomFieldValue.cjs +109 -0
- package/dist/models/CustomFieldValue.d.cts +20 -0
- package/dist/models/CustomFieldValue.d.ts +20 -0
- package/dist/models/CustomFieldValue.js +108 -0
- package/dist/models/CustomValidator.cjs +68 -0
- package/dist/models/CustomValidator.d.cts +18 -0
- package/dist/models/CustomValidator.d.ts +18 -0
- package/dist/models/CustomValidator.js +67 -0
- package/dist/models/FieldPolicy.cjs +54 -0
- package/dist/models/FieldPolicy.d.cts +25 -0
- package/dist/models/FieldPolicy.d.ts +25 -0
- package/dist/models/FieldPolicy.js +53 -0
- package/dist/models/index.cjs +163 -0
- package/dist/models/index.d.ts +11 -0
- package/dist/models/index.js +161 -0
- package/dist/models/tests/AssociatedTestModel.cjs +51 -0
- package/dist/models/tests/AssociatedTestModel.d.ts +2 -0
- package/dist/models/tests/AssociatedTestModel.js +50 -0
- package/dist/models/tests/TestModel.cjs +43 -0
- package/dist/models/tests/TestModel.d.ts +2 -0
- package/dist/models/tests/TestModel.js +42 -0
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.cjs +35 -0
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.d.ts +2 -0
- package/dist/models/tests/contextAwareModels/ContextAwareTestModel.js +34 -0
- package/dist/models/tests/contextAwareModels/ContextTestModel.cjs +33 -0
- package/dist/models/tests/contextAwareModels/ContextTestModel.d.ts +1 -0
- package/dist/models/tests/contextAwareModels/ContextTestModel.js +32 -0
- package/dist/repository/definition.cjs +221 -0
- package/dist/repository/definition.js +212 -0
- package/dist/repository/entries.cjs +69 -0
- package/dist/repository/entries.js +67 -0
- package/dist/repository/field-policy.cjs +69 -0
- package/dist/repository/field-policy.d.cts +35 -0
- package/dist/repository/field-policy.d.ts +35 -0
- package/dist/repository/field-policy.js +59 -0
- package/dist/repository/utils/formatValues.cjs +14 -0
- package/dist/repository/utils/formatValues.js +14 -0
- package/dist/repository/validator.cjs +85 -0
- package/dist/repository/validator.js +78 -0
- package/dist/repository/value.cjs +77 -0
- package/dist/repository/value.js +76 -0
- package/dist/scopes/filter.cjs +56 -0
- package/dist/scopes/filter.d.cts +22 -0
- package/dist/scopes/filter.d.ts +22 -0
- package/dist/scopes/filter.js +54 -0
- package/dist/scopes/helpers/filter.helpers.cjs +152 -0
- package/dist/scopes/helpers/filter.helpers.d.cts +16 -0
- package/dist/scopes/helpers/filter.helpers.d.ts +16 -0
- package/dist/scopes/helpers/filter.helpers.js +147 -0
- package/dist/scopes/index.cjs +1 -0
- package/dist/scopes/index.js +3 -0
- package/dist/types/index.d.cts +75 -0
- package/dist/types/index.d.ts +76 -0
- package/dist/utils/constants/index.cjs +32 -0
- package/dist/utils/constants/index.d.cts +23 -0
- package/dist/utils/constants/index.d.ts +23 -0
- package/dist/utils/constants/index.js +29 -0
- package/dist/utils/db/index.cjs +15 -0
- package/dist/utils/db/index.js +14 -0
- package/dist/utils/helpers/index.cjs +23 -0
- package/dist/utils/helpers/index.d.cts +30 -0
- package/dist/utils/helpers/index.d.ts +30 -0
- package/dist/utils/helpers/index.js +21 -0
- package/dist/utils/init.cjs +115 -0
- package/dist/utils/init.js +111 -0
- package/dist/utils/logger/index.cjs +15 -0
- package/dist/utils/logger/index.js +12 -0
- package/dist/utils/scopeAttributes.cjs +12 -0
- package/dist/utils/scopeAttributes.js +11 -0
- package/dist/utils/validations/index.cjs +34 -0
- package/dist/utils/validations/index.js +32 -0
- package/dist/utils/validations/schema/custom-fields.cjs +9 -0
- package/dist/utils/validations/schema/custom-fields.d.cts +6 -0
- package/dist/utils/validations/schema/custom-fields.d.ts +6 -0
- package/dist/utils/validations/schema/custom-fields.js +7 -0
- package/dist/utils/validations/schema/validator-schema.cjs +102 -0
- package/dist/utils/validations/schema/validator-schema.js +99 -0
- package/dist/utils/validations/validators/index.cjs +45 -0
- package/dist/utils/validations/validators/index.js +42 -0
- package/dist/utils/validations/validators/multi-select-dropdown.validator.cjs +7 -0
- package/dist/utils/validations/validators/multi-select-dropdown.validator.js +7 -0
- package/dist/utils/validations/validators/multi-select-status.validator.cjs +10 -0
- package/dist/utils/validations/validators/multi-select-status.validator.js +10 -0
- package/dist/utils/validations/validators/multi-select.helpers.cjs +12 -0
- package/dist/utils/validations/validators/multi-select.helpers.js +10 -0
- package/dist/utils/validations/validators/select.validator.cjs +12 -0
- package/dist/utils/validations/validators/select.validator.js +10 -0
- package/dist/utils/validations/validators/status.validator.cjs +12 -0
- package/dist/utils/validations/validators/status.validator.js +10 -0
- package/package.json +4 -4
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CustomFieldOptions } from "../types/index.cjs";
|
|
2
|
+
import { ConditionValue } from "./helpers/filter.helpers.cjs";
|
|
3
|
+
import { customFields } from "@autofleet/common-types";
|
|
4
|
+
import { Literal } from "sequelize/types/utils";
|
|
5
|
+
|
|
6
|
+
//#region src/scopes/filter.d.ts
|
|
7
|
+
|
|
8
|
+
declare const customFieldsSortScope: (name: string, options?: Pick<CustomFieldOptions, "useCustomFieldsEntries">) => ({
|
|
9
|
+
replacementsMap,
|
|
10
|
+
scopeValue: sort
|
|
11
|
+
}: {
|
|
12
|
+
replacementsMap: Record<string, string>;
|
|
13
|
+
scopeValue: Record<string, ConditionValue>;
|
|
14
|
+
}) => {
|
|
15
|
+
attributes: {
|
|
16
|
+
include: (string | Literal)[][];
|
|
17
|
+
};
|
|
18
|
+
order: Literal[];
|
|
19
|
+
replacements: Record<string, string>;
|
|
20
|
+
} | Record<string, never>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { customFieldsSortScope };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CustomFieldOptions } from "../types/index.js";
|
|
2
|
+
import { ConditionValue } from "./helpers/filter.helpers.js";
|
|
3
|
+
import { customFields } from "@autofleet/common-types";
|
|
4
|
+
import { Literal } from "sequelize/types/utils";
|
|
5
|
+
|
|
6
|
+
//#region src/scopes/filter.d.ts
|
|
7
|
+
|
|
8
|
+
declare const customFieldsSortScope: (name: string, options?: Pick<CustomFieldOptions, "useCustomFieldsEntries">) => ({
|
|
9
|
+
replacementsMap,
|
|
10
|
+
scopeValue: sort
|
|
11
|
+
}: {
|
|
12
|
+
replacementsMap: Record<string, string>;
|
|
13
|
+
scopeValue: Record<string, ConditionValue>;
|
|
14
|
+
}) => {
|
|
15
|
+
attributes: {
|
|
16
|
+
include: (string | Literal)[][];
|
|
17
|
+
};
|
|
18
|
+
order: Literal[];
|
|
19
|
+
replacements: Record<string, string>;
|
|
20
|
+
} | Record<string, never>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { customFieldsSortScope };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { generateRandomString } from "../utils/helpers/index.js";
|
|
2
|
+
import { SubQueryType, formatConditionsForEntries, formatConditionsForValues, getFilterCustomFieldsSubQuery, getSortCustomFieldsSubQuery } from "./helpers/filter.helpers.js";
|
|
3
|
+
import { Op } from "sequelize";
|
|
4
|
+
import { Sequelize } from "sequelize-typescript";
|
|
5
|
+
import { customFields } from "@autofleet/common-types";
|
|
6
|
+
|
|
7
|
+
//#region src/scopes/filter.ts
|
|
8
|
+
/**
|
|
9
|
+
* A Sequelize scope for filtering models by custom fields.
|
|
10
|
+
* This scope builds a WHERE clause to be applied on the main query.
|
|
11
|
+
*
|
|
12
|
+
* @param name - The model type name used to join custom_field_definitions.
|
|
13
|
+
* @returns A function that takes conditions and returns the Sequelize options object.
|
|
14
|
+
*/
|
|
15
|
+
const customFieldsFilterScope = (name, options) => ({ replacementsMap: replacements, scopeValue: conditions }) => {
|
|
16
|
+
if (!conditions || Object.keys(conditions).length === 0) return {};
|
|
17
|
+
const queryType = options?.useCustomFieldsEntries ? SubQueryType.ENTRIES : SubQueryType.VALUES;
|
|
18
|
+
const reverseReplacementsMap = new Map(Object.entries(replacements).map(([key, value]) => [value, key]));
|
|
19
|
+
const conditionsStrings = Object.entries(conditions).map(([key, condition]) => {
|
|
20
|
+
switch (queryType) {
|
|
21
|
+
case SubQueryType.ENTRIES: return formatConditionsForEntries(key, condition, reverseReplacementsMap);
|
|
22
|
+
case SubQueryType.VALUES: return formatConditionsForValues(key, condition, reverseReplacementsMap);
|
|
23
|
+
default: return false;
|
|
24
|
+
}
|
|
25
|
+
}).filter(Boolean);
|
|
26
|
+
if (conditionsStrings.length === 0) return {};
|
|
27
|
+
const subQuery = getFilterCustomFieldsSubQuery(queryType, name, conditionsStrings);
|
|
28
|
+
return {
|
|
29
|
+
where: { id: { [Op.in]: Sequelize.literal(`(${subQuery})`) } },
|
|
30
|
+
replacements
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const scopeName = customFields.CUSTOM_FIELDS_FILTER_SCOPE;
|
|
34
|
+
const customFieldsSortScope = (name, options) => ({ replacementsMap, scopeValue: sort }) => {
|
|
35
|
+
if (!sort || sort.length === 0) return {};
|
|
36
|
+
const queryType = options?.useCustomFieldsEntries ? SubQueryType.ENTRIES : SubQueryType.VALUES;
|
|
37
|
+
const randomStr = generateRandomString();
|
|
38
|
+
const includes = Object.entries(sort).map(([key]) => {
|
|
39
|
+
const replacementKey = Object.keys(replacementsMap).find((randomString) => replacementsMap[randomString] === key);
|
|
40
|
+
return [Sequelize.literal(getSortCustomFieldsSubQuery(queryType, name, replacementKey)), randomStr];
|
|
41
|
+
});
|
|
42
|
+
const orders = Object.entries(sort).map(([, sortObject]) => {
|
|
43
|
+
const direction = typeof sortObject === "string" ? sortObject : Object.values(sortObject)[0];
|
|
44
|
+
return Sequelize.literal(`"${randomStr}" ${direction || "ASC"}`);
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
attributes: { include: includes },
|
|
48
|
+
order: orders,
|
|
49
|
+
replacements: replacementsMap
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
export { customFieldsFilterScope, customFieldsSortScope };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/scopes/helpers/filter.helpers.ts
|
|
3
|
+
let SubQueryType = /* @__PURE__ */ function(SubQueryType$1) {
|
|
4
|
+
SubQueryType$1["VALUES"] = "values";
|
|
5
|
+
SubQueryType$1["ENTRIES"] = "entries";
|
|
6
|
+
return SubQueryType$1;
|
|
7
|
+
}({});
|
|
8
|
+
const isConditionStringArray = (input) => Array.isArray(input) && typeof input[0] === "string";
|
|
9
|
+
const isBooleanString = (input) => ["true", "false"].includes(input.toString());
|
|
10
|
+
const isDate = (input) => input instanceof Date || Object.prototype.toString.call(input) === "[object Date]";
|
|
11
|
+
const castValueToJsonb = (value, type) => `to_jsonb(${value}::${type})`;
|
|
12
|
+
const castValueToJsonbText = (value) => castValueToJsonb(value, "text");
|
|
13
|
+
const castValueToJsonbBoolean = (value) => castValueToJsonb(value, "boolean");
|
|
14
|
+
const castValueToJsonbNumeric = (value) => castValueToJsonb(value, "numeric");
|
|
15
|
+
const castIfNeeded = (columnName, conditionValue) => {
|
|
16
|
+
if (isDate(conditionValue)) return castValueToJsonb(columnName, "timestamp");
|
|
17
|
+
return columnName;
|
|
18
|
+
};
|
|
19
|
+
const AND_DELIMITER = " AND ";
|
|
20
|
+
const OR_DELIMITER = " OR ";
|
|
21
|
+
const CD_TABLE_ALIAS = "cd";
|
|
22
|
+
const CD_NAME_COLUMN = `${CD_TABLE_ALIAS}.name`;
|
|
23
|
+
const CV_TABLE_ALIAS = "cv";
|
|
24
|
+
const CV_VALUE_COLUMN = `${CV_TABLE_ALIAS}.value`;
|
|
25
|
+
const CE_TABLE_ALIAS = "ce";
|
|
26
|
+
const getSingleConditionWithOperator = (value, operator, replacementKey, reverseReplacementsMap) => {
|
|
27
|
+
let type = "text";
|
|
28
|
+
if (isDate(value)) type = "date";
|
|
29
|
+
else if (!Number.isNaN(Number(value))) type = "numeric";
|
|
30
|
+
else if (isBooleanString(value)) type = "boolean";
|
|
31
|
+
const replacedValue = reverseReplacementsMap.get(value);
|
|
32
|
+
return `(jsonb_extract_path_text(${CE_TABLE_ALIAS}.custom_fields, :${replacementKey})::${type}) ${operator} :${replacedValue}`;
|
|
33
|
+
};
|
|
34
|
+
const getFormattedValue = (value) => {
|
|
35
|
+
let formattedValue = value;
|
|
36
|
+
if (isBooleanString(value)) formattedValue = value === "true";
|
|
37
|
+
else if (!Number.isNaN(Number(value))) formattedValue = Number(value);
|
|
38
|
+
return formattedValue;
|
|
39
|
+
};
|
|
40
|
+
const getJSONSubQuery = (value, key) => {
|
|
41
|
+
const formattedValue = getFormattedValue(value);
|
|
42
|
+
const jsonQuery = JSON.stringify({ [key]: formattedValue });
|
|
43
|
+
let jsonQueryWithStringBoolean;
|
|
44
|
+
if (isBooleanString(value)) jsonQueryWithStringBoolean = `${CE_TABLE_ALIAS}.custom_fields @> '${JSON.stringify({ [key]: value })}'`;
|
|
45
|
+
return `
|
|
46
|
+
(
|
|
47
|
+
${jsonQueryWithStringBoolean ? `${jsonQueryWithStringBoolean} OR` : ""}
|
|
48
|
+
${CE_TABLE_ALIAS}.custom_fields @> '${jsonQuery}'
|
|
49
|
+
)
|
|
50
|
+
`;
|
|
51
|
+
};
|
|
52
|
+
const getFilterCustomFieldsSubQuery = (queryType, modelType, conditionsStrings) => {
|
|
53
|
+
switch (queryType) {
|
|
54
|
+
case SubQueryType.VALUES: return `
|
|
55
|
+
SELECT ${CV_TABLE_ALIAS}.model_id
|
|
56
|
+
FROM custom_field_values AS ${CV_TABLE_ALIAS}
|
|
57
|
+
INNER JOIN custom_field_definitions AS ${CD_TABLE_ALIAS} ON ${CV_TABLE_ALIAS}.custom_field_definition_id = ${CD_TABLE_ALIAS}.id
|
|
58
|
+
${AND_DELIMITER}${CD_TABLE_ALIAS}.model_type = '${modelType}'
|
|
59
|
+
WHERE ${conditionsStrings.join(OR_DELIMITER)}
|
|
60
|
+
${AND_DELIMITER}${CV_TABLE_ALIAS}.deleted_at IS NULL${AND_DELIMITER}${CD_TABLE_ALIAS}.deleted_at IS NULL
|
|
61
|
+
GROUP BY ${CV_TABLE_ALIAS}.model_id
|
|
62
|
+
HAVING COUNT(DISTINCT ${CV_TABLE_ALIAS}.custom_field_definition_id) = ${conditionsStrings.length}
|
|
63
|
+
`.replace(/\n/g, "");
|
|
64
|
+
case SubQueryType.ENTRIES: return `
|
|
65
|
+
SELECT ce.model_id
|
|
66
|
+
FROM custom_field_entries ce
|
|
67
|
+
JOIN custom_field_definitions cfd
|
|
68
|
+
ON ce.model_type = cfd.model_type
|
|
69
|
+
AND ce.entity_id = cfd.entity_id
|
|
70
|
+
WHERE
|
|
71
|
+
cfd.deleted_at IS NULL AND
|
|
72
|
+
${conditionsStrings.join(AND_DELIMITER)}
|
|
73
|
+
`;
|
|
74
|
+
default: throw new Error("Invalid query type");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const getSortCustomFieldsSubQuery = (queryType, modelType, replacementKey) => {
|
|
78
|
+
switch (queryType) {
|
|
79
|
+
case SubQueryType.VALUES: return `(
|
|
80
|
+
SELECT value
|
|
81
|
+
FROM (SELECT cv.model_id, cv.value
|
|
82
|
+
FROM custom_field_values AS cv INNER JOIN custom_field_definitions AS cd
|
|
83
|
+
ON cv.custom_field_definition_id = cd.id
|
|
84
|
+
${AND_DELIMITER}cd.model_type = '${modelType}'
|
|
85
|
+
WHERE cv.model_id = "${modelType}"."id"
|
|
86
|
+
${AND_DELIMITER}cd.name = :${replacementKey}
|
|
87
|
+
) AS CustomFieldAggregation
|
|
88
|
+
)
|
|
89
|
+
`;
|
|
90
|
+
case SubQueryType.ENTRIES: return `(
|
|
91
|
+
SELECT
|
|
92
|
+
customFields.value
|
|
93
|
+
FROM
|
|
94
|
+
custom_field_entries AS ${CE_TABLE_ALIAS},
|
|
95
|
+
jsonb_each_text(custom_fields) AS customFields
|
|
96
|
+
WHERE
|
|
97
|
+
customFields.key = :${replacementKey}${AND_DELIMITER}
|
|
98
|
+
${CE_TABLE_ALIAS}.model_type = '${modelType}'${AND_DELIMITER}
|
|
99
|
+
${CE_TABLE_ALIAS}.model_id = "${modelType}"."id"
|
|
100
|
+
)
|
|
101
|
+
`;
|
|
102
|
+
default: throw new Error("Invalid query type");
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const formatConditionsForValues = (key, condition, reverseReplacementsMap) => {
|
|
106
|
+
const replacementKey = reverseReplacementsMap.get(key);
|
|
107
|
+
if (!replacementKey) return false;
|
|
108
|
+
const columnCondition = `(${CD_NAME_COLUMN} = :${replacementKey})`;
|
|
109
|
+
if (Array.isArray(condition)) {
|
|
110
|
+
if (condition.length === 0) return false;
|
|
111
|
+
if (isConditionStringArray(condition)) return `(${columnCondition}${AND_DELIMITER}${CV_VALUE_COLUMN} IN (${condition.flatMap((v) => {
|
|
112
|
+
const valRandom = reverseReplacementsMap.get(v);
|
|
113
|
+
if (isBooleanString(v)) return [castValueToJsonbText(`:${valRandom}`), castValueToJsonbBoolean(`:${valRandom}`)];
|
|
114
|
+
if (!Number.isNaN(Number(v))) return castValueToJsonbNumeric(`:${valRandom}`);
|
|
115
|
+
return castValueToJsonbText(`:${valRandom}`);
|
|
116
|
+
}).join(",")}))`;
|
|
117
|
+
return condition.map((c) => {
|
|
118
|
+
const valueAsJsonb = castValueToJsonbText(`:${reverseReplacementsMap.get(c.value)}`);
|
|
119
|
+
return `(${columnCondition}${AND_DELIMITER}${castIfNeeded(CV_VALUE_COLUMN, c.value)} ${c.operator} ${valueAsJsonb})`;
|
|
120
|
+
}).join(AND_DELIMITER);
|
|
121
|
+
}
|
|
122
|
+
if (typeof condition === "string" || typeof condition === "number") {
|
|
123
|
+
const conditionRep = reverseReplacementsMap.get(condition);
|
|
124
|
+
const valueAsJsonb = !Number.isNaN(Number(condition)) ? castValueToJsonbNumeric(`:${conditionRep}`) : castValueToJsonbText(`:${conditionRep}`);
|
|
125
|
+
const valueAsJsonbBoolean = isBooleanString(condition) ? `${OR_DELIMITER}${CV_VALUE_COLUMN} = ${castValueToJsonbBoolean(`:${conditionRep}`)}` : "";
|
|
126
|
+
return `(${columnCondition}${AND_DELIMITER}(${castIfNeeded(CV_VALUE_COLUMN, condition)} = ${valueAsJsonb}${valueAsJsonbBoolean}))`;
|
|
127
|
+
}
|
|
128
|
+
if (condition?.operator) {
|
|
129
|
+
const valueAsJsonb = castValueToJsonbText(`:${reverseReplacementsMap.get(condition.value)}`);
|
|
130
|
+
return `( ${columnCondition}${AND_DELIMITER}${castIfNeeded(CV_VALUE_COLUMN, condition.value)} ${condition.operator} ${valueAsJsonb})`;
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
};
|
|
134
|
+
const formatConditionsForEntries = (key, condition, reverseReplacementsMap) => {
|
|
135
|
+
const replacementKey = reverseReplacementsMap.get(key);
|
|
136
|
+
if (!replacementKey) return false;
|
|
137
|
+
if (Array.isArray(condition)) {
|
|
138
|
+
if (condition.length === 0) return false;
|
|
139
|
+
if (isConditionStringArray(condition)) return `( ${condition.map((value) => getJSONSubQuery(value, key)).join(`${OR_DELIMITER}\n`)})`;
|
|
140
|
+
return condition.map((c) => getSingleConditionWithOperator(c.value, c.operator, replacementKey, reverseReplacementsMap)).join(AND_DELIMITER);
|
|
141
|
+
}
|
|
142
|
+
if (typeof condition === "string" || typeof condition === "number") return getJSONSubQuery(condition, key);
|
|
143
|
+
if (condition?.operator) return getSingleConditionWithOperator(condition.value, condition.operator, replacementKey, reverseReplacementsMap);
|
|
144
|
+
return false;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
exports.SubQueryType = SubQueryType;
|
|
149
|
+
exports.formatConditionsForEntries = formatConditionsForEntries;
|
|
150
|
+
exports.formatConditionsForValues = formatConditionsForValues;
|
|
151
|
+
exports.getFilterCustomFieldsSubQuery = getFilterCustomFieldsSubQuery;
|
|
152
|
+
exports.getSortCustomFieldsSubQuery = getSortCustomFieldsSubQuery;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { WhereOptions } from "sequelize";
|
|
2
|
+
|
|
3
|
+
//#region src/scopes/helpers/filter.helpers.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Type representing possible condition values.
|
|
7
|
+
* Currently supporting strings and arrays of strings.
|
|
8
|
+
* More types to be added (TBA).
|
|
9
|
+
*/
|
|
10
|
+
interface ConditionWithOperator {
|
|
11
|
+
operator: string;
|
|
12
|
+
value: string;
|
|
13
|
+
}
|
|
14
|
+
type ConditionValue = ConditionWithOperator | ConditionWithOperator[] | string | string[];
|
|
15
|
+
//#endregion
|
|
16
|
+
export { ConditionValue };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { WhereOptions } from "sequelize";
|
|
2
|
+
|
|
3
|
+
//#region src/scopes/helpers/filter.helpers.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Type representing possible condition values.
|
|
7
|
+
* Currently supporting strings and arrays of strings.
|
|
8
|
+
* More types to be added (TBA).
|
|
9
|
+
*/
|
|
10
|
+
interface ConditionWithOperator {
|
|
11
|
+
operator: string;
|
|
12
|
+
value: string;
|
|
13
|
+
}
|
|
14
|
+
type ConditionValue = ConditionWithOperator | ConditionWithOperator[] | string | string[];
|
|
15
|
+
//#endregion
|
|
16
|
+
export { ConditionValue };
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
//#region src/scopes/helpers/filter.helpers.ts
|
|
2
|
+
let SubQueryType = /* @__PURE__ */ function(SubQueryType$1) {
|
|
3
|
+
SubQueryType$1["VALUES"] = "values";
|
|
4
|
+
SubQueryType$1["ENTRIES"] = "entries";
|
|
5
|
+
return SubQueryType$1;
|
|
6
|
+
}({});
|
|
7
|
+
const isConditionStringArray = (input) => Array.isArray(input) && typeof input[0] === "string";
|
|
8
|
+
const isBooleanString = (input) => ["true", "false"].includes(input.toString());
|
|
9
|
+
const isDate = (input) => input instanceof Date || Object.prototype.toString.call(input) === "[object Date]";
|
|
10
|
+
const castValueToJsonb = (value, type) => `to_jsonb(${value}::${type})`;
|
|
11
|
+
const castValueToJsonbText = (value) => castValueToJsonb(value, "text");
|
|
12
|
+
const castValueToJsonbBoolean = (value) => castValueToJsonb(value, "boolean");
|
|
13
|
+
const castValueToJsonbNumeric = (value) => castValueToJsonb(value, "numeric");
|
|
14
|
+
const castIfNeeded = (columnName, conditionValue) => {
|
|
15
|
+
if (isDate(conditionValue)) return castValueToJsonb(columnName, "timestamp");
|
|
16
|
+
return columnName;
|
|
17
|
+
};
|
|
18
|
+
const AND_DELIMITER = " AND ";
|
|
19
|
+
const OR_DELIMITER = " OR ";
|
|
20
|
+
const CD_TABLE_ALIAS = "cd";
|
|
21
|
+
const CD_NAME_COLUMN = `${CD_TABLE_ALIAS}.name`;
|
|
22
|
+
const CV_TABLE_ALIAS = "cv";
|
|
23
|
+
const CV_VALUE_COLUMN = `${CV_TABLE_ALIAS}.value`;
|
|
24
|
+
const CE_TABLE_ALIAS = "ce";
|
|
25
|
+
const getSingleConditionWithOperator = (value, operator, replacementKey, reverseReplacementsMap) => {
|
|
26
|
+
let type = "text";
|
|
27
|
+
if (isDate(value)) type = "date";
|
|
28
|
+
else if (!Number.isNaN(Number(value))) type = "numeric";
|
|
29
|
+
else if (isBooleanString(value)) type = "boolean";
|
|
30
|
+
const replacedValue = reverseReplacementsMap.get(value);
|
|
31
|
+
return `(jsonb_extract_path_text(${CE_TABLE_ALIAS}.custom_fields, :${replacementKey})::${type}) ${operator} :${replacedValue}`;
|
|
32
|
+
};
|
|
33
|
+
const getFormattedValue = (value) => {
|
|
34
|
+
let formattedValue = value;
|
|
35
|
+
if (isBooleanString(value)) formattedValue = value === "true";
|
|
36
|
+
else if (!Number.isNaN(Number(value))) formattedValue = Number(value);
|
|
37
|
+
return formattedValue;
|
|
38
|
+
};
|
|
39
|
+
const getJSONSubQuery = (value, key) => {
|
|
40
|
+
const formattedValue = getFormattedValue(value);
|
|
41
|
+
const jsonQuery = JSON.stringify({ [key]: formattedValue });
|
|
42
|
+
let jsonQueryWithStringBoolean;
|
|
43
|
+
if (isBooleanString(value)) jsonQueryWithStringBoolean = `${CE_TABLE_ALIAS}.custom_fields @> '${JSON.stringify({ [key]: value })}'`;
|
|
44
|
+
return `
|
|
45
|
+
(
|
|
46
|
+
${jsonQueryWithStringBoolean ? `${jsonQueryWithStringBoolean} OR` : ""}
|
|
47
|
+
${CE_TABLE_ALIAS}.custom_fields @> '${jsonQuery}'
|
|
48
|
+
)
|
|
49
|
+
`;
|
|
50
|
+
};
|
|
51
|
+
const getFilterCustomFieldsSubQuery = (queryType, modelType, conditionsStrings) => {
|
|
52
|
+
switch (queryType) {
|
|
53
|
+
case SubQueryType.VALUES: return `
|
|
54
|
+
SELECT ${CV_TABLE_ALIAS}.model_id
|
|
55
|
+
FROM custom_field_values AS ${CV_TABLE_ALIAS}
|
|
56
|
+
INNER JOIN custom_field_definitions AS ${CD_TABLE_ALIAS} ON ${CV_TABLE_ALIAS}.custom_field_definition_id = ${CD_TABLE_ALIAS}.id
|
|
57
|
+
${AND_DELIMITER}${CD_TABLE_ALIAS}.model_type = '${modelType}'
|
|
58
|
+
WHERE ${conditionsStrings.join(OR_DELIMITER)}
|
|
59
|
+
${AND_DELIMITER}${CV_TABLE_ALIAS}.deleted_at IS NULL${AND_DELIMITER}${CD_TABLE_ALIAS}.deleted_at IS NULL
|
|
60
|
+
GROUP BY ${CV_TABLE_ALIAS}.model_id
|
|
61
|
+
HAVING COUNT(DISTINCT ${CV_TABLE_ALIAS}.custom_field_definition_id) = ${conditionsStrings.length}
|
|
62
|
+
`.replace(/\n/g, "");
|
|
63
|
+
case SubQueryType.ENTRIES: return `
|
|
64
|
+
SELECT ce.model_id
|
|
65
|
+
FROM custom_field_entries ce
|
|
66
|
+
JOIN custom_field_definitions cfd
|
|
67
|
+
ON ce.model_type = cfd.model_type
|
|
68
|
+
AND ce.entity_id = cfd.entity_id
|
|
69
|
+
WHERE
|
|
70
|
+
cfd.deleted_at IS NULL AND
|
|
71
|
+
${conditionsStrings.join(AND_DELIMITER)}
|
|
72
|
+
`;
|
|
73
|
+
default: throw new Error("Invalid query type");
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const getSortCustomFieldsSubQuery = (queryType, modelType, replacementKey) => {
|
|
77
|
+
switch (queryType) {
|
|
78
|
+
case SubQueryType.VALUES: return `(
|
|
79
|
+
SELECT value
|
|
80
|
+
FROM (SELECT cv.model_id, cv.value
|
|
81
|
+
FROM custom_field_values AS cv INNER JOIN custom_field_definitions AS cd
|
|
82
|
+
ON cv.custom_field_definition_id = cd.id
|
|
83
|
+
${AND_DELIMITER}cd.model_type = '${modelType}'
|
|
84
|
+
WHERE cv.model_id = "${modelType}"."id"
|
|
85
|
+
${AND_DELIMITER}cd.name = :${replacementKey}
|
|
86
|
+
) AS CustomFieldAggregation
|
|
87
|
+
)
|
|
88
|
+
`;
|
|
89
|
+
case SubQueryType.ENTRIES: return `(
|
|
90
|
+
SELECT
|
|
91
|
+
customFields.value
|
|
92
|
+
FROM
|
|
93
|
+
custom_field_entries AS ${CE_TABLE_ALIAS},
|
|
94
|
+
jsonb_each_text(custom_fields) AS customFields
|
|
95
|
+
WHERE
|
|
96
|
+
customFields.key = :${replacementKey}${AND_DELIMITER}
|
|
97
|
+
${CE_TABLE_ALIAS}.model_type = '${modelType}'${AND_DELIMITER}
|
|
98
|
+
${CE_TABLE_ALIAS}.model_id = "${modelType}"."id"
|
|
99
|
+
)
|
|
100
|
+
`;
|
|
101
|
+
default: throw new Error("Invalid query type");
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const formatConditionsForValues = (key, condition, reverseReplacementsMap) => {
|
|
105
|
+
const replacementKey = reverseReplacementsMap.get(key);
|
|
106
|
+
if (!replacementKey) return false;
|
|
107
|
+
const columnCondition = `(${CD_NAME_COLUMN} = :${replacementKey})`;
|
|
108
|
+
if (Array.isArray(condition)) {
|
|
109
|
+
if (condition.length === 0) return false;
|
|
110
|
+
if (isConditionStringArray(condition)) return `(${columnCondition}${AND_DELIMITER}${CV_VALUE_COLUMN} IN (${condition.flatMap((v) => {
|
|
111
|
+
const valRandom = reverseReplacementsMap.get(v);
|
|
112
|
+
if (isBooleanString(v)) return [castValueToJsonbText(`:${valRandom}`), castValueToJsonbBoolean(`:${valRandom}`)];
|
|
113
|
+
if (!Number.isNaN(Number(v))) return castValueToJsonbNumeric(`:${valRandom}`);
|
|
114
|
+
return castValueToJsonbText(`:${valRandom}`);
|
|
115
|
+
}).join(",")}))`;
|
|
116
|
+
return condition.map((c) => {
|
|
117
|
+
const valueAsJsonb = castValueToJsonbText(`:${reverseReplacementsMap.get(c.value)}`);
|
|
118
|
+
return `(${columnCondition}${AND_DELIMITER}${castIfNeeded(CV_VALUE_COLUMN, c.value)} ${c.operator} ${valueAsJsonb})`;
|
|
119
|
+
}).join(AND_DELIMITER);
|
|
120
|
+
}
|
|
121
|
+
if (typeof condition === "string" || typeof condition === "number") {
|
|
122
|
+
const conditionRep = reverseReplacementsMap.get(condition);
|
|
123
|
+
const valueAsJsonb = !Number.isNaN(Number(condition)) ? castValueToJsonbNumeric(`:${conditionRep}`) : castValueToJsonbText(`:${conditionRep}`);
|
|
124
|
+
const valueAsJsonbBoolean = isBooleanString(condition) ? `${OR_DELIMITER}${CV_VALUE_COLUMN} = ${castValueToJsonbBoolean(`:${conditionRep}`)}` : "";
|
|
125
|
+
return `(${columnCondition}${AND_DELIMITER}(${castIfNeeded(CV_VALUE_COLUMN, condition)} = ${valueAsJsonb}${valueAsJsonbBoolean}))`;
|
|
126
|
+
}
|
|
127
|
+
if (condition?.operator) {
|
|
128
|
+
const valueAsJsonb = castValueToJsonbText(`:${reverseReplacementsMap.get(condition.value)}`);
|
|
129
|
+
return `( ${columnCondition}${AND_DELIMITER}${castIfNeeded(CV_VALUE_COLUMN, condition.value)} ${condition.operator} ${valueAsJsonb})`;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
};
|
|
133
|
+
const formatConditionsForEntries = (key, condition, reverseReplacementsMap) => {
|
|
134
|
+
const replacementKey = reverseReplacementsMap.get(key);
|
|
135
|
+
if (!replacementKey) return false;
|
|
136
|
+
if (Array.isArray(condition)) {
|
|
137
|
+
if (condition.length === 0) return false;
|
|
138
|
+
if (isConditionStringArray(condition)) return `( ${condition.map((value) => getJSONSubQuery(value, key)).join(`${OR_DELIMITER}\n`)})`;
|
|
139
|
+
return condition.map((c) => getSingleConditionWithOperator(c.value, c.operator, replacementKey, reverseReplacementsMap)).join(AND_DELIMITER);
|
|
140
|
+
}
|
|
141
|
+
if (typeof condition === "string" || typeof condition === "number") return getJSONSubQuery(condition, key);
|
|
142
|
+
if (condition?.operator) return getSingleConditionWithOperator(condition.value, condition.operator, replacementKey, reverseReplacementsMap);
|
|
143
|
+
return false;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
//#endregion
|
|
147
|
+
export { SubQueryType, formatConditionsForEntries, formatConditionsForValues, getFilterCustomFieldsSubQuery, getSortCustomFieldsSubQuery };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const require_filter = require('./filter.cjs');
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { CustomFieldDefinition } from "../models/CustomFieldDefinition.cjs";
|
|
2
|
+
import { ModelCtor, Sequelize } from "sequelize-typescript";
|
|
3
|
+
import { IncludeOptions } from "sequelize";
|
|
4
|
+
|
|
5
|
+
//#region src/types/index.d.ts
|
|
6
|
+
type ModelFetcher = (name: string) => any;
|
|
7
|
+
interface ModelOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Include options for the model
|
|
10
|
+
*/
|
|
11
|
+
include?: (entityIds: string | string[]) => IncludeOptions[];
|
|
12
|
+
/**
|
|
13
|
+
* Custom association for the model
|
|
14
|
+
* @param model - The model to associate with
|
|
15
|
+
*/
|
|
16
|
+
customAssociation?: (model: ModelCtor) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Whether to use the entity id from the instance per scope attribute
|
|
19
|
+
*/
|
|
20
|
+
useEntityIdFromInclude?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Which attributes to include in the model
|
|
23
|
+
*/
|
|
24
|
+
attributes?: string[];
|
|
25
|
+
}
|
|
26
|
+
interface Models {
|
|
27
|
+
name: string;
|
|
28
|
+
scopeAttributes: any[];
|
|
29
|
+
modelOptions?: ModelOptions;
|
|
30
|
+
hasTypeId?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Optional async callback to determine which custom fields are required for an instance.
|
|
33
|
+
* Allows consumer applications to override CustomFieldDefinition.required based on custom logic.
|
|
34
|
+
* @param instance - The model instance being created/updated
|
|
35
|
+
* @param fieldDefinitions - Array of CustomFieldDefinition records
|
|
36
|
+
* @param options - Sequelize options including transaction
|
|
37
|
+
* @returns Array of field names that should be validated as required
|
|
38
|
+
*/
|
|
39
|
+
getRequiredCustomFields?: (instance: any, fieldDefinitions: CustomFieldDefinition[], options: any) => Promise<string[]> | string[];
|
|
40
|
+
creationWebhookHandler?: (instance: any) => any;
|
|
41
|
+
updateWebhookHandler?: (instance: any) => any;
|
|
42
|
+
deletionWebhookHandler?: (instance: any) => any;
|
|
43
|
+
}
|
|
44
|
+
interface CustomFieldOptions {
|
|
45
|
+
models: Models[];
|
|
46
|
+
hasTypeId?: boolean;
|
|
47
|
+
databaseConfig: any;
|
|
48
|
+
getUser: typeof GetUserType;
|
|
49
|
+
sequelize?: Sequelize;
|
|
50
|
+
useCustomFieldsEntries?: boolean;
|
|
51
|
+
useValidators?: boolean;
|
|
52
|
+
useModelTypeMapping?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* When true, use the legacy `sync({ alter: true })` migration approach
|
|
55
|
+
* instead of umzug-managed migrations via the `sadot_migrations` table.
|
|
56
|
+
* Defaults to false (umzug is used by default).
|
|
57
|
+
*/
|
|
58
|
+
runLegacyMigrations?: boolean;
|
|
59
|
+
getRequiredCustomFields?: (instance: any, fieldDefinitions: CustomFieldDefinition[], options: any) => Promise<string[]> | string[];
|
|
60
|
+
/**
|
|
61
|
+
* Optional predicate that returns true when materialized-path RLS is active for the current
|
|
62
|
+
* request. When it returns true, sadot's `userScope` on CFD/CV becomes a no-op so RLS is the
|
|
63
|
+
* sole filter (consumer typically decorates CFD/CV with their mat-path manager so RLS applies).
|
|
64
|
+
* Pass `matPathManager.isScopingRequest` if using `@autofleet/mat-path-mvp`.
|
|
65
|
+
*/
|
|
66
|
+
isMatPathEnabled?: () => boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Optional hook for the effective field-policy route. When provided, sadot mounts
|
|
69
|
+
* GET /field-policies/:entityType/effective and delegates scoped queries through this callback.
|
|
70
|
+
* Typically: (table, cb) => { matPathManager.setScopingMode({ [table]: SCOPING_MODE.ANCESTORS }); return matPathManager.withPaths(cb); }
|
|
71
|
+
*/
|
|
72
|
+
withMatPath?: (tableName: string, callback: (transaction: any) => Promise<any>) => Promise<any>;
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { CustomFieldOptions, ModelFetcher, Models };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { CustomFieldDefinition } from "../models/CustomFieldDefinition.js";
|
|
2
|
+
import "../models/CustomValidator.js";
|
|
3
|
+
import { IncludeOptions } from "sequelize";
|
|
4
|
+
import { ModelCtor, Sequelize } from "sequelize-typescript";
|
|
5
|
+
|
|
6
|
+
//#region src/types/index.d.ts
|
|
7
|
+
type ModelFetcher = (name: string) => any;
|
|
8
|
+
interface ModelOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Include options for the model
|
|
11
|
+
*/
|
|
12
|
+
include?: (entityIds: string | string[]) => IncludeOptions[];
|
|
13
|
+
/**
|
|
14
|
+
* Custom association for the model
|
|
15
|
+
* @param model - The model to associate with
|
|
16
|
+
*/
|
|
17
|
+
customAssociation?: (model: ModelCtor) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Whether to use the entity id from the instance per scope attribute
|
|
20
|
+
*/
|
|
21
|
+
useEntityIdFromInclude?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Which attributes to include in the model
|
|
24
|
+
*/
|
|
25
|
+
attributes?: string[];
|
|
26
|
+
}
|
|
27
|
+
interface Models {
|
|
28
|
+
name: string;
|
|
29
|
+
scopeAttributes: any[];
|
|
30
|
+
modelOptions?: ModelOptions;
|
|
31
|
+
hasTypeId?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Optional async callback to determine which custom fields are required for an instance.
|
|
34
|
+
* Allows consumer applications to override CustomFieldDefinition.required based on custom logic.
|
|
35
|
+
* @param instance - The model instance being created/updated
|
|
36
|
+
* @param fieldDefinitions - Array of CustomFieldDefinition records
|
|
37
|
+
* @param options - Sequelize options including transaction
|
|
38
|
+
* @returns Array of field names that should be validated as required
|
|
39
|
+
*/
|
|
40
|
+
getRequiredCustomFields?: (instance: any, fieldDefinitions: CustomFieldDefinition[], options: any) => Promise<string[]> | string[];
|
|
41
|
+
creationWebhookHandler?: (instance: any) => any;
|
|
42
|
+
updateWebhookHandler?: (instance: any) => any;
|
|
43
|
+
deletionWebhookHandler?: (instance: any) => any;
|
|
44
|
+
}
|
|
45
|
+
interface CustomFieldOptions {
|
|
46
|
+
models: Models[];
|
|
47
|
+
hasTypeId?: boolean;
|
|
48
|
+
databaseConfig: any;
|
|
49
|
+
getUser: typeof GetUserType;
|
|
50
|
+
sequelize?: Sequelize;
|
|
51
|
+
useCustomFieldsEntries?: boolean;
|
|
52
|
+
useValidators?: boolean;
|
|
53
|
+
useModelTypeMapping?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* When true, use the legacy `sync({ alter: true })` migration approach
|
|
56
|
+
* instead of umzug-managed migrations via the `sadot_migrations` table.
|
|
57
|
+
* Defaults to false (umzug is used by default).
|
|
58
|
+
*/
|
|
59
|
+
runLegacyMigrations?: boolean;
|
|
60
|
+
getRequiredCustomFields?: (instance: any, fieldDefinitions: CustomFieldDefinition[], options: any) => Promise<string[]> | string[];
|
|
61
|
+
/**
|
|
62
|
+
* Optional predicate that returns true when materialized-path RLS is active for the current
|
|
63
|
+
* request. When it returns true, sadot's `userScope` on CFD/CV becomes a no-op so RLS is the
|
|
64
|
+
* sole filter (consumer typically decorates CFD/CV with their mat-path manager so RLS applies).
|
|
65
|
+
* Pass `matPathManager.isScopingRequest` if using `@autofleet/mat-path-mvp`.
|
|
66
|
+
*/
|
|
67
|
+
isMatPathEnabled?: () => boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Optional hook for the effective field-policy route. When provided, sadot mounts
|
|
70
|
+
* GET /field-policies/:entityType/effective and delegates scoped queries through this callback.
|
|
71
|
+
* Typically: (table, cb) => { matPathManager.setScopingMode({ [table]: SCOPING_MODE.ANCESTORS }); return matPathManager.withPaths(cb); }
|
|
72
|
+
*/
|
|
73
|
+
withMatPath?: (tableName: string, callback: (transaction: any) => Promise<any>) => Promise<any>;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { CustomFieldOptions, ModelFetcher, Models };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
let _autofleet_common_types = require("@autofleet/common-types");
|
|
3
|
+
|
|
4
|
+
//#region src/utils/constants/index.ts
|
|
5
|
+
const supportedEntities = [
|
|
6
|
+
"businessModelId",
|
|
7
|
+
"fleetId",
|
|
8
|
+
"demandSourceId"
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Supported custom field types
|
|
12
|
+
*/
|
|
13
|
+
let CustomFieldDefinitionType = /* @__PURE__ */ function(CustomFieldDefinitionType$1) {
|
|
14
|
+
CustomFieldDefinitionType$1["NUMBER"] = "number";
|
|
15
|
+
CustomFieldDefinitionType$1["BOOLEAN"] = "boolean";
|
|
16
|
+
CustomFieldDefinitionType$1["DATE"] = "date";
|
|
17
|
+
CustomFieldDefinitionType$1["DATETIME"] = "datetime";
|
|
18
|
+
CustomFieldDefinitionType$1["TEXT"] = "text";
|
|
19
|
+
CustomFieldDefinitionType$1["IMAGE"] = "image";
|
|
20
|
+
CustomFieldDefinitionType$1["SELECT"] = "select";
|
|
21
|
+
CustomFieldDefinitionType$1["STATUS"] = "status";
|
|
22
|
+
CustomFieldDefinitionType$1["FILE"] = "file";
|
|
23
|
+
CustomFieldDefinitionType$1["MULTI_SELECT_STATUS"] = "multi-select-status";
|
|
24
|
+
CustomFieldDefinitionType$1["MULTI_SELECT_DROPDOWN"] = "multi-select-dropdown";
|
|
25
|
+
return CustomFieldDefinitionType$1;
|
|
26
|
+
}({});
|
|
27
|
+
const CUSTOM_FIELDS_FILTER_SCOPE = _autofleet_common_types.customFields.CUSTOM_FIELDS_FILTER_SCOPE;
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
exports.CUSTOM_FIELDS_FILTER_SCOPE = CUSTOM_FIELDS_FILTER_SCOPE;
|
|
31
|
+
exports.CustomFieldDefinitionType = CustomFieldDefinitionType;
|
|
32
|
+
exports.supportedEntities = supportedEntities;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { customFields } from "@autofleet/common-types";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/constants/index.d.ts
|
|
4
|
+
declare const supportedEntities: readonly ["businessModelId", "fleetId", "demandSourceId"];
|
|
5
|
+
/**
|
|
6
|
+
* Supported custom field types
|
|
7
|
+
*/
|
|
8
|
+
declare enum CustomFieldDefinitionType {
|
|
9
|
+
NUMBER = "number",
|
|
10
|
+
BOOLEAN = "boolean",
|
|
11
|
+
DATE = "date",
|
|
12
|
+
DATETIME = "datetime",
|
|
13
|
+
TEXT = "text",
|
|
14
|
+
IMAGE = "image",
|
|
15
|
+
SELECT = "select",
|
|
16
|
+
STATUS = "status",
|
|
17
|
+
FILE = "file",
|
|
18
|
+
MULTI_SELECT_STATUS = "multi-select-status",
|
|
19
|
+
MULTI_SELECT_DROPDOWN = "multi-select-dropdown",
|
|
20
|
+
}
|
|
21
|
+
declare const CUSTOM_FIELDS_FILTER_SCOPE: typeof customFields.CUSTOM_FIELDS_FILTER_SCOPE;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { CUSTOM_FIELDS_FILTER_SCOPE, CustomFieldDefinitionType, supportedEntities };
|