@autofleet/sadot 0.7.0-beta.2 → 0.7.0-beta.2.2

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.
@@ -73,8 +73,11 @@ exports.findValuesByModelIds = findValuesByModelIds;
73
73
  */
74
74
  const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, options = {}) => {
75
75
  const names = Object.keys(valuesToUpdate);
76
- logger_1.default.info(`custom-fields: updating values for ${modelType} ${modelId}`, {
77
- names, options, valuesToUpdate, identifiers,
76
+ logger_1.default.debug(`custom-fields: updating values for ${modelType} ${modelId}`, {
77
+ names,
78
+ optionsKeys: options ? Object.keys(options) : null,
79
+ valuesToUpdate,
80
+ identifiers,
78
81
  });
79
82
  const { modelOptions, transaction } = options;
80
83
  const where = {
@@ -87,7 +90,7 @@ const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, opt
87
90
  const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true, transaction, include: modelOptions.include?.(identifiers) }) || [];
88
91
  const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
89
92
  if (fieldDefinitions.length !== names.length) {
90
- logger_1.default.info(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
93
+ logger_1.default.warn(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
91
94
  const missingDefinitions = names.filter((name) => !fieldDefinitions.some((def) => def.name === name));
92
95
  throw new errors_1.MissingDefinitionError(missingDefinitions);
93
96
  }
@@ -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: replacements, scopeValue: conditions, }: {
28
- replacementsMap: Record<string, string>;
29
- scopeValue: Record<string, ConditionValue>;
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: Record<string, string>;
34
- scopeValue: string[];
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: Record<string, string>;
44
+ replacements: any;
45
45
  };
46
46
  export {};
@@ -21,36 +21,6 @@ const castIfNeeded = (conditionValue) => {
21
21
  return '';
22
22
  };
23
23
  const AND_DELIMETER = ' AND ';
24
- /**
25
- * Helper function to build condition strings for the WHERE clause.
26
- */
27
- // eslint-disable-next-line max-len
28
- const buildConditionString = (key, condition, replacements) => {
29
- const replacementKey = Object.keys(replacements).find((randomString) => replacements[randomString] === key);
30
- if (!replacementKey)
31
- return false;
32
- if (Array.isArray(condition)) {
33
- if (condition.length === 0)
34
- return false;
35
- if (typeof condition[0] === 'string') {
36
- const values = condition.map((v) => `:${replacements[v]}`).join(',');
37
- return `(custom_fields->> :${replacementKey}) IN (${values})`;
38
- }
39
- return condition.map((c) => {
40
- const valRep = replacements[c.value];
41
- return `(custom_fields->> :${replacementKey})${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
42
- }).join(AND_DELIMETER);
43
- }
44
- if (typeof condition === 'string') {
45
- const conditionRep = replacements[condition];
46
- return `(custom_fields->> :${replacementKey}) ${castIfNeeded(condition)} = :${conditionRep}`;
47
- }
48
- if (condition?.operator) {
49
- const valueRep = replacements[condition.value];
50
- return `(custom_fields->> :${replacementKey}) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
51
- }
52
- return false;
53
- };
54
24
  /**
55
25
  * A Sequelize scope for filtering models by custom fields.
56
26
  * This scope builds a WHERE clause to be applied on the main query.
@@ -58,16 +28,51 @@ const buildConditionString = (key, condition, replacements) => {
58
28
  * @param name - The model type name used to join custom_field_definitions.
59
29
  * @returns A function that takes conditions and returns the Sequelize options object.
60
30
  */
61
- const customFieldsFilterScope = (name) => ({ replacementsMap: replacements, scopeValue: conditions, }) => {
31
+ const customFieldsFilterScope = (name) => ({ replacementsMap, scopeValue }) => {
32
+ const conditions = scopeValue;
33
+ const replacements = replacementsMap;
62
34
  if (!conditions || Object.keys(conditions).length === 0) {
63
35
  return {};
64
36
  }
65
37
  // Build the WHERE clause for custom field filtering
66
38
  const conditionsStrings = Object.entries(conditions)
67
- .map(([key, condition]) => buildConditionString(key, condition, replacements))
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') {
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
+ })
68
71
  .filter(Boolean);
69
72
  if (conditionsStrings.length === 0) {
70
- return { replacements };
73
+ return {
74
+ replacements,
75
+ };
71
76
  }
72
77
  const customFieldConditions = conditionsStrings.join(AND_DELIMETER);
73
78
  const subQuery = `${'SELECT model_id FROM ('
@@ -94,21 +99,21 @@ const customFieldsSortScope = (name) => ({ replacementsMap, scopeValue: sort })
94
99
  }
95
100
  const randomStr = (0, helpers_1.generateRandomString)();
96
101
  const includes = Object.entries(sort).map(([key]) => {
97
- const replacementKey = Object.keys(replacementsMap)
98
- .find((randomString) => replacementsMap[randomString] === key);
99
- return [
102
+ const replacemetKey = Object.keys(replacementsMap).find((randomString) => replacementsMap[randomString] === key);
103
+ console.log('sort replacemetKey:', replacemetKey);
104
+ return ([
100
105
  sequelize_typescript_1.Sequelize.literal(`(
101
- SELECT value
102
- FROM (
103
- SELECT cv.model_id, cv.value
104
- FROM custom_field_values AS cv
105
- INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id
106
- AND cd.model_type = '${name}'
107
- WHERE cv.model_id = "${name}"."id" AND cd.name = :${replacementKey}
108
- ) AS CustomFieldAggregation
109
- )`),
110
- randomStr,
111
- ];
106
+ SELECT value
107
+ FROM (SELECT cv.model_id, cv.value
108
+ FROM custom_field_values AS cv INNER JOIN custom_field_definitions AS cd
109
+ ON cv.custom_field_definition_id = cd.id
110
+ AND cd.model_type = '${name}'
111
+ WHERE cv.model_id = "${name}"."id"
112
+ AND cd.name = :${replacemetKey}
113
+ ) AS CustomFieldAggregation
114
+ )
115
+ `), randomStr,
116
+ ]);
112
117
  });
