@autofleet/sadot 0.6.11 → 0.7.0-beta.1

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,11 +73,8 @@ 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.debug(`custom-fields: updating values for ${modelType} ${modelId}`, {
77
- names,
78
- optionsKeys: options ? Object.keys(options) : null,
79
- valuesToUpdate,
80
- identifiers,
76
+ logger_1.default.info(`custom-fields: updating values for ${modelType} ${modelId}`, {
77
+ names, options, valuesToUpdate, identifiers,
81
78
  });
82
79
  const { modelOptions, transaction } = options;
83
80
  const where = {
@@ -90,7 +87,7 @@ const updateValues = async (modelType, modelId, identifiers, valuesToUpdate, opt
90
87
  const fieldDefinitions = await DefinitionRepo.findAll(where, { withDisabled: true, transaction, include: modelOptions.include?.(identifiers) }) || [];
91
88
  const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
92
89
  if (fieldDefinitions.length !== names.length) {
93
- logger_1.default.warn(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
90
+ logger_1.default.info(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
94
91
  const missingDefinitions = names.filter((name) => !fieldDefinitions.some((def) => def.name === name));
95
92
  throw new errors_1.MissingDefinitionError(missingDefinitions);
96
93
  }
@@ -24,9 +24,15 @@ 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) => (conditions: Record<string, ConditionValue>) => CustomFieldFilterOptions;
27
+ export declare const customFieldsFilterScope: (name: string) => ({ replacementsMap, scopeValue }: {
28
+ replacementsMap: any;
29
+ scopeValue: any;
30
+ }) => CustomFieldFilterOptions;
28
31
  export declare const scopeName = "filterByCustomFields";
29
- export declare const customFieldsSortScope: (name: string) => (sort: CustomFieldSort[]) => {
32
+ export declare const customFieldsSortScope: (name: string) => ({ replacementsMap, scopeValue: sort }: {
33
+ replacementsMap: any;
34
+ scopeValue: any;
35
+ }) => {
30
36
  attributes?: undefined;
31
37
  order?: undefined;
32
38
  replacements?: undefined;
@@ -35,6 +41,6 @@ export declare const customFieldsSortScope: (name: string) => (sort: CustomField
35
41
  include: (string | import("sequelize/types/utils").Literal)[][];
36
42
  };
37
43
  order: import("sequelize/types/utils").Literal[];
38
- replacements: Record<string, string>;
44
+ replacements: any;
39
45
  };
40
46
  export {};
@@ -21,12 +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
24
  /**
31
25
  * A Sequelize scope for filtering models by custom fields.
32
26
  * This scope builds a WHERE clause to be applied on the main query.
@@ -34,19 +28,19 @@ const castValueToJsonb = (value) => `to_jsonb(${value}::text)`;
34
28
  * @param name - The model type name used to join custom_field_definitions.
35
29
  * @returns A function that takes conditions and returns the Sequelize options object.
36
30
  */
37
- const customFieldsFilterScope = (name) => (conditions) => {
31
+ const customFieldsFilterScope = (name) => ({ replacementsMap, scopeValue }) => {
32
+ const conditions = scopeValue;
33
+ const replacements = replacementsMap;
34
+ console.log('filter replacementsMap:', replacements);
38
35
  if (!conditions || Object.keys(conditions).length === 0) {
39
36
  return {};
40
37
  }
41
- const ConditionNameRandomStr = (0, helpers_1.generateRandomString)();
42
- const replacements = {};
43
- replacements[ConditionNameRandomStr] = `${name}`;
44
38
  // Build the WHERE clause for custom field filtering
45
39
  const conditionsStrings = Object.entries(conditions)
46
40
  .map(([key, condition]) => {
47
- const replacemetKey = (0, helpers_1.generateRandomString)();
48
- replacements[replacemetKey] = `${key}`;
49
- const columnCondition = `(${CD_NAME_COLUMN} = :${replacemetKey})`;
41
+ const replacemetKey = Object.keys(replacements).find((randomString) => replacements[randomString] === key);
42
+ if (!replacemetKey)
43
+ return false;
50
44
  if (Array.isArray(condition)) {
51
45
  if (condition.length === 0) {
52
46
  // if empty array, the condition is ignored
@@ -54,48 +48,41 @@ const customFieldsFilterScope = (name) => (conditions) => {
54
48
  }
55
49
  if (typeof condition[0] === 'string') {
56
50
  const values = condition.map((v) => {
57
- const valRandom = (0, helpers_1.generateRandomString)();
58
- replacements[`${valRandom}`] = `${v}`;
59
- return castValueToJsonb(`:${valRandom}`);
51
+ const valRandom = Object.keys(replacements).find((randomString) => replacements[randomString] === v);
52
+ return ` :${valRandom} `;
60
53
  }).join(',');
61
- return `(${columnCondition} AND ${CV_VALUE_COLUMN} IN ( ${values} ))`;
54
+ return `(custom_fields->> :${replacemetKey} ) IN ( ${values} )`;
62
55
  }
63
56
  return condition
64
57
  .map((c) => {
65
- const valRep = (0, helpers_1.generateRandomString)();
66
- replacements[valRep] = `${c.value}`;
67
- const valueAsJsonb = castValueToJsonb(`:${valRep}`);
68
- return `(${columnCondition} AND ${CV_VALUE_COLUMN}${castIfNeeded(c.value)} ${c.operator} ${valueAsJsonb})`;
58
+ const valRep = Object.keys(replacements).find((key) => replacements[key] === c.value);
59
+ return `(custom_fields->> :${replacemetKey} )${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
69
60
  }).join(AND_DELIMETER);
70
61
  }
71
- if (typeof condition === 'string' || typeof condition === 'number') {
72
- const conditionRep = (0, helpers_1.generateRandomString)();
73
- replacements[conditionRep] = `${condition}`;
74
- const valueAsJsonb = castValueToJsonb(`:${conditionRep}`);
75
- return `(${columnCondition} AND ${CV_VALUE_COLUMN}${castIfNeeded(condition)} = ${valueAsJsonb})`;
62
+ if (typeof condition === 'string') {
63
+ const conditionRep = Object.keys(replacements).find((key) => replacements[key] === condition);
64
+ return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition)} = :${conditionRep}`;
76
65
  }
77
66
  if (condition?.operator) {
78
- const valueRep = (0, helpers_1.generateRandomString)();
79
- replacements[valueRep] = `${condition.value}`;
80
- const valueAsJsonb = castValueToJsonb(`:${valueRep}`);
81
- return `( ${columnCondition} AND ${CV_VALUE_COLUMN}${castIfNeeded(condition.value)} ${condition.operator} ${valueAsJsonb})`;
67
+ const valueRep = Object.keys(replacements).find((key) => replacements[key] === condition.value);
68
+ return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
82
69
  }
83
70
  return false;
84
71
  })
85
72
  .filter(Boolean);
86
73
  if (conditionsStrings.length === 0) {
87
- return {};
74
+ return {
75
+ replacements,
76
+ };
88
77
  }
89
- const customFieldConditions = conditionsStrings.join(OR_DELIMETER);
90
- const subQuery = `
91
- SELECT cv.model_id
92
- FROM custom_field_values AS cv
93
- INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id
94
- AND cd.model_type = :${ConditionNameRandomStr}
95
- WHERE ${customFieldConditions}
96
- GROUP BY cv.model_id
97
- HAVING COUNT(DISTINCT cv.custom_field_definition_id) = ${conditionsStrings.length}
98
- `.replace(/\n/g, '');
78
+ const customFieldConditions = conditionsStrings.join(AND_DELIMETER);
79
+ const subQuery = `${'SELECT model_id FROM ('
80
+ + 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
81
+ + 'FROM custom_field_values AS cv '
82
+ + 'INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id '
83
+ + `AND cd.model_type = '${name}'`
84
+ + 'GROUP BY cv.model_id'
85
+ + ') AS CustomFieldAggregation WHERE '} ${customFieldConditions}`;
99
86
  return {
100
87
  where: {
101
88
  id: {
@@ -107,15 +94,14 @@ const customFieldsFilterScope = (name) => (conditions) => {
107
94
  };
108
95
  exports.customFieldsFilterScope = customFieldsFilterScope;
109
96
  exports.scopeName = CUSTOM_FIELDS_FILTER_SCOPE;
110
- const customFieldsSortScope = (name) => (sort) => {
97
+ const customFieldsSortScope = (name) => ({ replacementsMap, scopeValue: sort }) => {
111
98
  if (!sort || sort.length === 0) {
112
99
  return {};
113
100
  }
114
101
  const randomStr = (0, helpers_1.generateRandomString)();
115
- const replacements = {};
116
102
  const includes = Object.entries(sort).map(([key]) => {
117
- const keyRandomReplacement = (0, helpers_1.generateRandomString)();
118
- replacements[keyRandomReplacement] = `${key}`;
103
+ const replacemetKey = Object.keys(replacementsMap).find((randomString) => replacementsMap[randomString] === key);
104
+ console.log('sort replacemetKey:', replacemetKey);
119
105
  return ([
120
106
  sequelize_typescript_1.Sequelize.literal(`(
121
107
  SELECT value
@@ -124,7 +110,7 @@ const customFieldsSortScope = (name) => (sort) => {
124
110
  ON cv.custom_field_definition_id = cd.id
125
111
  AND cd.model_type = '${name}'
126
112
  WHERE cv.model_id = "${name}"."id"
127
- AND cd.name = :${keyRandomReplacement}
113
+ AND cd.name = :${replacemetKey}
128
114
  ) AS CustomFieldAggregation
129
115
  )
130
116
  `), randomStr,
@@ -136,7 +122,7 @@ const customFieldsSortScope = (name) => (sort) => {
136
122
  include: includes,
137
123
  },
138
124
  order: orders,
139
- replacements,
125
+ replacements: replacementsMap,
140
126
  };
141
127
  };
142
128
  exports.customFieldsSortScope = customFieldsSortScope;
@@ -10,7 +10,10 @@ declare const _default: {
10
10
  underscored: boolean;
11
11
  underscoredAll: boolean;
12
12
  };
13
- logging: boolean;
13
+ logging: {
14
+ (...data: any[]): void;
15
+ (message?: any, ...optionalParams: any[]): void;
16
+ };
14
17
  };
15
18
  };
16
19
  export default _default;
@@ -12,6 +12,6 @@ exports.default = {
12
12
  underscored: true,
13
13
  underscoredAll: true,
14
14
  },
15
- logging: false,
15
+ logging: console.log,
16
16
  },
17
17
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/sadot",
3
- "version": "0.6.11",
3
+ "version": "0.7.0-beta.1",
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/* ../$REPO/node_modules/$npm_package_name/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"
@@ -59,9 +59,6 @@
59
59
  "typescript": "^5.3.3",
60
60
  "typescript-eslint": "^0.0.1-alpha.0"
61
61
  },
62
- "engines": {
63
- "node": ">=16.0.0"
64
- },
65
62
  "author": "Autofleet",
66
63
  "license": "ISC"
67
64
  }
@@ -55,11 +55,8 @@ export const updateValues = async (
55
55
  options: FindOptions & { modelOptions?: ModelOptions } = {},
56
56
  ): Promise<CustomFieldValue[]> => {
57
57
  const names = Object.keys(valuesToUpdate);
58
- logger.debug(`custom-fields: updating values for ${modelType} ${modelId}`, {
59
- names,
60
- optionsKeys: options ? Object.keys(options) : null,
61
- valuesToUpdate,
62
- identifiers,
58
+ logger.info(`custom-fields: updating values for ${modelType} ${modelId}`, {
59
+ names, options, valuesToUpdate, identifiers,
63
60
  });
64
61
  const { modelOptions, transaction } = options;
65
62
 
@@ -75,7 +72,7 @@ export const updateValues = async (
75
72
 
76
73
  const disabledDefinitions = fieldDefinitions.filter((def) => def.disabled);
77
74
  if (fieldDefinitions.length !== names.length) {
78
- logger.warn(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
75
+ logger.info(`custom-fields: missing definitions for ${modelType} ${modelId}`, { names, fieldDefinitions });
79
76
  const missingDefinitions = names.filter((name) => !fieldDefinitions.some((def) => def.name === name));
80
77
  throw new MissingDefinitionError(missingDefinitions);
81
78
  }
@@ -37,13 +37,6 @@ const castIfNeeded = (conditionValue: string): string => {
37
37
  return '';
38
38
  };
39
39
  const AND_DELIMETER = ' AND ';
40
- const OR_DELIMETER = ' OR ';
41
-
42
- const CD_TABLE_ALIAS = 'cd';
43
- const CD_NAME_COLUMN = `${CD_TABLE_ALIAS}.name`;
44
- const CV_TABLE_ALIAS = 'cv';
45
- const CV_VALUE_COLUMN = `(${CV_TABLE_ALIAS}.value)`;
46
- const castValueToJsonb = (value) => `to_jsonb(${value}::text)`;
47
40
 
48
41
  /**
49
42
  * A Sequelize scope for filtering models by custom fields.
@@ -54,21 +47,20 @@ const castValueToJsonb = (value) => `to_jsonb(${value}::text)`;
54
47
  */
55
48
  export const customFieldsFilterScope = (
56
49
  name: string,
57
- ) => (conditions: Record<string, ConditionValue>): CustomFieldFilterOptions => {
50
+ ) => ({ replacementsMap, scopeValue }): CustomFieldFilterOptions => {
51
+ const conditions: Record<string, ConditionValue> = scopeValue;
52
+ const replacements: Record<string, string> = replacementsMap;
53
+ console.log('filter replacementsMap:', replacements);
58
54
  if (!conditions || Object.keys(conditions).length === 0) {
59
55
  return {};
60
56
  }
61
- const ConditionNameRandomStr = generateRandomString();
62
- const replacements: Record<string, string> = {};
63
- replacements[ConditionNameRandomStr] = `${name}`;
64
-
65
57
  // Build the WHERE clause for custom field filtering
66
58
  const conditionsStrings = Object.entries(conditions)
67
59
  .map(
68
60
  ([key, condition]) => {
69
- const replacemetKey = generateRandomString();
70
- replacements[replacemetKey] = `${key}`;
71
- const columnCondition = `(${CD_NAME_COLUMN} = :${replacemetKey})`;
61
+ const replacemetKey = Object.keys(replacements).find((randomString) => replacements[randomString] === key);
62
+ if (!replacemetKey) return false;
63
+
72
64
  if (Array.isArray(condition)) {
73
65
  if (condition.length === 0) {
74
66
  // if empty array, the condition is ignored
@@ -76,50 +68,42 @@ export const customFieldsFilterScope = (
76
68
  }
77
69
  if (typeof condition[0] === 'string') {
78
70
  const values = condition.map((v) => {
79
- const valRandom = generateRandomString();
80
- replacements[`${valRandom}`] = `${v}`;
81
- return castValueToJsonb(`:${valRandom}`);
71
+ const valRandom = Object.keys(replacements).find((randomString) => replacements[randomString] === v);
72
+ return ` :${valRandom} `;
82
73
  }).join(',');
83
- return `(${columnCondition} AND ${CV_VALUE_COLUMN} IN ( ${values} ))`;
74
+ return `(custom_fields->> :${replacemetKey} ) IN ( ${values} )`;
84
75
  }
85
76
  return condition
86
77
  .map((c) => {
87
- const valRep = generateRandomString();
88
- replacements[valRep] = `${c.value}`;
89
- const valueAsJsonb = castValueToJsonb(`:${valRep}`);
90
- return `(${columnCondition} AND ${CV_VALUE_COLUMN}${castIfNeeded(c.value)} ${c.operator} ${valueAsJsonb})`;
78
+ const valRep = Object.keys(replacements).find((key) => replacements[key] === c.value);
79
+ return `(custom_fields->> :${replacemetKey} )${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
91
80
  }).join(AND_DELIMETER);
92
81
  }
93
- if (typeof condition === 'string' || typeof condition === 'number') {
94
- const conditionRep = generateRandomString();
95
- replacements[conditionRep] = `${condition}`;
96
- const valueAsJsonb = castValueToJsonb(`:${conditionRep}`);
97
- return `(${columnCondition} AND ${CV_VALUE_COLUMN}${castIfNeeded(condition)} = ${valueAsJsonb})`;
82
+ if (typeof condition === 'string') {
83
+ const conditionRep = Object.keys(replacements).find((key) => replacements[key] === condition);
84
+ return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition)} = :${conditionRep}`;
98
85
  }
99
86
  if (condition?.operator) {
100
- const valueRep = generateRandomString();
101
- replacements[valueRep] = `${condition.value}`;
102
- const valueAsJsonb = castValueToJsonb(`:${valueRep}`);
103
- return `( ${columnCondition} AND ${CV_VALUE_COLUMN}${castIfNeeded(condition.value)} ${condition.operator} ${valueAsJsonb})`;
87
+ const valueRep = Object.keys(replacements).find((key) => replacements[key] === condition.value);
88
+ return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
104
89
  }
105
-
106
90
  return false;
107
91
  },
108
92
  )
109
93
  .filter(Boolean);
110
94
  if (conditionsStrings.length === 0) {
111
- return {};
95
+ return {
96
+ replacements,
97
+ };
112
98
  }
113
- const customFieldConditions = conditionsStrings.join(OR_DELIMETER);
114
- const subQuery = `
115
- SELECT cv.model_id
116
- FROM custom_field_values AS cv
117
- INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id
118
- AND cd.model_type = :${ConditionNameRandomStr}
119
- WHERE ${customFieldConditions}
120
- GROUP BY cv.model_id
121
- HAVING COUNT(DISTINCT cv.custom_field_definition_id) = ${conditionsStrings.length}
122
- `.replace(/\n/g, '');
99
+ const customFieldConditions = conditionsStrings.join(AND_DELIMETER);
100
+ const subQuery = `${'SELECT model_id FROM ('
101
+ + 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
102
+ + 'FROM custom_field_values AS cv '
103
+ + 'INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id '
104
+ + `AND cd.model_type = '${name}'`
105
+ + 'GROUP BY cv.model_id'
106
+ + ') AS CustomFieldAggregation WHERE '} ${customFieldConditions}`;
123
107
  return {
124
108
  where: {
125
109
  id: {
@@ -134,15 +118,14 @@ export const scopeName = CUSTOM_FIELDS_FILTER_SCOPE;
134
118
 
135
119
  export const customFieldsSortScope = (
136
120
  name: string,
137
- ) => (sort: CustomFieldSort[]) => {
121
+ ) => ({ replacementsMap, scopeValue: sort }) => {
138
122
  if (!sort || sort.length === 0) {
139
123
  return {};
140
124
  }
141
125
  const randomStr = generateRandomString();
142
- const replacements: Record<string, string> = {};
143
126
  const includes = Object.entries(sort).map(([key]) => {
144
- const keyRandomReplacement = generateRandomString();
145
- replacements[keyRandomReplacement] = `${key}`;
127
+ const replacemetKey = Object.keys(replacementsMap).find((randomString) => replacementsMap[randomString] === key);
128
+ console.log('sort replacemetKey:', replacemetKey);
146
129
  return ([
147
130
  Sequelize.literal(`(
148
131
  SELECT value
@@ -151,7 +134,7 @@ export const customFieldsSortScope = (
151
134
  ON cv.custom_field_definition_id = cd.id
152
135
  AND cd.model_type = '${name}'
153
136
  WHERE cv.model_id = "${name}"."id"
154
- AND cd.name = :${keyRandomReplacement}
137
+ AND cd.name = :${replacemetKey}
155
138
  ) AS CustomFieldAggregation
156
139
  )
157
140
  `), randomStr,
@@ -164,6 +147,6 @@ export const customFieldsSortScope = (
164
147
  include: includes,
165
148
  },
166
149
  order: orders,
167
- replacements,
150
+ replacements: replacementsMap,
168
151
  };
169
152
  };
@@ -10,6 +10,6 @@ export default {
10
10
  underscored: true,
11
11
  underscoredAll: true,
12
12
  },
13
- logging: false,
13
+ logging: console.log,
14
14
  },
15
15
  };