@autofleet/sadot 0.7.0-beta.1 → 0.7.0-beta.2.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,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, scopeValue }: {
28
- replacementsMap: any;
29
- scopeValue: any;
27
+ export declare const customFieldsFilterScope: (name: string) => ({ replacementsMap: replacements, scopeValue: conditions, }: {
28
+ replacementsMap: Record<string, string>;
29
+ scopeValue: Record<string, ConditionValue>;
30
30
  }) => CustomFieldFilterOptions;
31
31
  export declare const scopeName = "filterByCustomFields";
32
32
  export declare const customFieldsSortScope: (name: string) => ({ replacementsMap, scopeValue: sort }: {
33
- replacementsMap: any;
34
- scopeValue: any;
33
+ replacementsMap: Record<string, string>;
34
+ scopeValue: string[];
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: any;
44
+ replacements: Record<string, string>;
45
45
  };
46
46
  export {};
@@ -21,6 +21,42 @@ 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
+ };
24
60
  /**
25
61
  * A Sequelize scope for filtering models by custom fields.
26
62
  * This scope builds a WHERE clause to be applied on the main query.
@@ -28,52 +64,16 @@ const AND_DELIMETER = ' AND ';
28
64
  * @param name - The model type name used to join custom_field_definitions.
29
65
  * @returns A function that takes conditions and returns the Sequelize options object.
30
66
  */
31
- const customFieldsFilterScope = (name) => ({ replacementsMap, scopeValue }) => {
32
- const conditions = scopeValue;
33
- const replacements = replacementsMap;
34
- console.log('filter replacementsMap:', replacements);
67
+ const customFieldsFilterScope = (name) => ({ replacementsMap: replacements, scopeValue: conditions, }) => {
35
68
  if (!conditions || Object.keys(conditions).length === 0) {
36
69
  return {};
37
70
  }
38
71
  // Build the WHERE clause for custom field filtering
39
72
  const conditionsStrings = Object.entries(conditions)
40
- .map(([key, condition]) => {
41
- const replacemetKey = Object.keys(replacements).find((randomString) => replacements[randomString] === key);
42
- if (!replacemetKey)
43
- return false;
44
- if (Array.isArray(condition)) {
45
- if (condition.length === 0) {
46
- // if empty array, the condition is ignored
47
- return false;
48
- }
49
- if (typeof condition[0] === 'string') {
50
- const values = condition.map((v) => {
51
- const valRandom = Object.keys(replacements).find((randomString) => replacements[randomString] === v);
52
- return ` :${valRandom} `;
53
- }).join(',');
54
- return `(custom_fields->> :${replacemetKey} ) IN ( ${values} )`;
55
- }
56
- return condition
57
- .map((c) => {
58
- const valRep = Object.keys(replacements).find((key) => replacements[key] === c.value);
59
- return `(custom_fields->> :${replacemetKey} )${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
60
- }).join(AND_DELIMETER);
61
- }
62
- if (typeof condition === 'string') {
63
- const conditionRep = Object.keys(replacements).find((key) => replacements[key] === condition);
64
- return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition)} = :${conditionRep}`;
65
- }
66
- if (condition?.operator) {
67
- const valueRep = Object.keys(replacements).find((key) => replacements[key] === condition.value);
68
- return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
69
- }
70
- return false;
71
- })
73
+ .map(([key, condition]) => buildConditionString(key, condition, replacements))
72
74
  .filter(Boolean);
73
75
  if (conditionsStrings.length === 0) {
74
- return {
75
- replacements,
76
- };
76
+ return { replacements };
77
77
  }
78
78
  const customFieldConditions = conditionsStrings.join(AND_DELIMETER);
79
79
  const subQuery = `${'SELECT model_id FROM ('
@@ -100,21 +100,21 @@ const customFieldsSortScope = (name) => ({ replacementsMap, scopeValue: sort })
100
100
  }
101
101
  const randomStr = (0, helpers_1.generateRandomString)();
102
102
  const includes = Object.entries(sort).map(([key]) => {
103
- const replacemetKey = Object.keys(replacementsMap).find((randomString) => replacementsMap[randomString] === key);
104
- console.log('sort replacemetKey:', replacemetKey);
105
- return ([
103
+ const replacementKey = Object.keys(replacementsMap)
104
+ .find((randomString) => replacementsMap[randomString] === key);
105
+ return [
106
106
  sequelize_typescript_1.Sequelize.literal(`(
107
- SELECT value
108
- FROM (SELECT cv.model_id, cv.value
109
- FROM custom_field_values AS cv INNER JOIN custom_field_definitions AS cd
110
- ON cv.custom_field_definition_id = cd.id
111
- AND cd.model_type = '${name}'
112
- WHERE cv.model_id = "${name}"."id"
113
- AND cd.name = :${replacemetKey}
114
- ) AS CustomFieldAggregation
115
- )
116
- `), randomStr,
117
- ]);
107
+ SELECT value
108
+ FROM (
109
+ SELECT cv.model_id, cv.value
110
+ FROM custom_field_values AS cv
111
+ INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id
112
+ AND cd.model_type = '${name}'
113
+ WHERE cv.model_id = "${name}"."id" AND cd.name = :${replacementKey}
114
+ ) AS CustomFieldAggregation
115
+ )`),
116
+ randomStr,
117
+ ];
118
118
  });
119
119
  const orders = Object.entries(sort).map(([, value]) => sequelize_typescript_1.Sequelize.literal(`"${randomStr}" ${value}`));
120
120
  return {
@@ -10,10 +10,7 @@ declare const _default: {
10
10
  underscored: boolean;
11
11
  underscoredAll: boolean;
12
12
  };
13
- logging: {
14
- (...data: any[]): void;
15
- (message?: any, ...optionalParams: any[]): void;
16
- };
13
+ logging: boolean;
17
14
  };
18
15
  };
