@autofleet/sadot 0.6.8-beta.1 → 0.6.8-beta.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.
@@ -17,6 +17,13 @@ export type CustomFieldFilterOptions = {
17
17
  where?: WhereOptions;
18
18
  replacements?: Record<string, string>;
19
19
  };
20
+ /**
21
+ * A Sequelize scope for filtering models by custom fields.
22
+ * This scope builds a WHERE clause to be applied on the main query.
23
+ *
24
+ * @param name - The model type name used to join custom_field_definitions.
25
+ * @returns A function that takes conditions and returns the Sequelize options object.
26
+ */
20
27
  export declare const customFieldsFilterScope: (name: string) => (conditions: Record<string, ConditionValue>) => CustomFieldFilterOptions;
21
28
  export declare const scopeName = "filterByCustomFields";
22
29
  export declare const customFieldsSortScope: (name: string) => (sort: CustomFieldSort[]) => {
@@ -5,14 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.customFieldsSortScope = exports.scopeName = exports.customFieldsFilterScope = void 0;
7
7
  /* eslint-disable import/prefer-default-export */
8
+ // eslint-disable-next-line import/no-extraneous-dependencies
9
+ const dayjs_1 = __importDefault(require("dayjs"));
8
10
  const sequelize_1 = require("sequelize");
9
11
  const sequelize_typescript_1 = require("sequelize-typescript");
10
12
  const common_types_1 = require("@autofleet/common-types");
11
- const moment_1 = __importDefault(require("moment"));
12
13
  const helpers_1 = require("../utils/helpers");
13
14
  const { CUSTOM_FIELDS_FILTER_SCOPE } = common_types_1.customFields;
14
15
  const castIfNeeded = (conditionValue) => {
15
- if (moment_1.default.isDate(conditionValue)) {
16
+ if ((0, dayjs_1.default)(conditionValue).isValid()) {
16
17
  return '::timestamp';
17
18
  }
18
19
  if (!Number.isNaN(Number(conditionValue))) {
@@ -21,18 +22,24 @@ const castIfNeeded = (conditionValue) => {
21
22
  return '';
22
23
  };
23
24
  const AND_DELIMETER = ' AND ';
25
+ /**
26
+ * A Sequelize scope for filtering models by custom fields.
27
+ * This scope builds a WHERE clause to be applied on the main query.
28
+ *
29
+ * @param name - The model type name used to join custom_field_definitions.
30
+ * @returns A function that takes conditions and returns the Sequelize options object.
31
+ */
24
32
  const customFieldsFilterScope = (name) => (conditions) => {
25
33
  if (!conditions || Object.keys(conditions).length === 0) {
26
34
  return {};
27
35
  }
28
- const randomStr = (0, helpers_1.generateRandomString)();
29
- const replacementMapPrefix = `customFieldsFilterScopeConditionMap_${randomStr}`;
36
+ const ConditionNameRandomStr = (0, helpers_1.generateRandomString)();
30
37
  const replacements = {};
31
- replacements[`customFieldsFilterScopeConditionName_${randomStr}`] = `${name}`;
38
+ replacements[ConditionNameRandomStr] = `${name}`;
32
39
  // Build the WHERE clause for custom field filtering
33
40
  const conditionsStrings = Object.entries(conditions)
34
- .map(([key, condition], index) => {
35
- const replacemetKey = `${replacementMapPrefix}Key${index}`;
41
+ .map(([key, condition]) => {
42
+ const replacemetKey = (0, helpers_1.generateRandomString)();
36
43
  replacements[replacemetKey] = `${key}`;
37
44
  if (Array.isArray(condition)) {
38
45
  if (condition.length === 0) {
@@ -48,19 +55,19 @@ const customFieldsFilterScope = (name) => (conditions) => {
48
55
  return `(custom_fields->> :${replacemetKey} ) IN ( ${values} )`;
49
56
  }
50
57
  return condition
51
- .map((c, cIndex) => {
52
- const valRep = `${replacementMapPrefix}Values${index}${cIndex}`;
58
+ .map((c) => {
59
+ const valRep = (0, helpers_1.generateRandomString)();
53
60
  replacements[valRep] = `${c.value}`;
54
61
  return `(custom_fields->> :${replacemetKey} )${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
55
62
  }).join(AND_DELIMETER);
56
63
  }
57
64
  if (typeof condition === 'string') {
58
- const conditionRep = `${replacementMapPrefix}Condition${index}`;
65
+ const conditionRep = (0, helpers_1.generateRandomString)();
59
66
  replacements[conditionRep] = `${condition}`;
60
67
  return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition)} = :${conditionRep}`;
61
68
  }
62
69
  if (condition?.operator) {
63
- const valueRep = `${replacementMapPrefix}Values${index}`;
70
+ const valueRep = (0, helpers_1.generateRandomString)();
64
71
  replacements[valueRep] = `${condition.value}`;
65
72
  return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
66
73
  }
@@ -75,7 +82,7 @@ const customFieldsFilterScope = (name) => (conditions) => {
75
82
  + 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
76
83
  + 'FROM custom_field_values AS cv '
77
84
  + 'INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id '
78
- + `AND cd.model_type = :${`customFieldsFilterScopeConditionName_${randomStr}`} `
85
+ + `AND cd.model_type = :${ConditionNameRandomStr} `
79
86
  + 'GROUP BY cv.model_id'
80
87
  + ') AS CustomFieldAggregation WHERE '} ${customFieldConditions}`;
81
88
  return {
@@ -95,10 +102,9 @@ const customFieldsSortScope = (name) => (sort) => {
95
102
  }
96
103
  const randomStr = (0, helpers_1.generateRandomString)();
97
104
  const replacements = {};
98
- const includes = Object.entries(sort).map(([key], index) => {
99
- const keyReplacement = `key_${randomStr}_${index}`;
100
- replacements[`keyCustomFields_${randomStr}_${index}`] = `customFields_${key}`;
101
- replacements[keyReplacement] = `${key}`;
105
+ const includes = Object.entries(sort).map(([key]) => {
106
+ const keyRandomReplacement = (0, helpers_1.generateRandomString)();
107
+ replacements[keyRandomReplacement] = `${key}`;
102
108
  return ([
103
109
  sequelize_typescript_1.Sequelize.literal(`(
104
110
  SELECT value
@@ -107,7 +113,7 @@ const customFieldsSortScope = (name) => (sort) => {
107
113
  ON cv.custom_field_definition_id = cd.id
108
114
  AND cd.model_type = '${name}'
109
115
  WHERE cv.model_id = "${name}"."id"
110
- AND cd.name = :${keyReplacement}
116
+ AND cd.name = :${keyRandomReplacement}
111
117
  ) AS CustomFieldAggregation
112
118
  )
113
119
  `), randomStr,
@@ -4,10 +4,11 @@ exports.generateCustomFieldSearchQueryPayload = exports.generateRandomString = v
4
4
  /* eslint-disable import/prefer-default-export */
5
5
  const sequelize_1 = require("sequelize");
6
6
  const sequelize_typescript_1 = require("sequelize-typescript");
7
+ const node_crypto_1 = require("node:crypto");
7
8
  const constants_1 = require("../constants");
8
9
  const generateRandomString = (length = 5) => {
9
- const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
10
- return Array.from({ length }, () => characters.charAt(Math.floor(Math.random() * characters.length))).join('');
10
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
11
+ return Array.from({ length }, () => characters.charAt((0, node_crypto_1.randomInt)(characters.length))).join('');
11
12
  };
12
13
  exports.generateRandomString = generateRandomString;
13
14
  const generateCustomFieldSearchQueryPayload = (searchTerm, model, entityId, customFieldsTypesToExclude = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/sadot",
3
- "version": "0.6.8-beta.1",
3
+ "version": "0.6.8-beta.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -34,9 +34,9 @@
34
34
  "@autofleet/errors": "^1.0.10",
35
35
  "@autofleet/events": "^2.0.0",
36
36
  "@autofleet/logger": "^2.0.5",
37
+ "dayjs": "^1.11.11",
37
38
  "express": "^4.18.2",
38
39
  "joi": "^17.7.0",
39
- "moment": "^2.30.1",
40
40
  "pg": "^8.10.0",
41
41
  "reflect-metadata": "^0.1.13",
42
42
  "sequelize": "^6.31.1",
@@ -1,8 +1,9 @@
1
1
  /* eslint-disable import/prefer-default-export */
2
+ // eslint-disable-next-line import/no-extraneous-dependencies
3
+ import dayjs from 'dayjs';
2
4
  import { Op, type WhereOptions } from 'sequelize';
3
5
  import { Sequelize } from 'sequelize-typescript';
4
6
  import { customFields } from '@autofleet/common-types';
5
- import moment from 'moment';
6
7
  import { generateRandomString } from '../utils/helpers';
7
8
 
8
9
  const { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;
@@ -29,31 +30,38 @@ export type CustomFieldFilterOptions = {
29
30
  }
30
31
 
31
32
  const castIfNeeded = (conditionValue: string): string => {
32
- if (moment.isDate(conditionValue)) {
33
+ if (dayjs(conditionValue).isValid()) {
33
34
  return '::timestamp';
34
35
  } if (!Number.isNaN(Number(conditionValue))) {
35
36
  return '::numeric';
36
37
  }
37
38
  return '';
38
39
  };
40
+
39
41
  const AND_DELIMETER = ' AND ';
40
42
 
43
+ /**
44
+ * A Sequelize scope for filtering models by custom fields.
45
+ * This scope builds a WHERE clause to be applied on the main query.
46
+ *
47
+ * @param name - The model type name used to join custom_field_definitions.
48
+ * @returns A function that takes conditions and returns the Sequelize options object.
49
+ */
41
50
  export const customFieldsFilterScope = (
42
51
  name: string,
43
52
  ) => (conditions: Record<string, ConditionValue>): CustomFieldFilterOptions => {
44
53
  if (!conditions || Object.keys(conditions).length === 0) {
45
54
  return {};
46
55
  }
47
- const randomStr = generateRandomString();
48
- const replacementMapPrefix = `customFieldsFilterScopeConditionMap_${randomStr}`;
56
+ const ConditionNameRandomStr = generateRandomString();
49
57
  const replacements: Record<string, string> = {};
50
- replacements[`customFieldsFilterScopeConditionName_${randomStr}`] = `${name}`;
58
+ replacements[ConditionNameRandomStr] = `${name}`;
51
59
 
52
60
  // Build the WHERE clause for custom field filtering
53
61
  const conditionsStrings = Object.entries(conditions)
54
62
  .map(
55
- ([key, condition], index) => {
56
- const replacemetKey = `${replacementMapPrefix}Key${index}`;
63
+ ([key, condition]) => {
64
+ const replacemetKey = generateRandomString();
57
65
  replacements[replacemetKey] = `${key}`;
58
66
  if (Array.isArray(condition)) {
59
67
  if (condition.length === 0) {
@@ -69,19 +77,19 @@ export const customFieldsFilterScope = (
69
77
  return `(custom_fields->> :${replacemetKey} ) IN ( ${values} )`;
70
78
  }
71
79
  return condition
72
- .map((c, cIndex) => {
73
- const valRep = `${replacementMapPrefix}Values${index}${cIndex}`;
80
+ .map((c) => {
81
+ const valRep = generateRandomString();
74
82
  replacements[valRep] = `${c.value}`;
75
83
  return `(custom_fields->> :${replacemetKey} )${castIfNeeded(c.value)} ${c.operator} :${valRep}`;
76
84
  }).join(AND_DELIMETER);
77
85
  }
78
86
  if (typeof condition === 'string') {
79
- const conditionRep = `${replacementMapPrefix}Condition${index}`;
87
+ const conditionRep = generateRandomString();
80
88
  replacements[conditionRep] = `${condition}`;
81
89
  return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition)} = :${conditionRep}`;
82
90
  }
83
91
  if (condition?.operator) {
84
- const valueRep = `${replacementMapPrefix}Values${index}`;
92
+ const valueRep = generateRandomString();
85
93
  replacements[valueRep] = `${condition.value}`;
86
94
  return `(custom_fields->> :${replacemetKey} ) ${castIfNeeded(condition.value)} ${condition.operator} :${valueRep}`;
87
95
  }
@@ -98,7 +106,7 @@ export const customFieldsFilterScope = (
98
106
  + 'SELECT cv.model_id, jsonb_object_agg(cd.name, cv.value) AS custom_fields '
99
107
  + 'FROM custom_field_values AS cv '
100
108
  + 'INNER JOIN custom_field_definitions AS cd ON cv.custom_field_definition_id = cd.id '
101
- + `AND cd.model_type = :${`customFieldsFilterScopeConditionName_${randomStr}`} `
109
+ + `AND cd.model_type = :${ConditionNameRandomStr} `
102
110
  + 'GROUP BY cv.model_id'
103
111
  + ') AS CustomFieldAggregation WHERE '} ${customFieldConditions}`;
104
112
  return {
@@ -121,10 +129,9 @@ export const customFieldsSortScope = (
121
129
  }
122
130
  const randomStr = generateRandomString();
123
131
  const replacements: Record<string, string> = {};
124
- const includes = Object.entries(sort).map(([key], index) => {
125
- const keyReplacement = `key_${randomStr}_${index}`;
126
- replacements[`keyCustomFields_${randomStr}_${index}`] = `customFields_${key}`;
127
- replacements[keyReplacement] = `${key}`;
132
+ const includes = Object.entries(sort).map(([key]) => {
133
+ const keyRandomReplacement = generateRandomString();
134
+ replacements[keyRandomReplacement] = `${key}`;
128
135
  return ([
129
136
  Sequelize.literal(`(
130
137
  SELECT value
@@ -133,7 +140,7 @@ export const customFieldsSortScope = (
133
140
  ON cv.custom_field_definition_id = cd.id
134
141
  AND cd.model_type = '${name}'
135
142
  WHERE cv.model_id = "${name}"."id"
136
- AND cd.name = :${keyReplacement}
143
+ AND cd.name = :${keyRandomReplacement}
137
144
  ) AS CustomFieldAggregation
138
145
  )
139
146
  `), randomStr,
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable import/prefer-default-export */
2
2
  import { type WhereOptions, Op, type BindOrReplacements } from 'sequelize';
3
3
  import { type ModelStatic, Sequelize } from 'sequelize-typescript';
4
+ import { randomInt } from 'node:crypto';
4
5
  import { CustomFieldDefinitionType } from '../constants';
5
6
 
6
7
  /**
@@ -26,8 +27,8 @@ interface CustomFieldsSearchPayload {
26
27
  }
27
28
 
28
29
  export const generateRandomString = (length = 5): string => {
29
- const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
30
- return Array.from({ length }, () => characters.charAt(Math.floor(Math.random() * characters.length))).join('');
30
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
31
+ return Array.from({ length }, () => characters.charAt(randomInt(characters.length))).join('');
31
32
  };
32
33
 
33
34
  export const generateCustomFieldSearchQueryPayload = (