@teleporthq/teleport-plugin-next-data-source 0.43.63 → 0.43.64
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.
- package/__tests__/filter-groups.test.ts +174 -0
- package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
- package/dist/cjs/fetchers/airtable.js +1 -1
- package/dist/cjs/fetchers/airtable.js.map +1 -1
- package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/cjs/fetchers/clickhouse.js +1 -1
- package/dist/cjs/fetchers/clickhouse.js.map +1 -1
- package/dist/cjs/fetchers/csv-file.d.ts.map +1 -1
- package/dist/cjs/fetchers/csv-file.js +3 -3
- package/dist/cjs/fetchers/csv-file.js.map +1 -1
- package/dist/cjs/fetchers/firestore.d.ts.map +1 -1
- package/dist/cjs/fetchers/firestore.js +1 -1
- package/dist/cjs/fetchers/firestore.js.map +1 -1
- package/dist/cjs/fetchers/google-sheets.d.ts.map +1 -1
- package/dist/cjs/fetchers/google-sheets.js +1 -1
- package/dist/cjs/fetchers/google-sheets.js.map +1 -1
- package/dist/cjs/fetchers/javascript.d.ts.map +1 -1
- package/dist/cjs/fetchers/javascript.js +1 -1
- package/dist/cjs/fetchers/javascript.js.map +1 -1
- package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mariadb.js +1 -1
- package/dist/cjs/fetchers/mariadb.js.map +1 -1
- package/dist/cjs/fetchers/mongodb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mongodb.js +1 -1
- package/dist/cjs/fetchers/mongodb.js.map +1 -1
- package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
- package/dist/cjs/fetchers/mysql.js +1 -1
- package/dist/cjs/fetchers/mysql.js.map +1 -1
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +1 -1
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/redis.d.ts.map +1 -1
- package/dist/cjs/fetchers/redis.js +1 -1
- package/dist/cjs/fetchers/redis.js.map +1 -1
- package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
- package/dist/cjs/fetchers/redshift.js +2 -2
- package/dist/cjs/fetchers/redshift.js.map +1 -1
- package/dist/cjs/fetchers/rest-api.d.ts.map +1 -1
- package/dist/cjs/fetchers/rest-api.js +2 -2
- package/dist/cjs/fetchers/rest-api.js.map +1 -1
- package/dist/cjs/fetchers/supabase.d.ts.map +1 -1
- package/dist/cjs/fetchers/supabase.js +1 -1
- package/dist/cjs/fetchers/supabase.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +1 -1
- package/dist/cjs/fetchers/teleport.js.map +1 -1
- package/dist/cjs/fetchers/turso.d.ts.map +1 -1
- package/dist/cjs/fetchers/turso.js +1 -1
- package/dist/cjs/fetchers/turso.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +32 -0
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +116 -18
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/fetchers/airtable.d.ts.map +1 -1
- package/dist/esm/fetchers/airtable.js +2 -2
- package/dist/esm/fetchers/airtable.js.map +1 -1
- package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/esm/fetchers/clickhouse.js +2 -2
- package/dist/esm/fetchers/clickhouse.js.map +1 -1
- package/dist/esm/fetchers/csv-file.d.ts.map +1 -1
- package/dist/esm/fetchers/csv-file.js +4 -4
- package/dist/esm/fetchers/csv-file.js.map +1 -1
- package/dist/esm/fetchers/firestore.d.ts.map +1 -1
- package/dist/esm/fetchers/firestore.js +2 -2
- package/dist/esm/fetchers/firestore.js.map +1 -1
- package/dist/esm/fetchers/google-sheets.d.ts.map +1 -1
- package/dist/esm/fetchers/google-sheets.js +2 -2
- package/dist/esm/fetchers/google-sheets.js.map +1 -1
- package/dist/esm/fetchers/javascript.d.ts.map +1 -1
- package/dist/esm/fetchers/javascript.js +2 -2
- package/dist/esm/fetchers/javascript.js.map +1 -1
- package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
- package/dist/esm/fetchers/mariadb.js +2 -2
- package/dist/esm/fetchers/mariadb.js.map +1 -1
- package/dist/esm/fetchers/mongodb.d.ts.map +1 -1
- package/dist/esm/fetchers/mongodb.js +2 -2
- package/dist/esm/fetchers/mongodb.js.map +1 -1
- package/dist/esm/fetchers/mysql.d.ts.map +1 -1
- package/dist/esm/fetchers/mysql.js +2 -2
- package/dist/esm/fetchers/mysql.js.map +1 -1
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +2 -2
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/redis.d.ts.map +1 -1
- package/dist/esm/fetchers/redis.js +2 -2
- package/dist/esm/fetchers/redis.js.map +1 -1
- package/dist/esm/fetchers/redshift.d.ts.map +1 -1
- package/dist/esm/fetchers/redshift.js +3 -3
- package/dist/esm/fetchers/redshift.js.map +1 -1
- package/dist/esm/fetchers/rest-api.d.ts.map +1 -1
- package/dist/esm/fetchers/rest-api.js +3 -3
- package/dist/esm/fetchers/rest-api.js.map +1 -1
- package/dist/esm/fetchers/supabase.d.ts.map +1 -1
- package/dist/esm/fetchers/supabase.js +2 -2
- package/dist/esm/fetchers/supabase.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +2 -2
- package/dist/esm/fetchers/teleport.js.map +1 -1
- package/dist/esm/fetchers/turso.d.ts.map +1 -1
- package/dist/esm/fetchers/turso.js +2 -2
- package/dist/esm/fetchers/turso.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +32 -0
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +112 -17
- package/dist/esm/utils.js.map +1 -1
- package/package.json +3 -3
- package/src/fetchers/airtable.ts +35 -36
- package/src/fetchers/clickhouse.ts +26 -36
- package/src/fetchers/csv-file.ts +13 -21
- package/src/fetchers/firestore.ts +41 -35
- package/src/fetchers/google-sheets.ts +15 -23
- package/src/fetchers/javascript.ts +11 -21
- package/src/fetchers/mariadb.ts +33 -44
- package/src/fetchers/mongodb.ts +66 -81
- package/src/fetchers/mysql.ts +33 -45
- package/src/fetchers/postgresql.ts +52 -62
- package/src/fetchers/redis.ts +6 -5
- package/src/fetchers/redshift.ts +29 -41
- package/src/fetchers/rest-api.ts +11 -21
- package/src/fetchers/supabase.ts +104 -41
- package/src/fetchers/teleport.ts +53 -64
- package/src/fetchers/turso.ts +27 -39
- package/src/utils.ts +188 -31
package/src/fetchers/csv-file.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
generateDateFormatterCode,
|
|
3
3
|
generateSortFilterHelperCode,
|
|
4
4
|
generateSafeJSONParseCode,
|
|
5
|
+
generateFilterTreeHelpersCode,
|
|
5
6
|
} from '../utils'
|
|
6
7
|
import { generateHeaderDetectionCode } from './utils/header-detection'
|
|
7
8
|
|
|
@@ -86,17 +87,15 @@ const generateHandlerBody = (): string => {
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
if (filters) {
|
|
89
|
-
const
|
|
90
|
+
const filterTree = normalizeFilterTree(safeJSONParse(filters))
|
|
90
91
|
|
|
91
|
-
if (
|
|
92
|
-
filteredData = filteredData.filter((item) =>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const field = labelToIdMap[filter.source] || filter.source
|
|
92
|
+
if (filterTree) {
|
|
93
|
+
filteredData = filteredData.filter((item) =>
|
|
94
|
+
evaluateFilterTree(filterTree, (condition) => {
|
|
95
|
+
const field = labelToIdMap[condition.source] || condition.source
|
|
97
96
|
const value = getNestedValue(item, field)
|
|
98
|
-
const target =
|
|
99
|
-
const operand =
|
|
97
|
+
const target = condition.destination
|
|
98
|
+
const operand = condition.operand
|
|
100
99
|
|
|
101
100
|
if (Array.isArray(target)) {
|
|
102
101
|
if (operand === '!=') {
|
|
@@ -107,18 +106,7 @@ const generateHandlerBody = (): string => {
|
|
|
107
106
|
|
|
108
107
|
return compareValues(value, target, operand)
|
|
109
108
|
})
|
|
110
|
-
|
|
111
|
-
} else {
|
|
112
|
-
filteredData = filteredData.filter((item) => {
|
|
113
|
-
return Object.entries(parsedFilters).every(([key, value]) => {
|
|
114
|
-
const field = labelToIdMap[key] || key
|
|
115
|
-
const itemValue = getNestedValue(item, field)
|
|
116
|
-
if (Array.isArray(value)) {
|
|
117
|
-
return value.includes(itemValue)
|
|
118
|
-
}
|
|
119
|
-
return compareValues(itemValue, value, '=')
|
|
120
|
-
})
|
|
121
|
-
})
|
|
109
|
+
)
|
|
122
110
|
}
|
|
123
111
|
}
|
|
124
112
|
|
|
@@ -227,6 +215,8 @@ ${generateDateFormatterCode()}
|
|
|
227
215
|
|
|
228
216
|
${generateSortFilterHelperCode()}
|
|
229
217
|
|
|
218
|
+
${generateFilterTreeHelpersCode()}
|
|
219
|
+
|
|
230
220
|
${generateHeaderDetectionCode()}
|
|
231
221
|
|
|
232
222
|
function parseCSVLine(line) {
|
|
@@ -335,6 +325,8 @@ ${generateDateFormatterCode()}
|
|
|
335
325
|
|
|
336
326
|
${generateSortFilterHelperCode()}
|
|
337
327
|
|
|
328
|
+
${generateFilterTreeHelpersCode()}
|
|
329
|
+
|
|
338
330
|
export default async function handler(req, res) {${generateHandlerBody()}
|
|
339
331
|
}
|
|
340
332
|
`
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
replaceSecretReference,
|
|
3
3
|
generateDateFormatterCode,
|
|
4
4
|
generateSafeJSONParseCode,
|
|
5
|
+
generateFilterTreeHelpersCode,
|
|
5
6
|
} from '../utils'
|
|
6
7
|
|
|
7
8
|
export const validateFirestoreConfig = (
|
|
@@ -73,6 +74,8 @@ const getFirestore = () => {
|
|
|
73
74
|
|
|
74
75
|
${generateSafeJSONParseCode()}
|
|
75
76
|
|
|
77
|
+
${generateFilterTreeHelpersCode()}
|
|
78
|
+
|
|
76
79
|
${generateDateFormatterCode()}
|
|
77
80
|
|
|
78
81
|
export default async function handler(req, res) {
|
|
@@ -83,46 +86,49 @@ export default async function handler(req, res) {
|
|
|
83
86
|
let queryRef = firestore.collection('${tableName}')
|
|
84
87
|
|
|
85
88
|
if (filters) {
|
|
86
|
-
const
|
|
89
|
+
const filterTree = normalizeFilterTree(safeJSONParse(filters))
|
|
87
90
|
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
91
|
+
if (filterTree) {
|
|
92
|
+
// Chained .where() calls are ANDed, so an OR group is built with
|
|
93
|
+
// Firestore's composite Filter API instead.
|
|
94
|
+
const operatorMap = {
|
|
95
|
+
'=': '==',
|
|
96
|
+
'!=': '!=',
|
|
97
|
+
'>': '>',
|
|
98
|
+
'<': '<',
|
|
99
|
+
'>=': '>=',
|
|
100
|
+
'<=': '<=',
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const buildCondition = (condition) => {
|
|
104
|
+
const field = condition.source
|
|
105
|
+
const value = condition.destination
|
|
106
|
+
const operand = condition.operand
|
|
105
107
|
|
|
106
108
|
if (Array.isArray(value)) {
|
|
107
|
-
if (value.length === 0) return
|
|
108
|
-
|
|
109
|
-
queryRef = queryRef.where(field, 'not-in', value)
|
|
110
|
-
} else {
|
|
111
|
-
queryRef = queryRef.where(field, 'in', value)
|
|
112
|
-
}
|
|
113
|
-
} else {
|
|
114
|
-
const firestoreOp = operatorMap[operand] || '=='
|
|
115
|
-
queryRef = queryRef.where(field, firestoreOp, value)
|
|
109
|
+
if (value.length === 0) return null
|
|
110
|
+
return admin.firestore.Filter.where(field, operand === '!=' ? 'not-in' : 'in', value)
|
|
116
111
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
112
|
+
|
|
113
|
+
return admin.firestore.Filter.where(field, operatorMap[operand] || '==', value)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const buildNode = (node) => {
|
|
117
|
+
if (node.type === 'group') {
|
|
118
|
+
const parts = node.children.map(buildNode).filter(Boolean)
|
|
119
|
+
if (parts.length === 0) return null
|
|
120
|
+
if (parts.length === 1) return parts[0]
|
|
121
|
+
return node.operator === 'or'
|
|
122
|
+
? admin.firestore.Filter.or(...parts)
|
|
123
|
+
: admin.firestore.Filter.and(...parts)
|
|
124
124
|
}
|
|
125
|
-
|
|
125
|
+
return buildCondition(node)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const filterExpression = buildNode(filterTree)
|
|
129
|
+
if (filterExpression) {
|
|
130
|
+
queryRef = queryRef.where(filterExpression)
|
|
131
|
+
}
|
|
126
132
|
}
|
|
127
133
|
}
|
|
128
134
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
generateSortFilterHelperCode,
|
|
3
|
+
generateSafeJSONParseCode,
|
|
4
|
+
generateFilterTreeHelpersCode,
|
|
5
|
+
} from '../utils'
|
|
2
6
|
import { generateHeaderDetectionCode } from './utils/header-detection'
|
|
3
7
|
|
|
4
8
|
export const validateGoogleSheetsConfig = (
|
|
@@ -53,6 +57,8 @@ ${generateSafeJSONParseCode()}
|
|
|
53
57
|
|
|
54
58
|
${generateSortFilterHelperCode()}
|
|
55
59
|
|
|
60
|
+
${generateFilterTreeHelpersCode()}
|
|
61
|
+
|
|
56
62
|
${generateHeaderDetectionCode()}
|
|
57
63
|
|
|
58
64
|
export default async function handler(req, res) {
|
|
@@ -280,18 +286,16 @@ export default async function handler(req, res) {
|
|
|
280
286
|
}
|
|
281
287
|
|
|
282
288
|
if (filters) {
|
|
283
|
-
const
|
|
289
|
+
const filterTree = normalizeFilterTree(safeJSONParse(filters))
|
|
284
290
|
|
|
285
|
-
if (
|
|
286
|
-
filteredData = filteredData.filter((item) =>
|
|
287
|
-
|
|
288
|
-
if (!filter.source || filter.destination === undefined) return true
|
|
289
|
-
|
|
291
|
+
if (filterTree) {
|
|
292
|
+
filteredData = filteredData.filter((item) =>
|
|
293
|
+
evaluateFilterTree(filterTree, (condition) => {
|
|
290
294
|
// Map label to column ID
|
|
291
|
-
const field = labelToIdMap[
|
|
295
|
+
const field = labelToIdMap[condition.source] || condition.source
|
|
292
296
|
const value = getNestedValue(item, field)
|
|
293
|
-
const target =
|
|
294
|
-
const operand =
|
|
297
|
+
const target = condition.destination
|
|
298
|
+
const operand = condition.operand
|
|
295
299
|
|
|
296
300
|
if (Array.isArray(target)) {
|
|
297
301
|
if (operand === '!=') {
|
|
@@ -302,19 +306,7 @@ export default async function handler(req, res) {
|
|
|
302
306
|
|
|
303
307
|
return compareValues(value, target, operand)
|
|
304
308
|
})
|
|
305
|
-
|
|
306
|
-
} else {
|
|
307
|
-
filteredData = filteredData.filter((item) => {
|
|
308
|
-
return Object.entries(parsedFilters).every(([key, value]) => {
|
|
309
|
-
// Map label to column ID
|
|
310
|
-
const field = labelToIdMap[key] || key
|
|
311
|
-
const itemValue = getNestedValue(item, field)
|
|
312
|
-
if (Array.isArray(value)) {
|
|
313
|
-
return value.includes(itemValue)
|
|
314
|
-
}
|
|
315
|
-
return compareValues(itemValue, value, '=')
|
|
316
|
-
})
|
|
317
|
-
})
|
|
309
|
+
)
|
|
318
310
|
}
|
|
319
311
|
}
|
|
320
312
|
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
generateDateFormatterCode,
|
|
3
3
|
generateSortFilterHelperCode,
|
|
4
4
|
generateSafeJSONParseCode,
|
|
5
|
+
generateFilterTreeHelpersCode,
|
|
5
6
|
} from '../utils'
|
|
6
7
|
|
|
7
8
|
export const validateJavaScriptConfig = (
|
|
@@ -47,6 +48,8 @@ ${generateDateFormatterCode()}
|
|
|
47
48
|
|
|
48
49
|
${generateSortFilterHelperCode()}
|
|
49
50
|
|
|
51
|
+
${generateFilterTreeHelpersCode()}
|
|
52
|
+
|
|
50
53
|
export default async function handler(req, res) {
|
|
51
54
|
try {
|
|
52
55
|
const { limit, offset, page, perPage, query, queryColumns, sortBy, sortOrder, filters, sorts } = req.query
|
|
@@ -87,17 +90,14 @@ export default async function handler(req, res) {
|
|
|
87
90
|
|
|
88
91
|
if (filters) {
|
|
89
92
|
try {
|
|
90
|
-
const
|
|
93
|
+
const filterTree = normalizeFilterTree(safeJSONParse(filters))
|
|
91
94
|
|
|
92
|
-
if (
|
|
93
|
-
data = data.filter((item) =>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
const value = getNestedValue(item, field)
|
|
99
|
-
const target = filter.destination
|
|
100
|
-
const operand = filter.operand || '='
|
|
95
|
+
if (filterTree) {
|
|
96
|
+
data = data.filter((item) =>
|
|
97
|
+
evaluateFilterTree(filterTree, (condition) => {
|
|
98
|
+
const value = getNestedValue(item, condition.source)
|
|
99
|
+
const target = condition.destination
|
|
100
|
+
const operand = condition.operand
|
|
101
101
|
|
|
102
102
|
if (Array.isArray(target)) {
|
|
103
103
|
if (operand === '!=') {
|
|
@@ -108,17 +108,7 @@ export default async function handler(req, res) {
|
|
|
108
108
|
|
|
109
109
|
return compareValues(value, target, operand)
|
|
110
110
|
})
|
|
111
|
-
|
|
112
|
-
} else {
|
|
113
|
-
data = data.filter((item) => {
|
|
114
|
-
return Object.entries(parsedFilters).every(([key, value]) => {
|
|
115
|
-
const itemValue = getNestedValue(item, key)
|
|
116
|
-
if (Array.isArray(value)) {
|
|
117
|
-
return value.includes(itemValue)
|
|
118
|
-
}
|
|
119
|
-
return compareValues(itemValue, value, '=')
|
|
120
|
-
})
|
|
121
|
-
})
|
|
111
|
+
)
|
|
122
112
|
}
|
|
123
113
|
} catch (err) {
|
|
124
114
|
console.error('Error parsing filters:', err)
|
package/src/fetchers/mariadb.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
replaceSecretReference,
|
|
3
3
|
generateDateFormatterCode,
|
|
4
4
|
generateSafeJSONParseCode,
|
|
5
|
+
generateFilterTreeHelpersCode,
|
|
5
6
|
generateSearchEscapeHelpersCode,
|
|
6
7
|
} from '../utils'
|
|
7
8
|
|
|
@@ -27,58 +28,46 @@ export const generateMariaDBFetcher = (
|
|
|
27
28
|
|
|
28
29
|
${generateSafeJSONParseCode()}
|
|
29
30
|
|
|
31
|
+
${generateFilterTreeHelpersCode()}
|
|
32
|
+
|
|
30
33
|
${generateSearchEscapeHelpersCode()}
|
|
31
34
|
|
|
32
35
|
// Helper function to process filters and build conditions
|
|
33
36
|
const processFilters = (filters, conditions, queryParams) => {
|
|
34
37
|
if (!filters) return
|
|
35
38
|
|
|
36
|
-
const
|
|
39
|
+
const filterTree = normalizeFilterTree(safeJSONParse(filters))
|
|
40
|
+
if (!filterTree) return
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
64
|
-
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
65
|
-
conditions.push(\`\${field} \${sqlOperator} ?\`)
|
|
66
|
-
queryParams.push(value)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
|
-
} else {
|
|
71
|
-
Object.entries(parsedFilters).forEach(([key, value]) => {
|
|
72
|
-
if (Array.isArray(value)) {
|
|
73
|
-
const placeholders = value.map(() => '?').join(', ')
|
|
74
|
-
queryParams.push(...value)
|
|
75
|
-
conditions.push(\`\\\`\${key}\\\` IN (\${placeholders})\`)
|
|
76
|
-
} else {
|
|
77
|
-
conditions.push(\`\\\`\${key}\\\` = ?\`)
|
|
78
|
-
queryParams.push(value)
|
|
79
|
-
}
|
|
80
|
-
})
|
|
42
|
+
const buildCondition = (condition) => {
|
|
43
|
+
const field = \`\\\`\${condition.source}\\\`\`
|
|
44
|
+
const value = condition.destination
|
|
45
|
+
const operand = condition.operand
|
|
46
|
+
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
if (value.length === 0) return null
|
|
49
|
+
const placeholders = value.map(() => '?').join(', ')
|
|
50
|
+
queryParams.push(...value)
|
|
51
|
+
return operand === '!='
|
|
52
|
+
? \`\${field} NOT IN (\${placeholders})\`
|
|
53
|
+
: \`\${field} IN (\${placeholders})\`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (value === null) {
|
|
57
|
+
if (operand === '=') return \`\${field} IS NULL\`
|
|
58
|
+
if (operand === '!=') return \`\${field} IS NOT NULL\`
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Validate operator to prevent SQL injection
|
|
63
|
+
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
64
|
+
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
65
|
+
queryParams.push(value)
|
|
66
|
+
return \`\${field} \${sqlOperator} ?\`
|
|
81
67
|
}
|
|
68
|
+
|
|
69
|
+
const clause = buildFilterTreeClause(filterTree, buildCondition)
|
|
70
|
+
if (clause) conditions.push(clause)
|
|
82
71
|
}
|
|
83
72
|
|
|
84
73
|
${generateDateFormatterCode()}
|
package/src/fetchers/mongodb.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
replaceSecretReference,
|
|
3
3
|
generateDateFormatterCode,
|
|
4
4
|
generateSafeJSONParseCode,
|
|
5
|
+
generateFilterTreeHelpersCode,
|
|
5
6
|
} from '../utils'
|
|
6
7
|
|
|
7
8
|
export const validateMongoDBConfig = (
|
|
@@ -73,95 +74,79 @@ export const generateMongoDBFetcher = (
|
|
|
73
74
|
|
|
74
75
|
${generateSafeJSONParseCode()}
|
|
75
76
|
|
|
77
|
+
${generateFilterTreeHelpersCode()}
|
|
78
|
+
|
|
76
79
|
// Helper function to process filters
|
|
77
80
|
const processFilters = (filters, filter) => {
|
|
78
81
|
if (!filters) return
|
|
79
82
|
|
|
80
|
-
const
|
|
83
|
+
const filterTree = normalizeFilterTree(safeJSONParse(filters))
|
|
84
|
+
if (!filterTree) return
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const value = filterItem.destination
|
|
88
|
-
const operand = filterItem.operand || '='
|
|
89
|
-
|
|
90
|
-
// Handle _id specially
|
|
91
|
-
const processValue = (v) => {
|
|
92
|
-
if (field === '_id' && typeof v === 'string') {
|
|
93
|
-
try {
|
|
94
|
-
return new ObjectId(v)
|
|
95
|
-
} catch (e) {
|
|
96
|
-
return v
|
|
97
|
-
}
|
|
98
|
-
}
|
|
86
|
+
const processValue = (field, v) => {
|
|
87
|
+
if (field === '_id' && typeof v === 'string') {
|
|
88
|
+
try {
|
|
89
|
+
return new ObjectId(v)
|
|
90
|
+
} catch (e) {
|
|
99
91
|
return v
|
|
100
92
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const processedValues = value.map(processValue)
|
|
104
|
-
if (operand === '!=') {
|
|
105
|
-
filter[field] = { $nin: processedValues }
|
|
106
|
-
} else {
|
|
107
|
-
filter[field] = { $in: processedValues }
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
const processedValue = processValue(value)
|
|
111
|
-
|
|
112
|
-
// Handle null values
|
|
113
|
-
if (processedValue === null) {
|
|
114
|
-
if (operand === '=') {
|
|
115
|
-
filter[field] = null
|
|
116
|
-
} else if (operand === '!=') {
|
|
117
|
-
filter[field] = { $ne: null }
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
// Map operand to MongoDB operators
|
|
121
|
-
switch (operand) {
|
|
122
|
-
case '=':
|
|
123
|
-
filter[field] = processedValue
|
|
124
|
-
break
|
|
125
|
-
case '!=':
|
|
126
|
-
filter[field] = { $ne: processedValue }
|
|
127
|
-
break
|
|
128
|
-
case '>':
|
|
129
|
-
filter[field] = { $gt: processedValue }
|
|
130
|
-
break
|
|
131
|
-
case '>=':
|
|
132
|
-
filter[field] = { $gte: processedValue }
|
|
133
|
-
break
|
|
134
|
-
case '<':
|
|
135
|
-
filter[field] = { $lt: processedValue }
|
|
136
|
-
break
|
|
137
|
-
case '<=':
|
|
138
|
-
filter[field] = { $lte: processedValue }
|
|
139
|
-
break
|
|
140
|
-
default:
|
|
141
|
-
filter[field] = processedValue
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
})
|
|
146
|
-
} else {
|
|
147
|
-
Object.entries(parsedFilters).forEach(([key, value]) => {
|
|
148
|
-
if (key === '_id') {
|
|
149
|
-
if (Array.isArray(value)) {
|
|
150
|
-
filter[key] = {
|
|
151
|
-
$in: value.map((id) => (typeof id === 'string' ? new ObjectId(id) : id))
|
|
152
|
-
}
|
|
153
|
-
} else if (typeof value === 'string') {
|
|
154
|
-
filter[key] = new ObjectId(value)
|
|
155
|
-
} else {
|
|
156
|
-
filter[key] = value
|
|
157
|
-
}
|
|
158
|
-
} else if (Array.isArray(value)) {
|
|
159
|
-
filter[key] = { $in: value }
|
|
160
|
-
} else {
|
|
161
|
-
filter[key] = value
|
|
162
|
-
}
|
|
163
|
-
})
|
|
93
|
+
}
|
|
94
|
+
return v
|
|
164
95
|
}
|
|
96
|
+
|
|
97
|
+
const buildCondition = (condition) => {
|
|
98
|
+
const field = condition.source
|
|
99
|
+
const value = condition.destination
|
|
100
|
+
const operand = condition.operand
|
|
101
|
+
|
|
102
|
+
if (Array.isArray(value)) {
|
|
103
|
+
const processedValues = value.map((entry) => processValue(field, entry))
|
|
104
|
+
return operand === '!='
|
|
105
|
+
? { [field]: { $nin: processedValues } }
|
|
106
|
+
: { [field]: { $in: processedValues } }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const processedValue = processValue(field, value)
|
|
110
|
+
|
|
111
|
+
if (processedValue === null) {
|
|
112
|
+
if (operand === '=') return { [field]: null }
|
|
113
|
+
if (operand === '!=') return { [field]: { $ne: null } }
|
|
114
|
+
return null
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
switch (operand) {
|
|
118
|
+
case '!=':
|
|
119
|
+
return { [field]: { $ne: processedValue } }
|
|
120
|
+
case '>':
|
|
121
|
+
return { [field]: { $gt: processedValue } }
|
|
122
|
+
case '>=':
|
|
123
|
+
return { [field]: { $gte: processedValue } }
|
|
124
|
+
case '<':
|
|
125
|
+
return { [field]: { $lt: processedValue } }
|
|
126
|
+
case '<=':
|
|
127
|
+
return { [field]: { $lte: processedValue } }
|
|
128
|
+
default:
|
|
129
|
+
return { [field]: processedValue }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const buildNode = (node) => {
|
|
134
|
+
if (node.type === 'group') {
|
|
135
|
+
const parts = node.children.map(buildNode).filter(Boolean)
|
|
136
|
+
if (parts.length === 0) return null
|
|
137
|
+
if (parts.length === 1) return parts[0]
|
|
138
|
+
return node.operator === 'or' ? { $or: parts } : { $and: parts }
|
|
139
|
+
}
|
|
140
|
+
return buildCondition(node)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const query = buildNode(filterTree)
|
|
144
|
+
if (!query) return
|
|
145
|
+
|
|
146
|
+
// Merged under $and rather than assigned onto filter directly: the search
|
|
147
|
+
// above may already own filter.$or, and two conditions on the same field
|
|
148
|
+
// would otherwise overwrite each other.
|
|
149
|
+
filter.$and = (filter.$and || []).concat([query])
|
|
165
150
|
}
|
|
166
151
|
|
|
167
152
|
${generateDateFormatterCode()}
|
package/src/fetchers/mysql.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
replaceSecretReference,
|
|
3
3
|
generateDateFormatterCode,
|
|
4
4
|
generateSafeJSONParseCode,
|
|
5
|
+
generateFilterTreeHelpersCode,
|
|
5
6
|
generateSearchEscapeHelpersCode,
|
|
6
7
|
} from '../utils'
|
|
7
8
|
|
|
@@ -64,59 +65,46 @@ const getConnection = () => {
|
|
|
64
65
|
|
|
65
66
|
${generateSafeJSONParseCode()}
|
|
66
67
|
|
|
68
|
+
${generateFilterTreeHelpersCode()}
|
|
69
|
+
|
|
67
70
|
${generateSearchEscapeHelpersCode()}
|
|
68
71
|
|
|
69
72
|
// Helper function to process filters and build conditions
|
|
70
73
|
const processFilters = (filters, conditions, queryParams) => {
|
|
71
74
|
if (!filters) return
|
|
72
75
|
|
|
73
|
-
const
|
|
76
|
+
const filterTree = normalizeFilterTree(safeJSONParse(filters))
|
|
77
|
+
if (!filterTree) return
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// Validate operator to prevent SQL injection
|
|
101
|
-
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
102
|
-
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
103
|
-
conditions.push(\`\${field} \${sqlOperator} ?\`)
|
|
104
|
-
queryParams.push(value)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
})
|
|
108
|
-
} else {
|
|
109
|
-
Object.entries(parsedFilters).forEach(([key, value]) => {
|
|
110
|
-
if (Array.isArray(value)) {
|
|
111
|
-
const placeholders = value.map(() => '?').join(', ')
|
|
112
|
-
queryParams.push(...value)
|
|
113
|
-
conditions.push(\`\${mysql.escapeId(key)} IN (\${placeholders})\`)
|
|
114
|
-
} else {
|
|
115
|
-
conditions.push(\`\${mysql.escapeId(key)} = ?\`)
|
|
116
|
-
queryParams.push(value)
|
|
117
|
-
}
|
|
118
|
-
})
|
|
79
|
+
const buildCondition = (condition) => {
|
|
80
|
+
const field = mysql.escapeId(condition.source)
|
|
81
|
+
const value = condition.destination
|
|
82
|
+
const operand = condition.operand
|
|
83
|
+
|
|
84
|
+
if (Array.isArray(value)) {
|
|
85
|
+
if (value.length === 0) return null
|
|
86
|
+
const placeholders = value.map(() => '?').join(', ')
|
|
87
|
+
queryParams.push(...value)
|
|
88
|
+
return operand === '!='
|
|
89
|
+
? \`\${field} NOT IN (\${placeholders})\`
|
|
90
|
+
: \`\${field} IN (\${placeholders})\`
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (value === null) {
|
|
94
|
+
if (operand === '=') return \`\${field} IS NULL\`
|
|
95
|
+
if (operand === '!=') return \`\${field} IS NOT NULL\`
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Validate operator to prevent SQL injection
|
|
100
|
+
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
101
|
+
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
102
|
+
queryParams.push(value)
|
|
103
|
+
return \`\${field} \${sqlOperator} ?\`
|
|
119
104
|
}
|
|
105
|
+
|
|
106
|
+
const clause = buildFilterTreeClause(filterTree, buildCondition)
|
|
107
|
+
if (clause) conditions.push(clause)
|
|
120
108
|
}
|
|
121
109
|
|
|
122
110
|
${generateDateFormatterCode()}
|