19
16
  export default _default;
@@ -12,6 +12,6 @@ exports.default = {
12
12
  underscored: true,
13
13
  underscoredAll: true,
14
14
  },
15
- logging: console.log,
15
+ logging: false,
16
16
  },
17
17
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/sadot",
3
- "version": "0.7.0-beta.1",
3
+ "version": "0.7.0-beta.2.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/* ../task-ms/node_modules/@autofleet/sadot/dist",
12
+ "build-to-local-repo": "npm run build && cp -r dist/* ../$REPO/node_modules/$npm_package_name/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,6 +59,12 @@
59
59
  "typescript": "^5.3.3",
60
60
  "typescript-eslint": "^0.0.1-alpha.0"
61
61
  },
62
+ "peerDependencies": {
63
+ "@autofleet/sheilta": ">=1.4.0-beta.2"
64
+ },
65
+ "engines": {
66
+ "node": ">=16.0.0"
67
+ },
62
68
  "author": "Autofleet",
63
69
  "license": "ISC"
64
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
  }
@@ -31,12 +31,55 @@ export type CustomFieldFilterOptions = {
31
31
  const castIfNeeded = (conditionValue: string): string => {
32
32
  if (moment.isDate(conditionValue)) {
33
33
  return '::timestamp';
34
- } if (!Number.isNaN(Number(conditionValue))) {
34
+ }
35
+ if (!Number.isNaN(Number(conditionValue))) {
35
36
  return '::numeric';
36
37
  }
37
38
  return '';
38
39
  };
39
40
  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
+ };
40
83
 
41
84
  /**
42
85
  * A Sequelize scope for filtering models by custom fields.
@@ -47,54 +90,22 @@ const AND_DELIMETER = ' AND ';
47
90
  */
