@teleporthq/teleport-plugin-next-data-source 0.42.9 → 0.42.10
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__/csv-header-detection.test.ts +212 -0
- package/__tests__/validation.test.ts +33 -2
- package/dist/cjs/data-source-fetchers.d.ts +2 -2
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- package/dist/cjs/data-source-fetchers.js +30 -7
- package/dist/cjs/data-source-fetchers.js.map +1 -1
- 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 +22 -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 +6 -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 +3 -3
- package/dist/cjs/fetchers/mariadb.js.map +1 -1
- package/dist/cjs/fetchers/mongodb.d.ts +1 -1
- package/dist/cjs/fetchers/mongodb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mongodb.js +11 -3
- 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 +2 -2
- 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 +3 -3
- 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/static-collection.d.ts.map +1 -1
- package/dist/cjs/fetchers/static-collection.js +1 -1
- package/dist/cjs/fetchers/static-collection.js.map +1 -1
- package/dist/cjs/fetchers/supabase.d.ts.map +1 -1
- package/dist/cjs/fetchers/supabase.js +2 -2
- package/dist/cjs/fetchers/supabase.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/fetchers/utils/header-detection.d.ts +2 -0
- package/dist/cjs/fetchers/utils/header-detection.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/header-detection.js +8 -0
- package/dist/cjs/fetchers/utils/header-detection.js.map +1 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +168 -4
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +320 -65
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +2 -0
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +214 -46
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/data-source-fetchers.d.ts +2 -2
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- package/dist/esm/data-source-fetchers.js +29 -6
- package/dist/esm/data-source-fetchers.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 +23 -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 +6 -1
- 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 +4 -4
- package/dist/esm/fetchers/mariadb.js.map +1 -1
- package/dist/esm/fetchers/mongodb.d.ts +1 -1
- package/dist/esm/fetchers/mongodb.d.ts.map +1 -1
- package/dist/esm/fetchers/mongodb.js +12 -4
- 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 +3 -3
- 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 +4 -4
- 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/static-collection.d.ts.map +1 -1
- package/dist/esm/fetchers/static-collection.js +2 -2
- package/dist/esm/fetchers/static-collection.js.map +1 -1
- package/dist/esm/fetchers/supabase.d.ts.map +1 -1
- package/dist/esm/fetchers/supabase.js +3 -3
- package/dist/esm/fetchers/supabase.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/fetchers/utils/header-detection.d.ts +2 -0
- package/dist/esm/fetchers/utils/header-detection.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/header-detection.js +4 -0
- package/dist/esm/fetchers/utils/header-detection.js.map +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +169 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +320 -65
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +2 -0
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +211 -45
- package/dist/esm/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/data-source-fetchers.ts +29 -13
- package/src/fetchers/airtable.ts +78 -30
- package/src/fetchers/clickhouse.ts +85 -18
- package/src/fetchers/csv-file.ts +254 -29
- package/src/fetchers/firestore.ts +62 -12
- package/src/fetchers/google-sheets.ts +147 -30
- package/src/fetchers/javascript.ts +102 -23
- package/src/fetchers/mariadb.ts +82 -25
- package/src/fetchers/mongodb.ts +153 -36
- package/src/fetchers/mysql.ts +83 -25
- package/src/fetchers/postgresql.ts +86 -26
- package/src/fetchers/redis.ts +40 -4
- package/src/fetchers/redshift.ts +84 -17
- package/src/fetchers/rest-api.ts +101 -24
- package/src/fetchers/static-collection.ts +96 -18
- package/src/fetchers/supabase.ts +175 -53
- package/src/fetchers/turso.ts +84 -17
- package/src/fetchers/utils/header-detection.ts +200 -0
- package/src/index.ts +248 -2
- package/src/pagination-plugin.ts +708 -191
- package/src/utils.ts +344 -38
package/src/fetchers/mongodb.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
replaceSecretReference,
|
|
3
|
+
generateDateFormatterCode,
|
|
4
|
+
generateSafeJSONParseCode,
|
|
5
|
+
} from '../utils'
|
|
2
6
|
|
|
3
7
|
export const validateMongoDBConfig = (
|
|
4
8
|
config: Record<string, unknown>
|
|
@@ -67,6 +71,99 @@ export const generateMongoDBFetcher = (
|
|
|
67
71
|
|
|
68
72
|
return `import { MongoClient, ObjectId } from 'mongodb'
|
|
69
73
|
|
|
74
|
+
${generateSafeJSONParseCode()}
|
|
75
|
+
|
|
76
|
+
// Helper function to process filters
|
|
77
|
+
const processFilters = (filters, filter) => {
|
|
78
|
+
if (!filters) return
|
|
79
|
+
|
|
80
|
+
const parsedFilters = safeJSONParse(filters)
|
|
81
|
+
|
|
82
|
+
if (Array.isArray(parsedFilters)) {
|
|
83
|
+
parsedFilters.forEach((filterItem) => {
|
|
84
|
+
if (!filterItem.source || filterItem.destination === undefined) return
|
|
85
|
+
|
|
86
|
+
const field = filterItem.source
|
|
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
|
+
}
|
|
99
|
+
return v
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (Array.isArray(value)) {
|
|
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
|
+
})
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
70
167
|
${generateDateFormatterCode()}
|
|
71
168
|
|
|
72
169
|
export default async function handler(req, res) {
|
|
@@ -82,7 +179,7 @@ export default async function handler(req, res) {
|
|
|
82
179
|
const db = client.db(${JSON.stringify(database)})
|
|
83
180
|
const collection = db.collection('${tableName}')
|
|
84
181
|
|
|
85
|
-
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, offset } = req.query
|
|
182
|
+
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query
|
|
86
183
|
|
|
87
184
|
const filter = {}
|
|
88
185
|
|
|
@@ -91,7 +188,7 @@ export default async function handler(req, res) {
|
|
|
91
188
|
|
|
92
189
|
if (queryColumns) {
|
|
93
190
|
// Use specified columns
|
|
94
|
-
columns =
|
|
191
|
+
columns = safeJSONParse(queryColumns)
|
|
95
192
|
} else {
|
|
96
193
|
// Fallback: Get all field names from a sample document
|
|
97
194
|
try {
|
|
@@ -113,30 +210,26 @@ export default async function handler(req, res) {
|
|
|
113
210
|
}
|
|
114
211
|
}
|
|
115
212
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
Object.entries(parsedFilters).forEach(([key, value]) => {
|
|
119
|
-
if (key === '_id') {
|
|
120
|
-
if (Array.isArray(value)) {
|
|
121
|
-
filter[key] = {
|
|
122
|
-
$in: value.map((id) => (typeof id === 'string' ? new ObjectId(id) : id))
|
|
123
|
-
}
|
|
124
|
-
} else if (typeof value === 'string') {
|
|
125
|
-
filter[key] = new ObjectId(value)
|
|
126
|
-
} else {
|
|
127
|
-
filter[key] = value
|
|
128
|
-
}
|
|
129
|
-
} else if (Array.isArray(value)) {
|
|
130
|
-
filter[key] = { $in: value }
|
|
131
|
-
} else {
|
|
132
|
-
filter[key] = value
|
|
133
|
-
}
|
|
134
|
-
})
|
|
135
|
-
}
|
|
213
|
+
// Apply filters using helper function
|
|
214
|
+
processFilters(filters, filter)
|
|
136
215
|
|
|
137
216
|
let cursor = collection.find(filter)
|
|
138
217
|
|
|
139
|
-
|
|
218
|
+
// Handle sorts - new array format
|
|
219
|
+
if (sorts) {
|
|
220
|
+
const parsedSorts = safeJSONParse(sorts)
|
|
221
|
+
if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {
|
|
222
|
+
const sortObject = {}
|
|
223
|
+
parsedSorts.forEach((sort) => {
|
|
224
|
+
if (sort.field) {
|
|
225
|
+
sortObject[sort.field] = sort.order?.toLowerCase() === 'desc' ? -1 : 1
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
if (Object.keys(sortObject).length > 0) {
|
|
229
|
+
cursor = cursor.sort(sortObject)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
} else if (sortBy) {
|
|
140
233
|
const sortOrderValue = sortOrder?.toLowerCase() === 'desc' ? -1 : 1
|
|
141
234
|
cursor = cursor.sort({ [sortBy]: sortOrderValue })
|
|
142
235
|
}
|
|
@@ -181,15 +274,34 @@ export default async function handler(req, res) {
|
|
|
181
274
|
}
|
|
182
275
|
|
|
183
276
|
// tslint:disable-next-line:variable-name
|
|
184
|
-
export const generateMongoDBCountFetcher = (
|
|
277
|
+
export const generateMongoDBCountFetcher = (config: any, tableName: string): string => {
|
|
278
|
+
const mongoConfig = config as MongoDBConfig
|
|
279
|
+
const hasUsername = mongoConfig?.username
|
|
280
|
+
const database = mongoConfig?.database
|
|
281
|
+
|
|
282
|
+
// Build connection string from parts if not provided
|
|
283
|
+
let connectionString = mongoConfig.connectionString
|
|
284
|
+
if (!connectionString) {
|
|
285
|
+
connectionString = `mongodb://${
|
|
286
|
+
hasUsername ? `${mongoConfig.username}:${mongoConfig.password}@` : ''
|
|
287
|
+
}${mongoConfig.host}:${mongoConfig.port || 27017}/${database}`
|
|
288
|
+
}
|
|
289
|
+
|
|
185
290
|
return `
|
|
186
291
|
async function getCount(req, res) {
|
|
187
|
-
|
|
188
|
-
const db = client.db()
|
|
189
|
-
|
|
292
|
+
let client = null
|
|
190
293
|
try {
|
|
191
|
-
const
|
|
294
|
+
const url = ${replaceSecretReference(connectionString)}
|
|
295
|
+
client = new MongoClient(url, {
|
|
296
|
+
connectTimeoutMS: 30000,
|
|
297
|
+
serverSelectionTimeoutMS: 30000
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
await client.connect()
|
|
301
|
+
const db = client.db(${JSON.stringify(database)})
|
|
192
302
|
const collection = db.collection('${tableName}')
|
|
303
|
+
|
|
304
|
+
const { query, queryColumns, filters } = req.query
|
|
193
305
|
const filter = {}
|
|
194
306
|
|
|
195
307
|
if (query) {
|
|
@@ -197,7 +309,8 @@ async function getCount(req, res) {
|
|
|
197
309
|
|
|
198
310
|
if (queryColumns) {
|
|
199
311
|
// Use specified columns
|
|
200
|
-
|
|
312
|
+
const parsed = safeJSONParse(queryColumns)
|
|
313
|
+
columns = Array.isArray(parsed) ? parsed : [parsed]
|
|
201
314
|
} else {
|
|
202
315
|
// Fallback: Get all field names from a sample document
|
|
203
316
|
try {
|
|
@@ -218,12 +331,8 @@ async function getCount(req, res) {
|
|
|
218
331
|
}
|
|
219
332
|
}
|
|
220
333
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
for (const f of parsedFilters) {
|
|
224
|
-
filter[f.column] = f.value
|
|
225
|
-
}
|
|
226
|
-
}
|
|
334
|
+
// Apply filters using helper function
|
|
335
|
+
processFilters(filters, filter)
|
|
227
336
|
|
|
228
337
|
const count = await collection.countDocuments(filter)
|
|
229
338
|
|
|
@@ -239,6 +348,14 @@ async function getCount(req, res) {
|
|
|
239
348
|
error: error.message || 'Failed to get count',
|
|
240
349
|
timestamp: Date.now()
|
|
241
350
|
})
|
|
351
|
+
} finally {
|
|
352
|
+
if (client) {
|
|
353
|
+
try {
|
|
354
|
+
await client.close()
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error('Error closing MongoDB client:', error)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
242
359
|
}
|
|
243
360
|
}
|
|
244
361
|
`
|
package/src/fetchers/mysql.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
replaceSecretReference,
|
|
3
|
+
generateDateFormatterCode,
|
|
4
|
+
generateSafeJSONParseCode,
|
|
5
|
+
} from '../utils'
|
|
2
6
|
|
|
3
7
|
interface MySQLConfig {
|
|
4
8
|
host?: string
|
|
@@ -57,13 +61,68 @@ const getConnection = () => {
|
|
|
57
61
|
})
|
|
58
62
|
}
|
|
59
63
|
|
|
64
|
+
${generateSafeJSONParseCode()}
|
|
65
|
+
|
|
66
|
+
// Helper function to process filters and build conditions
|
|
67
|
+
const processFilters = (filters, conditions, queryParams) => {
|
|
68
|
+
if (!filters) return
|
|
69
|
+
|
|
70
|
+
const parsedFilters = safeJSONParse(filters)
|
|
71
|
+
|
|
72
|
+
if (Array.isArray(parsedFilters)) {
|
|
73
|
+
parsedFilters.forEach((filter) => {
|
|
74
|
+
if (!filter.source || filter.destination === undefined) return
|
|
75
|
+
|
|
76
|
+
const field = mysql.escapeId(filter.source)
|
|
77
|
+
const value = filter.destination
|
|
78
|
+
const operand = filter.operand || '='
|
|
79
|
+
|
|
80
|
+
if (Array.isArray(value)) {
|
|
81
|
+
if (value.length === 0) return
|
|
82
|
+
const placeholders = value.map(() => '?').join(', ')
|
|
83
|
+
queryParams.push(...value)
|
|
84
|
+
if (operand === '!=') {
|
|
85
|
+
conditions.push(\`\${field} NOT IN (\${placeholders})\`)
|
|
86
|
+
} else {
|
|
87
|
+
conditions.push(\`\${field} IN (\${placeholders})\`)
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
if (value === null) {
|
|
91
|
+
if (operand === '=') {
|
|
92
|
+
conditions.push(\`\${field} IS NULL\`)
|
|
93
|
+
} else if (operand === '!=') {
|
|
94
|
+
conditions.push(\`\${field} IS NOT NULL\`)
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
// Validate operator to prevent SQL injection
|
|
98
|
+
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
99
|
+
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
100
|
+
conditions.push(\`\${field} \${sqlOperator} ?\`)
|
|
101
|
+
queryParams.push(value)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
} else {
|
|
106
|
+
Object.entries(parsedFilters).forEach(([key, value]) => {
|
|
107
|
+
if (Array.isArray(value)) {
|
|
108
|
+
const placeholders = value.map(() => '?').join(', ')
|
|
109
|
+
queryParams.push(...value)
|
|
110
|
+
conditions.push(\`\${mysql.escapeId(key)} IN (\${placeholders})\`)
|
|
111
|
+
} else {
|
|
112
|
+
conditions.push(\`\${mysql.escapeId(key)} = ?\`)
|
|
113
|
+
queryParams.push(value)
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
60
119
|
${generateDateFormatterCode()}
|
|
61
120
|
|
|
62
121
|
export default async function handler(req, res) {
|
|
63
122
|
const connection = await getConnection()
|
|
64
123
|
|
|
65
124
|
try {
|
|
66
|
-
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, offset } = req.query
|
|
125
|
+
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query
|
|
67
126
|
|
|
68
127
|
const conditions = []
|
|
69
128
|
const queryParams = []
|
|
@@ -73,7 +132,7 @@ export default async function handler(req, res) {
|
|
|
73
132
|
|
|
74
133
|
if (queryColumns) {
|
|
75
134
|
// Use specified columns
|
|
76
|
-
columns =
|
|
135
|
+
columns = safeJSONParse(queryColumns)
|
|
77
136
|
} else {
|
|
78
137
|
// Fallback: Get all columns from information_schema
|
|
79
138
|
try {
|
|
@@ -97,19 +156,8 @@ export default async function handler(req, res) {
|
|
|
97
156
|
}
|
|
98
157
|
}
|
|
99
158
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Object.entries(parsedFilters).forEach(([key, value]) => {
|
|
103
|
-
if (Array.isArray(value)) {
|
|
104
|
-
const placeholders = value.map(() => '?').join(', ')
|
|
105
|
-
queryParams.push(...value)
|
|
106
|
-
conditions.push(\`\${mysql.escapeId(key)} IN (\${placeholders})\`)
|
|
107
|
-
} else {
|
|
108
|
-
conditions.push(\`\${mysql.escapeId(key)} = ?\`)
|
|
109
|
-
queryParams.push(value)
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
}
|
|
159
|
+
// Apply filters using helper function
|
|
160
|
+
processFilters(filters, conditions, queryParams)
|
|
113
161
|
|
|
114
162
|
let sql = \`SELECT * FROM \${mysql.escapeId('${tableName}')}\`
|
|
115
163
|
|
|
@@ -117,7 +165,21 @@ export default async function handler(req, res) {
|
|
|
117
165
|
sql += \` WHERE \${conditions.join(' AND ')}\`
|
|
118
166
|
}
|
|
119
167
|
|
|
120
|
-
|
|
168
|
+
// Handle sorts - new array format
|
|
169
|
+
if (sorts) {
|
|
170
|
+
const parsedSorts = safeJSONParse(sorts)
|
|
171
|
+
if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {
|
|
172
|
+
const orderClauses = parsedSorts.map((sort) => {
|
|
173
|
+
if (!sort.field) return null
|
|
174
|
+
const order = sort.order?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC'
|
|
175
|
+
return \`\${mysql.escapeId(sort.field)} \${order}\`
|
|
176
|
+
}).filter(Boolean)
|
|
177
|
+
|
|
178
|
+
if (orderClauses.length > 0) {
|
|
179
|
+
sql += \` ORDER BY \${orderClauses.join(', ')}\`
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} else if (sortBy) {
|
|
121
183
|
sql += \` ORDER BY \${mysql.escapeId(sortBy)} \${sortOrder?.toUpperCase() || 'ASC'}\`
|
|
122
184
|
}
|
|
123
185
|
|
|
@@ -184,7 +246,8 @@ async function getCount(req, res) {
|
|
|
184
246
|
|
|
185
247
|
if (queryColumns) {
|
|
186
248
|
// Use specified columns
|
|
187
|
-
|
|
249
|
+
const parsed = safeJSONParse(queryColumns)
|
|
250
|
+
columns = Array.isArray(parsed) ? parsed : [parsed]
|
|
188
251
|
} else {
|
|
189
252
|
// Fallback: Get all columns from information_schema
|
|
190
253
|
try {
|
|
@@ -208,13 +271,8 @@ async function getCount(req, res) {
|
|
|
208
271
|
}
|
|
209
272
|
}
|
|
210
273
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
for (const filter of parsedFilters) {
|
|
214
|
-
conditions.push(\`\${filter.column} \${filter.operator} ?\`)
|
|
215
|
-
queryParams.push(filter.value)
|
|
216
|
-
}
|
|
217
|
-
}
|
|
274
|
+
// Apply filters using helper function
|
|
275
|
+
processFilters(filters, conditions, queryParams)
|
|
218
276
|
|
|
219
277
|
let countSql = \`SELECT COUNT(*) as count FROM ${tableName}\`
|
|
220
278
|
if (conditions.length > 0) {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
replaceSecretReference,
|
|
3
|
+
generateDateFormatterCode,
|
|
4
|
+
generateSafeJSONParseCode,
|
|
5
|
+
} from '../utils'
|
|
2
6
|
|
|
3
7
|
interface PostgreSQLConfig {
|
|
4
8
|
host?: string
|
|
@@ -43,6 +47,64 @@ const getClient = () => {
|
|
|
43
47
|
})
|
|
44
48
|
}
|
|
45
49
|
|
|
50
|
+
${generateSafeJSONParseCode()}
|
|
51
|
+
|
|
52
|
+
// Helper function to process filters and build conditions
|
|
53
|
+
const processFilters = (filters, conditions, queryParams, paramIndex) => {
|
|
54
|
+
if (!filters) return paramIndex
|
|
55
|
+
|
|
56
|
+
const parsedFilters = safeJSONParse(filters)
|
|
57
|
+
|
|
58
|
+
if (Array.isArray(parsedFilters)) {
|
|
59
|
+
parsedFilters.forEach((filter) => {
|
|
60
|
+
if (!filter.source || filter.destination === undefined) return
|
|
61
|
+
|
|
62
|
+
const field = filter.source
|
|
63
|
+
const value = filter.destination
|
|
64
|
+
const operand = filter.operand || '='
|
|
65
|
+
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
if (value.length === 0) return
|
|
68
|
+
const placeholders = value.map(() => \`$\${paramIndex++}\`)
|
|
69
|
+
queryParams.push(...value)
|
|
70
|
+
if (operand === '!=') {
|
|
71
|
+
conditions.push(\`\${field} NOT IN (\${placeholders.join(', ')})\`)
|
|
72
|
+
} else {
|
|
73
|
+
conditions.push(\`\${field} IN (\${placeholders.join(', ')})\`)
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
if (value === null) {
|
|
77
|
+
if (operand === '=') {
|
|
78
|
+
conditions.push(\`\${field} IS NULL\`)
|
|
79
|
+
} else if (operand === '!=') {
|
|
80
|
+
conditions.push(\`\${field} IS NOT NULL\`)
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
const validOps = ['=', '!=', '>', '<', '>=', '<=']
|
|
84
|
+
const sqlOperator = validOps.includes(operand) ? operand : '='
|
|
85
|
+
conditions.push(\`\${field} \${sqlOperator} $\${paramIndex}\`)
|
|
86
|
+
queryParams.push(value)
|
|
87
|
+
paramIndex++
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
} else {
|
|
92
|
+
Object.entries(parsedFilters).forEach(([key, value]) => {
|
|
93
|
+
if (Array.isArray(value)) {
|
|
94
|
+
const placeholders = value.map(() => \`$\${paramIndex++}\`)
|
|
95
|
+
queryParams.push(...value)
|
|
96
|
+
conditions.push(\`\${key} IN (\${placeholders.join(', ')})\`)
|
|
97
|
+
} else {
|
|
98
|
+
conditions.push(\`\${key} = $\${paramIndex}\`)
|
|
99
|
+
queryParams.push(value)
|
|
100
|
+
paramIndex++
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return paramIndex
|
|
106
|
+
}
|
|
107
|
+
|
|
46
108
|
${generateDateFormatterCode()}
|
|
47
109
|
|
|
48
110
|
export default async function handler(req, res) {
|
|
@@ -52,7 +114,7 @@ export default async function handler(req, res) {
|
|
|
52
114
|
await client.connect()
|
|
53
115
|
${schema ? `await client.query('SET search_path TO ${schema}')` : ''}
|
|
54
116
|
|
|
55
|
-
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, offset } = req.query
|
|
117
|
+
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query
|
|
56
118
|
|
|
57
119
|
const conditions = []
|
|
58
120
|
const queryParams = []
|
|
@@ -63,7 +125,7 @@ export default async function handler(req, res) {
|
|
|
63
125
|
|
|
64
126
|
if (queryColumns) {
|
|
65
127
|
// Use specified columns
|
|
66
|
-
columns =
|
|
128
|
+
columns = safeJSONParse(queryColumns)
|
|
67
129
|
} else {
|
|
68
130
|
// Fallback: Get all columns from information_schema
|
|
69
131
|
try {
|
|
@@ -97,20 +159,8 @@ export default async function handler(req, res) {
|
|
|
97
159
|
}
|
|
98
160
|
}
|
|
99
161
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Object.entries(parsedFilters).forEach(([key, value]) => {
|
|
103
|
-
if (Array.isArray(value)) {
|
|
104
|
-
const placeholders = value.map(() => \`$\${paramIndex++}\`)
|
|
105
|
-
queryParams.push(...value)
|
|
106
|
-
conditions.push(\`\${key} IN (\${placeholders.join(', ')})\`)
|
|
107
|
-
} else {
|
|
108
|
-
conditions.push(\`\${key} = $\${paramIndex}\`)
|
|
109
|
-
queryParams.push(value)
|
|
110
|
-
paramIndex++
|
|
111
|
-
}
|
|
112
|
-
})
|
|
113
|
-
}
|
|
162
|
+
// Apply filters using helper function
|
|
163
|
+
paramIndex = processFilters(filters, conditions, queryParams, paramIndex)
|
|
114
164
|
|
|
115
165
|
let sql = \`SELECT * FROM ${tableName}\`
|
|
116
166
|
|
|
@@ -118,7 +168,21 @@ export default async function handler(req, res) {
|
|
|
118
168
|
sql += \` WHERE \${conditions.join(' AND ')}\`
|
|
119
169
|
}
|
|
120
170
|
|
|
121
|
-
|
|
171
|
+
// Handle sorts - new array format
|
|
172
|
+
if (sorts) {
|
|
173
|
+
const parsedSorts = safeJSONParse(sorts)
|
|
174
|
+
if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {
|
|
175
|
+
const orderClauses = parsedSorts.map((sort) => {
|
|
176
|
+
if (!sort.field) return null
|
|
177
|
+
const order = sort.order?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC'
|
|
178
|
+
return \`\${sort.field} \${order}\`
|
|
179
|
+
}).filter(Boolean)
|
|
180
|
+
|
|
181
|
+
if (orderClauses.length > 0) {
|
|
182
|
+
sql += \` ORDER BY \${orderClauses.join(', ')}\`
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} else if (sortBy) {
|
|
122
186
|
sql += \` ORDER BY \${sortBy} \${sortOrder?.toUpperCase() || 'ASC'}\`
|
|
123
187
|
}
|
|
124
188
|
|
|
@@ -188,7 +252,8 @@ async function getCount(req, res) {
|
|
|
188
252
|
|
|
189
253
|
if (queryColumns) {
|
|
190
254
|
// Use specified columns
|
|
191
|
-
|
|
255
|
+
const parsed = safeJSONParse(queryColumns)
|
|
256
|
+
columns = Array.isArray(parsed) ? parsed : [parsed]
|
|
192
257
|
} else {
|
|
193
258
|
// Fallback: Get all columns from information_schema
|
|
194
259
|
try {
|
|
@@ -220,13 +285,8 @@ async function getCount(req, res) {
|
|
|
220
285
|
}
|
|
221
286
|
}
|
|
222
287
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
for (const filter of parsedFilters) {
|
|
226
|
-
conditions.push(\`\${filter.column} \${filter.operator} $\${paramIndex++}\`)
|
|
227
|
-
queryParams.push(filter.value)
|
|
228
|
-
}
|
|
229
|
-
}
|
|
288
|
+
// Apply filters using helper function
|
|
289
|
+
paramIndex = processFilters(filters, conditions, queryParams, paramIndex)
|
|
230
290
|
|
|
231
291
|
let countSql = \`SELECT COUNT(*) FROM ${tableName}\`
|
|
232
292
|
if (conditions.length > 0) {
|
package/src/fetchers/redis.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
replaceSecretReference,
|
|
3
|
+
generateDateFormatterCode,
|
|
4
|
+
generateSafeJSONParseCode,
|
|
5
|
+
} from '../utils'
|
|
2
6
|
|
|
3
7
|
export const validateRedisConfig = (
|
|
4
8
|
config: Record<string, unknown>
|
|
@@ -63,6 +67,8 @@ export const generateRedisFetcher = (config: Record<string, unknown>): string =>
|
|
|
63
67
|
|
|
64
68
|
return `import { createClient } from 'redis'
|
|
65
69
|
|
|
70
|
+
${generateSafeJSONParseCode()}
|
|
71
|
+
|
|
66
72
|
${generateDateFormatterCode()}
|
|
67
73
|
|
|
68
74
|
export default async function handler(req, res) {
|
|
@@ -76,9 +82,23 @@ export default async function handler(req, res) {
|
|
|
76
82
|
|
|
77
83
|
await client.connect()
|
|
78
84
|
|
|
79
|
-
const { query, limit, page, perPage, sortBy, sortOrder, filters, offset } = req.query
|
|
85
|
+
const { query, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query
|
|
86
|
+
|
|
87
|
+
let pattern = query || '*'
|
|
88
|
+
|
|
89
|
+
// Extract pattern from filters if available (new format)
|
|
90
|
+
if (filters) {
|
|
91
|
+
const parsedFilters = safeJSONParse(filters)
|
|
92
|
+
if (Array.isArray(parsedFilters)) {
|
|
93
|
+
const patternFilter = parsedFilters.find(f => f.source === 'pattern')
|
|
94
|
+
if (patternFilter) {
|
|
95
|
+
pattern = patternFilter.destination || pattern
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
pattern = parsedFilters.pattern || pattern
|
|
99
|
+
}
|
|
100
|
+
}
|
|
80
101
|
|
|
81
|
-
const pattern = (filters && JSON.parse(filters).pattern) || query || '*'
|
|
82
102
|
const keys = await client.keys(pattern)
|
|
83
103
|
|
|
84
104
|
const limitValue = limit || perPage || 100
|
|
@@ -119,7 +139,23 @@ export default async function handler(req, res) {
|
|
|
119
139
|
})
|
|
120
140
|
}
|
|
121
141
|
|
|
122
|
-
|
|
142
|
+
// Handle sorts - new array format
|
|
143
|
+
if (sorts) {
|
|
144
|
+
const parsedSorts = safeJSONParse(sorts)
|
|
145
|
+
if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {
|
|
146
|
+
const primarySort = parsedSorts[0]
|
|
147
|
+
if (primarySort.field) {
|
|
148
|
+
const sortOrderValue = primarySort.order?.toLowerCase() === 'desc' ? -1 : 1
|
|
149
|
+
results.sort((a, b) => {
|
|
150
|
+
const aVal = a[primarySort.field]
|
|
151
|
+
const bVal = b[primarySort.field]
|
|
152
|
+
if (aVal < bVal) return -sortOrderValue
|
|
153
|
+
if (aVal > bVal) return sortOrderValue
|
|
154
|
+
return 0
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} else if (sortBy) {
|
|
123
159
|
const sortOrderValue = sortOrder?.toLowerCase() === 'desc' ? -1 : 1
|
|
124
160
|
results.sort((a, b) => {
|
|
125
161
|
const aVal = a[sortBy]
|