@autofleet/sadot 0.6.3 → 0.6.4

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.
@@ -4,7 +4,11 @@ import { WhereOptions } from 'sequelize';
4
4
  * Currently supporting strings and arrays of strings.
5
5
  * More types to be added (TBA).
6
6
  */
7
- export type ConditionValue = string | string[];
7
+ type ConditionWithOperator = {
8
+ operator: string;
9
+ value: string;
10
+ };
11
+ export type ConditionValue = ConditionWithOperator | ConditionWithOperator[] | string | string[];
8
12
  export type CustomFieldSort = {
9
13
  field: string;
10
14
  direction: 'ASC' | 'DESC';
@@ -30,3 +34,4 @@ export declare const customFieldsSortScope: (name: string) => (sort: CustomField
30
34
  };
31
35
  order: import("sequelize/types/utils").Literal[];
32
36
  };
37
+ export {};
@@ -6,6 +6,13 @@ const sequelize_1 = require("sequelize");
6
6
  const sequelize_typescript_1 = require("sequelize-typescript");
7
7
  const common_types_1 = require("@autofleet/common-types");
8
8
  const { CUSTOM_FIELDS_FILTER_SCOPE } = common_types_1.customFields;
9
+ const castIfNeeded = (conditionValue) => {
10
+ if (!Number.isNaN(Date.parse(conditionValue))) {
11
+ return '::timestamp';
12
+ }
13
+ return '';
14
+ };
15
+ const AND_DELIMETER = ' AND ';
9
16
  /**
10
17
  * A Sequelize scope for filtering models by custom fields.
11
18
  * This scope builds a WHERE clause to be applied on the main query.
@@ -19,30 +26,39 @@ const customFieldsFilterScope = (name) => (conditions) => {
19
26
  }
20
27
  // Build the WHERE clause for custom field filtering
21
28
  const conditionsStrings = Object.entries(conditions)
22
- .map(([key, value]) => {
23
- if (Array.isArray(value)) {
24
- if (value.length === 0) {
29
+ .map(([key, condition]) => {
30
+ if (Array.isArray(condition)) {
31
+ if (condition.length === 0) {
25
32
  // if empty array, the condition is ignored
26
33
  return false;
27
34
  }
28
- const values = value.map((v) => `'${v}'`).join(',');
29
- return `custom_fields->>'${key}' IN (${values})`;
35
+ if (typeof condition[0] === 'string') {
36
+ const values = condition.map((v) => `'${v}'`).join(',');
37
+ return `(custom_fields->>'${key}') IN (${values})`;
38
+ }
39
+ return condition
40
+ .map((c) => `(custom_fields->>'${key}')${castIfNeeded(c.value)} ${c.operator} '${c.value}'`).join(AND_DELIMETER);
41
+ }
42
+ if (typeof condition === 'string') {
43
+ return `(custom_fields->>'${key}')${castIfNeeded(condition)} = '${condition}'`;
44
+ }
45
+ if (condition?.operator) {
46
+ return `(custom_fields->>'${key}')${castIfNeeded(condition.value)} ${condition.operator} '${condition.value}'`;
30
47
  }
31
- return `custom_fields->>'${key}' = '${value}'`;
48
+ return false;
32
49
  })
33
50
  .filter(Boolean);
34
51
  if (conditionsStrings.length === 0) {
35
52
  return {};
36
53
  }
37
- const customFieldConditions = conditionsStrings.join(' AND ');
54
+ const customFieldConditions = conditionsStrings.join(AND_DELIMETER);
38
55
  const subQuery = `${'SELECT model_id FROM ('
39
56
  + 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
40
57
  + 'FROM custom_field_values AS cv '
41
58
  + 'INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id '
42
59
  + `AND cd.model_type = '${name}' `
43
60
  + 'GROUP BY cv.model_id'
44
- + ') AS CustomFieldAggregation '
45
- + 'WHERE '}${customFieldConditions}`;
61
+ + ') AS CustomFieldAggregation WHERE '}${customFieldConditions}`;
46
62
  return {
47
63
  where: {
48
64
  id: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/sadot",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -10,7 +10,11 @@ const { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;
10
10
  * Currently supporting strings and arrays of strings.
11
11
  * More types to be added (TBA).
12
12
  */
13
- export type ConditionValue = string | string[];
13
+ type ConditionWithOperator = {
14
+ operator: string;
15
+ value: string;
16
+ };
17
+ export type ConditionValue = ConditionWithOperator | ConditionWithOperator[] | string | string[];
14
18
 
15
19
  export type CustomFieldSort = {
16
20
  field: string;
@@ -21,6 +25,13 @@ export type CustomFieldFilterOptions = {
21
25
  where?: WhereOptions;
22
26
  }
23
27
 
28
+ const castIfNeeded = (conditionValue: string): string => {
29
+ if (!Number.isNaN(Date.parse(conditionValue))) {
30
+ return '::timestamp';
31
+ }
32
+ return '';
33
+ };
34
+ const AND_DELIMETER = ' AND ';
24
35
  /**
25
36
  * A Sequelize scope for filtering models by custom fields.
26
37
  * This scope builds a WHERE clause to be applied on the main query.
@@ -37,24 +48,33 @@ export const customFieldsFilterScope = (
37
48
  // Build the WHERE clause for custom field filtering
38
49
  const conditionsStrings = Object.entries(conditions)
39
50
  .map(
40
- ([key, value]) => {
41
- if (Array.isArray(value)) {
42
- if (value.length === 0) {
51
+ ([key, condition]) => {
52
+ if (Array.isArray(condition)) {
53
+ if (condition.length === 0) {
43
54
  // if empty array, the condition is ignored
44
55
  return false;
45
56
  }
46
- const values = value.map((v) => `'${v}'`).join(',');
47
- return `custom_fields->>'${key}' IN (${values})`;
57
+ if (typeof condition[0] === 'string') {
58
+ const values = condition.map((v) => `'${v}'`).join(',');
59
+ return `(custom_fields->>'${key}') IN (${values})`;
60
+ }
61
+ return condition
62
+ .map((c) => `(custom_fields->>'${key}')${castIfNeeded(c.value)} ${c.operator} '${c.value}'`).join(AND_DELIMETER);
48
63
  }
49
- return `custom_fields->>'${key}' = '${value}'`;
64
+ if (typeof condition === 'string') {
65
+ return `(custom_fields->>'${key}')${castIfNeeded(condition)} = '${condition}'`;
66
+ }
67
+ if (condition?.operator) {
68
+ return `(custom_fields->>'${key}')${castIfNeeded(condition.value)} ${condition.operator} '${condition.value}'`;
69
+ }
70
+ return false;
50
71
  },
51
72
  )
52
73
  .filter(Boolean);
53
-
54
74
  if (conditionsStrings.length === 0) {
55
75
  return {};
56
76
  }
57
- const customFieldConditions = conditionsStrings.join(' AND ');
77
+ const customFieldConditions = conditionsStrings.join(AND_DELIMETER);
58
78
 
59
79
  const subQuery = `${'SELECT model_id FROM ('
60
80
  + 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
@@ -62,8 +82,7 @@ export const customFieldsFilterScope = (
62
82
  + 'INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id '
63
83
  + `AND cd.model_type = '${name}' `
64
84
  + 'GROUP BY cv.model_id'
65
- + ') AS CustomFieldAggregation '
66
- + 'WHERE '}${customFieldConditions}`;
85
+ + ') AS CustomFieldAggregation WHERE '}${customFieldConditions}`;
67
86
 
68
87
  return {
69
88
  where: {