48
91
  export const customFieldsFilterScope = (
49
92
  name: string,
50
- ) => ({ replacementsMap, scopeValue }): CustomFieldFilterOptions => {
51
- const conditions: Record<string, ConditionValue> = scopeValue;
52
- const replacements: Record<string, string> = replacementsMap;
53
- console.log('filter replacementsMap:', replacements);
93
+ ) => (
94
+ {
95
+ replacementsMap: replacements,
96
+ scopeValue: conditions,
97
+ }: { replacementsMap: Record<string, string>, scopeValue: Record<string, ConditionValue> },
98
+ ): CustomFieldFilterOptions => {
54
99
  if (!conditions || Object.keys(conditions).length === 0) {
55
100
  return {};
56
101
  }
102
+
57
103
  // Build the WHERE clause for custom field filtering
58
104
  const conditionsStrings = Object.entries(conditions)
59
- .map(
60
- ([key, condition]) => {
61
- const replacemetKey = Object.keys(replacements).find((randomString) => replacements[randomString] === key);
62
- if (!replacemetKey) return false;
63
-
64
- if (Array.isArray(condition)) {
65
- if (condition.length === 0) {
66
- // if empty array, the condition is ignored
67
- return false;
68
- }
69
- if (typeof condition[0] === 'string') {
70
- const values = condition.map((v) => {
71
- const valRandom = Object.keys(replacements).find((randomString) => replacements[randomString] === v);
72
- return ` :${valRandom} `;
73
- }).join(',');
74
- return `(custom_fields->> :${replacemetKey} ) IN ( ${values} )`;
75
- }
76
- return condition
77
- .map((c) => {
78
- const valRep = Object.keys(replacements).find((key) => replacements[key] === c.value);
79
- return `(custom_fields->> :${replacemetKey} )${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
80
- }).join(AND_DELIMETER);
81
- }
82
- if (typeof condition === 'string') {
83
- const conditionRep = Object.keys(replacements).find((key) => replacements[key] === condition);
84
- return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition)} = :${conditionRep}`;
85
- }
86
- if (condition?.operator) {
87
- const valueRep = Object.keys(replacements).find((key) => replacements[key] === condition.value);
88
- return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
89
- }
90
- return false;
91
- },
92
- )
93
- .filter(Boolean);
105
+ .map(([key, condition]) => buildConditionString(key, condition, replacements))
106
+ .filter(Boolean) as string[];
94
107
  if (conditionsStrings.length === 0) {
95
- return {
96
- replacements,
97
- };
108
+ return { replacements };
98
109
  }
99
110
  const customFieldConditions = conditionsStrings.join(AND_DELIMETER);
100
111
  const subQuery = `${'SELECT model_id FROM ('
@@ -118,27 +129,28 @@ export const scopeName = CUSTOM_FIELDS_FILTER_SCOPE;
118
129
 
119
130
  export const customFieldsSortScope = (
120
131
  name: string,
121
- ) => ({ replacementsMap, scopeValue: sort }) => {
132
+ ) => ({ replacementsMap, scopeValue: sort }: { replacementsMap: Record<string, string>, scopeValue: string[] }) => {
122
133
  if (!sort || sort.length === 0) {
123
134
  return {};
124
135
  }
136
+
125
137
  const randomStr = generateRandomString();
126
138
  const includes = Object.entries(sort).map(([key]) => {
127
- const replacemetKey = Object.keys(replacementsMap).find((randomString) => replacementsMap[randomString] === key);
128
- console.log('sort replacemetKey:', replacemetKey);
129
- return ([
139
+ const replacementKey = Object.keys(replacementsMap)
140
+ .find((randomString) => replacementsMap[randomString] === key);
141
+ return [
130
142
  Sequelize.literal(`(
131
- SELECT value
132
- FROM (SELECT cv.model_id, cv.value
133
- FROM custom_field_values AS cv INNER JOIN custom_field_definitions AS cd
134
- ON cv.custom_field_definition_id = cd.id
135
- AND cd.model_type = '${name}'
136
- WHERE cv.model_id = "${name}"."id"
137
- AND cd.name = :${replacemetKey}
138
- ) AS CustomFieldAggregation
139
- )
140
- `), randomStr,
141
- ]);
143
+ SELECT value
144
+ FROM (
145
+ SELECT cv.model_id, cv.value
146
+ FROM custom_field_values AS cv
147
+ INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id
148
+ AND cd.model_type = '${name}'
149
+ WHERE cv.model_id = "${name}"."id" AND cd.name = :${replacementKey}
150
+ ) AS CustomFieldAggregation
151
+ )`),
152
+ randomStr,
153
+ ];
142
154
  });
143
155
 
144
156
  const orders = Object.entries(sort).map(([, value]) => Sequelize.literal(`"${randomStr}" ${value}`));
@@ -10,6 +10,6 @@ export default {
10
10
  underscored: true,
11
11
  underscoredAll: true,
12
12
  },
13
- logging: console.log,
13
+ logging: false,
14
14
  },
15
15
  };