113
118
  const orders = Object.entries(sort).map(([, value]) => sequelize_typescript_1.Sequelize.literal(`"${randomStr}" ${value}`));
114
119
  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.2",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -62,6 +62,9 @@
62
62
  "peerDependencies": {
63
63
  "@autofleet/sheilta": ">=1.4.0-beta.2"
64
64
  },
65
+ "engines": {
66
+ "node": ">=16.0.0"
67
+ },
65
68
  "author": "Autofleet",
66
69
  "license": "ISC"
67
70
  }
@@ -55,8 +55,11 @@ export const updateValues = async (
55
55
  options: FindOptions & { modelOptions?: ModelOptions } = {},
56
56
  ): Promise<CustomFieldValue[]> => {
57
57
  const names = Object.keys(valuesToUpdate);
58
- logger.info(`custom-fields: updating values for ${modelType} ${modelId}`, {
59
- names, options, valuesToUpdate, identifiers,
58
+ logger.debug(`custom-fields: updating values for ${modelType} ${modelId}`, {
59
+ names,
60
+ optionsKeys: options ? Object.keys(options) : null,
61
+ valuesToUpdate,
62
+ identifiers,
60
63
  });
61
64
  const { modelOptions, transaction } = options;
62
65
 
@@ -72,7 +75,7 @@ export const updateValues = async (
72
75
 
73
76
  const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
74
77
  if (fieldDefinitions.length !== names.length) {
75
- logger.info(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
78
+ logger.warn(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
76
79
  const missingDefinitions = names.filter((name) => !fieldDefinitions.some((def) => def.name === name));
77
80
  throw new MissingDefinitionError(missingDefinitions);
78
81
  }
@@ -13,67 +13,31 @@ const { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;
13
13
  * More types to be added (TBA).
14
14
  */
15
15
  type ConditionWithOperator = {
16
- operator: string;
17
- value: string;
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
- field: string;
23
- direction: 'ASC' | 'DESC';
22
+ field: string;
23
+ direction: 'ASC' | 'DESC';
24
24
  }
25
25
 
26
26
  export type CustomFieldFilterOptions = {
27
- where?: WhereOptions;
28
- replacements?: Record<string, string>;
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
40
 
42
- /**
43
- * Helper function to build condition strings for the WHERE clause.
44
- */
45
- // eslint-disable-next-line max-len
46
- const buildConditionString = (key: string, condition: ConditionValue | ConditionValue[], replacements: Record<string, string>): string | false => {
47
- const replacementKey = Object.keys(replacements).find((randomString) => replacements[randomString] === key);
48
- if (!replacementKey) return false;
49
-
50
- if (Array.isArray(condition)) {
51
- if (condition.length === 0) return false;
52
-
53
- if (typeof condition[0] === 'string') {
54
- const values = condition.map((v) => `:${replacements[v]}`).join(',');
55
- return `(custom_fields->> :${replacementKey}) IN (${values})`;
56
- }
57
-
58
- return condition.map((c) => {
59
- const valRep = replacements[c.value];
60
- return `(custom_fields->> :${replacementKey})${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
61
- }).join(AND_DELIMETER);
62
- }
63
-
64
- if (typeof condition === 'string') {
65
- const conditionRep = replacements[condition];
66
- return `(custom_fields->> :${replacementKey}) ${castIfNeeded(condition)} = :${conditionRep}`;
67
- }
68
-
69
- if (condition?.operator) {
70
- const valueRep = replacements[condition.value];
71
- return `(custom_fields->> :${replacementKey}) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
72
- }
73
-
74
- return false;
75
- };
76
-
77
41
  /**
78
42
  * A Sequelize scope for filtering models by custom fields.
79
43
  * This scope builds a WHERE clause to be applied on the main query.
@@ -83,33 +47,62 @@ const buildConditionString = (key: string, condition: ConditionValue | Condition
83
47
  */
84
48
  export const customFieldsFilterScope = (
85
49
  name: string,
86
- ) => (
87
- {
88
- replacementsMap: replacements,
89
- scopeValue: conditions,
90
- }: { replacementsMap: Record<string, string>, scopeValue: Record<string, ConditionValue> },
91
- ): CustomFieldFilterOptions => {
50
+ ) => ({ replacementsMap, scopeValue }): CustomFieldFilterOptions => {
51
+ const conditions: Record<string, ConditionValue> = scopeValue;
52
+ const replacements: Record<string, string> = replacementsMap;
92
53
  if (!conditions || Object.keys(conditions).length === 0) {
93
54
  return {};
94
55
  }
95
-
96
56
  // Build the WHERE clause for custom field filtering
97
57
  const conditionsStrings = Object.entries(conditions)
98
- .map(([key, condition]) => buildConditionString(key, condition, replacements))
99
- .filter(Boolean) as string[];
100
-
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') {
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);
101
93
  if (conditionsStrings.length === 0) {
102
- return { replacements };
94
+ return {
95
+ replacements,
96
+ };
103
97
  }
104
-
105
98
  const customFieldConditions = conditionsStrings.join(AND_DELIMETER);
106
99
  const subQuery = `${'SELECT model_id FROM ('
107
- + 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
108
- + 'FROM custom_field_values AS cv '
109
- + 'INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id '
110
- + `AND cd.model_type = '${name}'`
111
- + 'GROUP BY cv.model_id'
112
- + ') AS CustomFieldAggregation WHERE '} ${customFieldConditions}`;
100
+ + 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
101
+ + 'FROM custom_field_values AS cv '
102
+ + 'INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id '
103
+ + `AND cd.model_type = '${name}'`
104
+ + 'GROUP BY cv.model_id'
105
+ + ') AS CustomFieldAggregation WHERE '} ${customFieldConditions}`;
113
106
  return {
114
107
  where: {
115
108
  id: {
@@ -124,28 +117,27 @@ export const scopeName = CUSTOM_FIELDS_FILTER_SCOPE;
124
117
 
125
118
  export const customFieldsSortScope = (
126
119
  name: string,
127
- ) => ({ replacementsMap, scopeValue: sort }: { replacementsMap: Record<string, string>, scopeValue: string[] }) => {
120
+ ) => ({ replacementsMap, scopeValue: sort }) => {
128
121
  if (!sort || sort.length === 0) {
129
122
  return {};
130
123
  }
131
-
132
124
  const randomStr = generateRandomString();
133
125
  const includes = Object.entries(sort).map(([key]) => {
134
- const replacementKey = Object.keys(replacementsMap)
135
- .find((randomString) => replacementsMap[randomString] === key);
136
- return [
126
+ const replacemetKey = Object.keys(replacementsMap).find((randomString) => replacementsMap[randomString] === key);
127
+ console.log('sort replacemetKey:', replacemetKey);
128
+ return ([
137
129
  Sequelize.literal(`(
138
- SELECT value
139
- FROM (
140
- SELECT cv.model_id, cv.value
141
- FROM custom_field_values AS cv
142
- INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id
143
- AND cd.model_type = '${name}'
144
- WHERE cv.model_id = "${name}"."id" AND cd.name = :${replacementKey}
145
- ) AS CustomFieldAggregation
146
- )`),
147
- randomStr,
148
- ];
130
+ SELECT value
131
+ FROM (SELECT cv.model_id, cv.value
132
+ FROM custom_field_values AS cv INNER JOIN custom_field_definitions AS cd
133
+ ON cv.custom_field_definition_id = cd.id
134
+ AND cd.model_type = '${name}'
135
+ WHERE cv.model_id = "${name}"."id"
136
+ AND cd.name = :${replacemetKey}
137
+ ) AS CustomFieldAggregation
138
+ )
139
+ `), randomStr,
140
+ ]);
149
141
  });
150
142
 
151
143
  const orders = Object.entries(sort).map(([, value]) => Sequelize.literal(`"${randomStr}" ${value}`));