@autofleet/sadot 0.7.0-beta.2.1 → 0.7.0-beta.2.3
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/scopes/filter.d.ts +6 -6
- package/dist/scopes/filter.js +49 -53
- package/package.json +2 -2
- package/src/scopes/filter.ts +66 -82
package/dist/scopes/filter.d.ts
CHANGED
|
@@ -24,14 +24,14 @@ export type CustomFieldFilterOptions = {
|
|
|
24
24
|
* @param name - The model type name used to join custom_field_definitions.
|
|
25
25
|
* @returns A function that takes conditions and returns the Sequelize options object.
|
|
26
26
|
*/
|
|
27
|
-
export declare const customFieldsFilterScope: (name: string) => ({ replacementsMap
|
|
28
|
-
replacementsMap:
|
|
29
|
-
scopeValue:
|
|
27
|
+
export declare const customFieldsFilterScope: (name: string) => ({ replacementsMap, scopeValue }: {
|
|
28
|
+
replacementsMap: any;
|
|
29
|
+
scopeValue: any;
|
|
30
30
|
}) => CustomFieldFilterOptions;
|
|
31
31
|
export declare const scopeName = "filterByCustomFields";
|
|
32
32
|
export declare const customFieldsSortScope: (name: string) => ({ replacementsMap, scopeValue: sort }: {
|
|
33
|
-
replacementsMap:
|
|
34
|
-
scopeValue:
|
|
33
|
+
replacementsMap: any;
|
|
34
|
+
scopeValue: any;
|
|
35
35
|
}) => {
|
|
36
36
|
attributes?: undefined;
|
|
37
37
|
order?: undefined;
|
|
@@ -41,6 +41,6 @@ export declare const customFieldsSortScope: (name: string) => ({ replacementsMap
|
|
|
41
41
|
include: (string | import("sequelize/types/utils").Literal)[][];
|
|
42
42
|
};
|
|
43
43
|
order: import("sequelize/types/utils").Literal[];
|
|
44
|
-
replacements:
|
|
44
|
+
replacements: any;
|
|
45
45
|
};
|
|
46
46
|
export {};
|
package/dist/scopes/filter.js
CHANGED
|
@@ -21,42 +21,6 @@ const castIfNeeded = (conditionValue) => {
|
|
|
21
21
|
return '';
|
|
22
22
|
};
|
|
23
23
|
const AND_DELIMETER = ' AND ';
|
|
24
|
-
const OR_DELIMETER = ' OR ';
|
|
25
|
-
const CD_TABLE_ALIAS = 'cd';
|
|
26
|
-
const CD_NAME_COLUMN = `${CD_TABLE_ALIAS}.name`;
|
|
27
|
-
const CV_TABLE_ALIAS = 'cv';
|
|
28
|
-
const CV_VALUE_COLUMN = `(${CV_TABLE_ALIAS}.value)`;
|
|
29
|
-
const castValueToJsonb = (value) => `to_jsonb(${value}::text)`;
|
|
30
|
-
/**
|
|
31
|
-
* Helper function to build condition strings for the WHERE clause.
|
|
32
|
-
*/
|
|
33
|
-
// eslint-disable-next-line max-len
|
|
34
|
-
const buildConditionString = (key, condition, replacements) => {
|
|
35
|
-
const replacementKey = Object.keys(replacements).find((randomString) => replacements[randomString] === key);
|
|
36
|
-
if (!replacementKey)
|
|
37
|
-
return false;
|
|
38
|
-
if (Array.isArray(condition)) {
|
|
39
|
-
if (condition.length === 0)
|
|
40
|
-
return false;
|
|
41
|
-
if (typeof condition[0] === 'string') {
|
|
42
|
-
const values = condition.map((v) => `:${replacements[v]}`).join(',');
|
|
43
|
-
return `(custom_fields->> :${replacementKey}) IN (${values})`;
|
|
44
|
-
}
|
|
45
|
-
return condition.map((c) => {
|
|
46
|
-
const valRep = replacements[c.value];
|
|
47
|
-
return `(custom_fields->> :${replacementKey})${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
|
|
48
|
-
}).join(AND_DELIMETER);
|
|
49
|
-
}
|
|
50
|
-
if (typeof condition === 'string' || typeof condition === 'number') {
|
|
51
|
-
const conditionRep = replacements[condition];
|
|
52
|
-
return `(custom_fields->> :${replacementKey}) ${castIfNeeded(condition)} = :${conditionRep}`;
|
|
53
|
-
}
|
|
54
|
-
if (condition?.operator) {
|
|
55
|
-
const valueRep = replacements[condition.value];
|
|
56
|
-
return `(custom_fields->> :${replacementKey}) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
|
|
57
|
-
}
|
|
58
|
-
return false;
|
|
59
|
-
};
|
|
60
24
|
/**
|
|
61
25
|
* A Sequelize scope for filtering models by custom fields.
|
|
62
26
|
* This scope builds a WHERE clause to be applied on the main query.
|
|
@@ -64,16 +28,49 @@ const buildConditionString = (key, condition, replacements) => {
|
|
|
64
28
|
* @param name - The model type name used to join custom_field_definitions.
|
|
65
29
|
* @returns A function that takes conditions and returns the Sequelize options object.
|
|
66
30
|
*/
|
|
67
|
-
const customFieldsFilterScope = (name) => ({ replacementsMap
|
|
31
|
+
const customFieldsFilterScope = (name) => ({ replacementsMap, scopeValue }) => {
|
|
32
|
+
const conditions = scopeValue;
|
|
33
|
+
const replacements = replacementsMap;
|
|
68
34
|
if (!conditions || Object.keys(conditions).length === 0) {
|
|
69
35
|
return {};
|
|
70
36
|
}
|
|
71
37
|
// Build the WHERE clause for custom field filtering
|
|
72
38
|
const conditionsStrings = Object.entries(conditions)
|
|
73
|
-
.map(([key, condition]) =>
|
|
39
|
+
.map(([key, condition]) => {
|
|
40
|
+
const replacemetKey = Object.keys(replacements).find((randomString) => replacements[randomString] === key);
|
|
41
|
+
if (!replacemetKey)
|
|
42
|
+
return false;
|
|
43
|
+
if (Array.isArray(condition)) {
|
|
44
|
+
if (condition.length === 0) {
|
|
45
|
+
// if empty array, the condition is ignored
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (typeof condition[0] === 'string') {
|
|
49
|
+
const values = condition.map((v) => {
|
|
50
|
+
const valRandom = Object.keys(replacements).find((randomString) => replacements[randomString] === v);
|
|
51
|
+
return ` :${valRandom} `;
|
|
52
|
+
}).join(',');
|
|
53
|
+
return `(custom_fields->> :${replacemetKey} ) IN ( ${values} )`;
|
|
54
|
+
}
|
|
55
|
+
return condition
|
|
56
|
+
.map((c) => {
|
|
57
|
+
const valRep = Object.keys(replacements).find((key) => replacements[key] === c.value);
|
|
58
|
+
return `(custom_fields->> :${replacemetKey} )${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
|
|
59
|
+
}).join(AND_DELIMETER);
|
|
60
|
+
}
|
|
61
|
+
if (typeof condition === 'string' || typeof condition === 'number') {
|
|
62
|
+
const conditionRep = Object.keys(replacements).find((key) => replacements[key] === condition);
|
|
63
|
+
return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition)} = :${conditionRep}`;
|
|
64
|
+
}
|
|
65
|
+
if (condition?.operator) {
|
|
66
|
+
const valueRep = Object.keys(replacements).find((key) => replacements[key] === condition.value);
|
|
67
|
+
return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
})
|
|
74
71
|
.filter(Boolean);
|
|
75
72
|
if (conditionsStrings.length === 0) {
|
|
76
|
-
return {
|
|
73
|
+
return {};
|
|
77
74
|
}
|
|
78
75
|
const customFieldConditions = conditionsStrings.join(AND_DELIMETER);
|
|
79
76
|
const subQuery = `${'SELECT model_id FROM ('
|
|
@@ -100,21 +97,20 @@ const customFieldsSortScope = (name) => ({ replacementsMap, scopeValue: sort })
|
|
|
100
97
|
}
|
|
101
98
|
const randomStr = (0, helpers_1.generateRandomString)();
|
|
102
99
|
const includes = Object.entries(sort).map(([key]) => {
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
return [
|
|
100
|
+
const replacemetKey = Object.keys(replacementsMap).find((randomString) => replacementsMap[randomString] === key);
|
|
101
|
+
return ([
|
|
106
102
|
sequelize_typescript_1.Sequelize.literal(`(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
];
|
|
103
|
+
SELECT value
|
|
104
|
+
FROM (SELECT cv.model_id, cv.value
|
|
105
|
+
FROM custom_field_values AS cv INNER JOIN custom_field_definitions AS cd
|
|
106
|
+
ON cv.custom_field_definition_id = cd.id
|
|
107
|
+
AND cd.model_type = '${name}'
|
|
108
|
+
WHERE cv.model_id = "${name}"."id"
|
|
109
|
+
AND cd.name = :${replacemetKey}
|
|
110
|
+
) AS CustomFieldAggregation
|
|
111
|
+
)
|
|
112
|
+
`), randomStr,
|
|
113
|
+
]);
|
|
118
114
|
});
|
|
119
115
|
const orders = Object.entries(sort).map(([, value]) => sequelize_typescript_1.Sequelize.literal(`"${randomStr}" ${value}`));
|
|
120
116
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/sadot",
|
|
3
|
-
"version": "0.7.0-beta.2.
|
|
3
|
+
"version": "0.7.0-beta.2.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"linter": "./node_modules/.bin/eslint .",
|
|
10
10
|
"test": "jest --forceExit --runInBand",
|
|
11
11
|
"coverage": "jest --coverage --forceExit --runInBand && rm -rf ./coverage",
|
|
12
|
-
"build-to-local-repo": "npm run build && cp -r dist/*
|
|
12
|
+
"build-to-local-repo": "npm run build && cp -r dist/* ../task-ms/node_modules/@autofleet/sadot/dist",
|
|
13
13
|
"dev": "nodemon",
|
|
14
14
|
"watch": "npm-watch build-to-local-repo",
|
|
15
15
|
"publish-dev": "npm run build && npm publish --tag dev"
|
package/src/scopes/filter.ts
CHANGED
|
@@ -13,73 +13,30 @@ const { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;
|
|
|
13
13
|
* More types to be added (TBA).
|
|
14
14
|
*/
|
|
15
15
|
type ConditionWithOperator = {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
operator: string;
|
|
17
|
+
value: string;
|
|
18
18
|
};
|
|
19
19
|
export type ConditionValue = ConditionWithOperator | ConditionWithOperator[] | string | string[];
|
|
20
20
|
|
|
21
21
|
export type CustomFieldSort = {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
field: string;
|
|
23
|
+
direction: 'ASC' | 'DESC';
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export type CustomFieldFilterOptions = {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
where?: WhereOptions;
|
|
28
|
+
replacements?: Record<string, string>;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const castIfNeeded = (conditionValue: string): string => {
|
|
32
32
|
if (moment.isDate(conditionValue)) {
|
|
33
33
|
return '::timestamp';
|
|
34
|
-
}
|
|
35
|
-
if (!Number.isNaN(Number(conditionValue))) {
|
|
34
|
+
} if (!Number.isNaN(Number(conditionValue))) {
|
|
36
35
|
return '::numeric';
|
|
37
36
|
}
|
|
38
37
|
return '';
|
|
39
38
|
};
|
|
40
39
|
const AND_DELIMETER = ' AND ';
|
|
41
|
-
const OR_DELIMETER = ' OR ';
|
|
42
|
-
|
|
43
|
-
const CD_TABLE_ALIAS = 'cd';
|
|
44
|
-
const CD_NAME_COLUMN = `${CD_TABLE_ALIAS}.name`;
|
|
45
|
-
const CV_TABLE_ALIAS = 'cv';
|
|
46
|
-
const CV_VALUE_COLUMN = `(${CV_TABLE_ALIAS}.value)`;
|
|
47
|
-
const castValueToJsonb = (value) => `to_jsonb(${value}::text)`;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Helper function to build condition strings for the WHERE clause.
|
|
51
|
-
*/
|
|
52
|
-
// eslint-disable-next-line max-len
|
|
53
|
-
const buildConditionString = (key: string, condition: ConditionValue | ConditionValue[], replacements: Record<string, string>): string | false => {
|
|
54
|
-
const replacementKey = Object.keys(replacements).find((randomString) => replacements[randomString] === key);
|
|
55
|
-
if (!replacementKey) return false;
|
|
56
|
-
|
|
57
|
-
if (Array.isArray(condition)) {
|
|
58
|
-
if (condition.length === 0) return false;
|
|
59
|
-
|
|
60
|
-
if (typeof condition[0] === 'string') {
|
|
61
|
-
const values = condition.map((v) => `:${replacements[v]}`).join(',');
|
|
62
|
-
return `(custom_fields->> :${replacementKey}) IN (${values})`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return condition.map((c) => {
|
|
66
|
-
const valRep = replacements[c.value];
|
|
67
|
-
return `(custom_fields->> :${replacementKey})${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
|
|
68
|
-
}).join(AND_DELIMETER);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (typeof condition === 'string'|| typeof condition === 'number') {
|
|
72
|
-
const conditionRep = replacements[condition];
|
|
73
|
-
return `(custom_fields->> :${replacementKey}) ${castIfNeeded(condition)} = :${conditionRep}`;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (condition?.operator) {
|
|
77
|
-
const valueRep = replacements[condition.value];
|
|
78
|
-
return `(custom_fields->> :${replacementKey}) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return false;
|
|
82
|
-
};
|
|
83
40
|
|
|
84
41
|
/**
|
|
85
42
|
* A Sequelize scope for filtering models by custom fields.
|
|
@@ -90,31 +47,60 @@ const buildConditionString = (key: string, condition: ConditionValue | Condition
|
|
|
90
47
|
*/
|
|
91
48
|
export const customFieldsFilterScope = (
|
|
92
49
|
name: string,
|
|
93
|
-
) => (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
scopeValue: conditions,
|
|
97
|
-
}: { replacementsMap: Record<string, string>, scopeValue: Record<string, ConditionValue> },
|
|
98
|
-
): CustomFieldFilterOptions => {
|
|
50
|
+
) => ({ replacementsMap, scopeValue }): CustomFieldFilterOptions => {
|
|
51
|
+
const conditions: Record<string, ConditionValue> = scopeValue;
|
|
52
|
+
const replacements: Record<string, string> = replacementsMap;
|
|
99
53
|
if (!conditions || Object.keys(conditions).length === 0) {
|
|
100
54
|
return {};
|
|
101
55
|
}
|
|
102
|
-
|
|
103
56
|
// Build the WHERE clause for custom field filtering
|
|
104
57
|
const conditionsStrings = Object.entries(conditions)
|
|
105
|
-
.map(
|
|
106
|
-
|
|
58
|
+
.map(
|
|
59
|
+
([key, condition]) => {
|
|
60
|
+
const replacemetKey = Object.keys(replacements).find((randomString) => replacements[randomString] === key);
|
|
61
|
+
if (!replacemetKey) return false;
|
|
62
|
+
|
|
63
|
+
if (Array.isArray(condition)) {
|
|
64
|
+
if (condition.length === 0) {
|
|
65
|
+
// if empty array, the condition is ignored
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (typeof condition[0] === 'string') {
|
|
69
|
+
const values = condition.map((v) => {
|
|
70
|
+
const valRandom = Object.keys(replacements).find((randomString) => replacements[randomString] === v);
|
|
71
|
+
return ` :${valRandom} `;
|
|
72
|
+
}).join(',');
|
|
73
|
+
return `(custom_fields->> :${replacemetKey} ) IN ( ${values} )`;
|
|
74
|
+
}
|
|
75
|
+
return condition
|
|
76
|
+
.map((c) => {
|
|
77
|
+
const valRep = Object.keys(replacements).find((key) => replacements[key] === c.value);
|
|
78
|
+
return `(custom_fields->> :${replacemetKey} )${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
|
|
79
|
+
}).join(AND_DELIMETER);
|
|
80
|
+
}
|
|
81
|
+
if (typeof condition === 'string' || typeof condition === 'number') {
|
|
82
|
+
const conditionRep = Object.keys(replacements).find((key) => replacements[key] === condition);
|
|
83
|
+
return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition)} = :${conditionRep}`;
|
|
84
|
+
}
|
|
85
|
+
if (condition?.operator) {
|
|
86
|
+
const valueRep = Object.keys(replacements).find((key) => replacements[key] === condition.value);
|
|
87
|
+
return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
},
|
|
91
|
+
)
|
|
92
|
+
.filter(Boolean);
|
|
107
93
|
if (conditionsStrings.length === 0) {
|
|
108
|
-
return {
|
|
94
|
+
return {};
|
|
109
95
|
}
|
|
110
96
|
const customFieldConditions = conditionsStrings.join(AND_DELIMETER);
|
|
111
97
|
const subQuery = `${'SELECT model_id FROM ('
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
98
|
+
+ 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
|
|
99
|
+
+ 'FROM custom_field_values AS cv '
|
|
100
|
+
+ 'INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id '
|
|
101
|
+
+ `AND cd.model_type = '${name}'`
|
|
102
|
+
+ 'GROUP BY cv.model_id'
|
|
103
|
+
+ ') AS CustomFieldAggregation WHERE '} ${customFieldConditions}`;
|
|
118
104
|
return {
|
|
119
105
|
where: {
|
|
120
106
|
id: {
|
|
@@ -129,28 +115,26 @@ export const scopeName = CUSTOM_FIELDS_FILTER_SCOPE;
|
|
|
129
115
|
|
|
130
116
|
export const customFieldsSortScope = (
|
|
131
117
|
name: string,
|
|
132
|
-
) => ({ replacementsMap, scopeValue: sort }
|
|
118
|
+
) => ({ replacementsMap, scopeValue: sort }) => {
|
|
133
119
|
if (!sort || sort.length === 0) {
|
|
134
120
|
return {};
|
|
135
121
|
}
|
|
136
|
-
|
|
137
122
|
const randomStr = generateRandomString();
|
|
138
123
|
const includes = Object.entries(sort).map(([key]) => {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
return [
|
|
124
|
+
const replacemetKey = Object.keys(replacementsMap).find((randomString) => replacementsMap[randomString] === key);
|
|
125
|
+
return ([
|
|
142
126
|
Sequelize.literal(`(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
];
|
|
127
|
+
SELECT value
|
|
128
|
+
FROM (SELECT cv.model_id, cv.value
|
|
129
|
+
FROM custom_field_values AS cv INNER JOIN custom_field_definitions AS cd
|
|
130
|
+
ON cv.custom_field_definition_id = cd.id
|
|
131
|
+
AND cd.model_type = '${name}'
|
|
132
|
+
WHERE cv.model_id = "${name}"."id"
|
|
133
|
+
AND cd.name = :${replacemetKey}
|
|
134
|
+
) AS CustomFieldAggregation
|
|
135
|
+
)
|
|
136
|
+
`), randomStr,
|
|
137
|
+
]);
|
|
154
138
|
});
|
|
155
139
|
|
|
156
140
|
const orders = Object.entries(sort).map(([, value]) => Sequelize.literal(`"${randomStr}" ${value}`));
|