@teleporthq/teleport-plugin-next-data-source 0.42.1 → 0.42.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.
- package/dist/cjs/count-fetchers.d.ts.map +1 -1
- package/dist/cjs/count-fetchers.js +1 -1
- package/dist/cjs/count-fetchers.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/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/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/redshift.d.ts.map +1 -1
- package/dist/cjs/fetchers/redshift.js +3 -1
- 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/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/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +151 -119
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/count-fetchers.d.ts.map +1 -1
- package/dist/esm/count-fetchers.js +1 -1
- package/dist/esm/count-fetchers.js.map +1 -1
- package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/esm/fetchers/clickhouse.js +1 -1
- package/dist/esm/fetchers/clickhouse.js.map +1 -1
- package/dist/esm/fetchers/firestore.d.ts.map +1 -1
- package/dist/esm/fetchers/firestore.js +1 -1
- package/dist/esm/fetchers/firestore.js.map +1 -1
- package/dist/esm/fetchers/javascript.d.ts.map +1 -1
- package/dist/esm/fetchers/javascript.js +1 -1
- package/dist/esm/fetchers/javascript.js.map +1 -1
- package/dist/esm/fetchers/redshift.d.ts.map +1 -1
- package/dist/esm/fetchers/redshift.js +3 -1
- 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 +2 -2
- package/dist/esm/fetchers/rest-api.js.map +1 -1
- package/dist/esm/fetchers/turso.d.ts.map +1 -1
- package/dist/esm/fetchers/turso.js +1 -1
- package/dist/esm/fetchers/turso.js.map +1 -1
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +151 -119
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/count-fetchers.ts +2 -1
- package/src/fetchers/clickhouse.ts +12 -6
- package/src/fetchers/firestore.ts +45 -13
- package/src/fetchers/javascript.ts +48 -13
- package/src/fetchers/redshift.ts +32 -9
- package/src/fetchers/rest-api.ts +68 -6
- package/src/fetchers/turso.ts +46 -16
- package/src/pagination-plugin.ts +308 -257
|
@@ -85,12 +85,20 @@ export default async function handler(req, res) {
|
|
|
85
85
|
})
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
let usePostFiltering = false
|
|
89
|
+
|
|
90
|
+
if (query) {
|
|
91
|
+
if (queryColumns) {
|
|
92
|
+
const columns = typeof queryColumns === 'string' ? JSON.parse(queryColumns) : (Array.isArray(queryColumns) ? queryColumns : [queryColumns])
|
|
93
|
+
for (const column of columns) {
|
|
94
|
+
queryRef = queryRef
|
|
95
|
+
.where(column, '>=', query)
|
|
96
|
+
.where(column, '<=', query + '\\uf8ff')
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
// Firestore doesn't support full-text search without queryColumns
|
|
100
|
+
// We'll fetch all data and filter in JavaScript
|
|
101
|
+
usePostFiltering = true
|
|
94
102
|
}
|
|
95
103
|
}
|
|
96
104
|
|
|
@@ -100,17 +108,20 @@ export default async function handler(req, res) {
|
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
const limitValue = limit || perPage
|
|
103
|
-
if (limitValue) {
|
|
104
|
-
queryRef = queryRef.limit(parseInt(limitValue))
|
|
105
|
-
}
|
|
106
|
-
|
|
107
111
|
const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage && parseInt(page) > 1 ? (parseInt(page) - 1) * parseInt(perPage) : undefined)
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
|
|
113
|
+
// Only apply pagination at query level if not post-filtering
|
|
114
|
+
if (!usePostFiltering) {
|
|
115
|
+
if (limitValue) {
|
|
116
|
+
queryRef = queryRef.limit(parseInt(limitValue))
|
|
117
|
+
}
|
|
118
|
+
if (offsetValue !== undefined) {
|
|
119
|
+
queryRef = queryRef.offset(offsetValue)
|
|
120
|
+
}
|
|
110
121
|
}
|
|
111
122
|
|
|
112
123
|
const snapshot = await queryRef.get()
|
|
113
|
-
|
|
124
|
+
let documents = []
|
|
114
125
|
snapshot.forEach((doc) => {
|
|
115
126
|
documents.push({
|
|
116
127
|
id: doc.id,
|
|
@@ -118,6 +129,27 @@ export default async function handler(req, res) {
|
|
|
118
129
|
})
|
|
119
130
|
})
|
|
120
131
|
|
|
132
|
+
// Apply post-filtering if needed
|
|
133
|
+
if (usePostFiltering && query) {
|
|
134
|
+
const searchQuery = query.toLowerCase()
|
|
135
|
+
documents = documents.filter((item) => {
|
|
136
|
+
try {
|
|
137
|
+
const stringified = JSON.stringify(item).toLowerCase()
|
|
138
|
+
return stringified.includes(searchQuery)
|
|
139
|
+
} catch {
|
|
140
|
+
return false
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// Apply pagination after filtering
|
|
145
|
+
if (limitValue) {
|
|
146
|
+
const start = offsetValue || 0
|
|
147
|
+
documents = documents.slice(start, start + parseInt(limitValue))
|
|
148
|
+
} else if (offsetValue) {
|
|
149
|
+
documents = documents.slice(offsetValue)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
121
153
|
const safeData = JSON.parse(JSON.stringify(documents))
|
|
122
154
|
|
|
123
155
|
return res.status(200).json({
|
|
@@ -37,28 +37,31 @@ export const generateJavaScriptFetcher = (config: Record<string, unknown>): stri
|
|
|
37
37
|
const jsConfig = config as JavaScriptConfig
|
|
38
38
|
return `export default async function handler(req, res) {
|
|
39
39
|
try {
|
|
40
|
-
const { limit, offset, page, perPage, query, queryColumns } = req.query
|
|
40
|
+
const { limit, offset, page, perPage, query, queryColumns, sortBy, sortOrder, filters } = req.query
|
|
41
41
|
|
|
42
42
|
const code = ${JSON.stringify(jsConfig.code)}
|
|
43
43
|
const executeCode = new Function('return ' + code)
|
|
44
44
|
let data = executeCode()
|
|
45
45
|
|
|
46
46
|
if (Array.isArray(data)) {
|
|
47
|
-
|
|
47
|
+
// 1. Apply search filter
|
|
48
|
+
if (query && query.trim()) {
|
|
48
49
|
const searchQuery = query.toLowerCase()
|
|
49
50
|
|
|
50
51
|
if (queryColumns) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
try {
|
|
53
|
+
const columns = typeof queryColumns === 'string' ? JSON.parse(queryColumns) : (Array.isArray(queryColumns) ? queryColumns : [queryColumns])
|
|
54
|
+
data = data.filter(item => {
|
|
55
|
+
return columns.some(col => {
|
|
56
|
+
const value = item[col]
|
|
57
|
+
if (value === null || value === undefined) return false
|
|
58
|
+
return String(value).toLowerCase().includes(searchQuery)
|
|
59
|
+
})
|
|
58
60
|
})
|
|
59
|
-
})
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.error('Error parsing queryColumns:', err)
|
|
63
|
+
}
|
|
60
64
|
} else {
|
|
61
|
-
// Search across all fields by stringifying the entire record
|
|
62
65
|
data = data.filter(item => {
|
|
63
66
|
try {
|
|
64
67
|
const stringified = JSON.stringify(item).toLowerCase()
|
|
@@ -70,11 +73,43 @@ export const generateJavaScriptFetcher = (config: Record<string, unknown>): stri
|
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
// 2. Apply custom filters
|
|
77
|
+
if (filters) {
|
|
78
|
+
try {
|
|
79
|
+
const parsedFilters = typeof filters === 'string' ? JSON.parse(filters) : filters
|
|
80
|
+
data = data.filter((item) => {
|
|
81
|
+
return Object.entries(parsedFilters).every(([key, value]) => {
|
|
82
|
+
if (Array.isArray(value)) {
|
|
83
|
+
return value.includes(item[key])
|
|
84
|
+
}
|
|
85
|
+
return item[key] === value
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.error('Error parsing filters:', err)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 3. Apply sorting
|
|
94
|
+
if (sortBy && sortBy.trim()) {
|
|
95
|
+
data.sort((a, b) => {
|
|
96
|
+
const aVal = a[sortBy]
|
|
97
|
+
const bVal = b[sortBy]
|
|
98
|
+
const sortOrderValue = sortOrder?.toLowerCase() === 'desc' ? -1 : 1
|
|
99
|
+
if (aVal < bVal) return -sortOrderValue
|
|
100
|
+
if (aVal > bVal) return sortOrderValue
|
|
101
|
+
return 0
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 4. Apply pagination
|
|
73
106
|
const limitValue = limit || perPage
|
|
74
|
-
const
|
|
107
|
+
const pageValue = page ? Math.max(1, parseInt(page)) : undefined
|
|
108
|
+
const offsetValue = offset !== undefined ? Math.max(0, parseInt(offset)) : (pageValue && perPage ? (pageValue - 1) * Math.max(1, parseInt(perPage)) : 0)
|
|
75
109
|
|
|
76
110
|
if (limitValue) {
|
|
77
|
-
|
|
111
|
+
const limitInt = Math.max(1, parseInt(limitValue))
|
|
112
|
+
data = data.slice(offsetValue, offsetValue + limitInt)
|
|
78
113
|
} else if (offsetValue > 0) {
|
|
79
114
|
data = data.slice(offsetValue)
|
|
80
115
|
}
|
package/src/fetchers/redshift.ts
CHANGED
|
@@ -66,15 +66,38 @@ export default async function handler(req, res) {
|
|
|
66
66
|
const queryParams = []
|
|
67
67
|
let paramIndex = 1
|
|
68
68
|
|
|
69
|
-
if (query
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
if (query) {
|
|
70
|
+
let columns = []
|
|
71
|
+
|
|
72
|
+
if (queryColumns) {
|
|
73
|
+
columns = typeof queryColumns === 'string' ? JSON.parse(queryColumns) : (Array.isArray(queryColumns) ? queryColumns : [queryColumns])
|
|
74
|
+
} else {
|
|
75
|
+
// Fallback: Get all columns from information_schema
|
|
76
|
+
try {
|
|
77
|
+
const schemaQuery = 'SELECT column_name FROM information_schema.columns WHERE table_name = $1' +
|
|
78
|
+
${schema ? `' AND table_schema = $2'` : `''`} +
|
|
79
|
+
' ORDER BY ordinal_position'
|
|
80
|
+
const schemaParams = ${
|
|
81
|
+
schema
|
|
82
|
+
? `[${JSON.stringify(tableName)}, ${JSON.stringify(schema)}]`
|
|
83
|
+
: `[${JSON.stringify(tableName)}]`
|
|
84
|
+
}
|
|
85
|
+
const schemaResult = await pool.query(schemaQuery, schemaParams)
|
|
86
|
+
columns = schemaResult.rows.map(row => row.column_name)
|
|
87
|
+
} catch (schemaError) {
|
|
88
|
+
console.warn('Failed to fetch column names from information_schema:', schemaError.message)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (columns.length > 0) {
|
|
93
|
+
const searchConditions = columns.map((col) => {
|
|
94
|
+
const condition = \`\${col}::text ILIKE $\${paramIndex}\`
|
|
95
|
+
paramIndex++
|
|
96
|
+
return condition
|
|
97
|
+
})
|
|
98
|
+
columns.forEach(() => queryParams.push(\`%\${query}%\`))
|
|
99
|
+
conditions.push(\`(\${searchConditions.join(' OR ')})\`)
|
|
100
|
+
}
|
|
78
101
|
}
|
|
79
102
|
|
|
80
103
|
if (filters) {
|
package/src/fetchers/rest-api.ts
CHANGED
|
@@ -77,7 +77,7 @@ export const generateRESTAPIFetcher = (config: Record<string, unknown>): string
|
|
|
77
77
|
|
|
78
78
|
export default async function handler(req, res) {
|
|
79
79
|
try {
|
|
80
|
-
const {
|
|
80
|
+
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, offset } = req.query
|
|
81
81
|
|
|
82
82
|
const url = ${JSON.stringify(restConfig.url)}
|
|
83
83
|
const method = ${JSON.stringify(restConfig.method || 'GET')}
|
|
@@ -116,13 +116,75 @@ export default async function handler(req, res) {
|
|
|
116
116
|
|
|
117
117
|
let data = await response.json()
|
|
118
118
|
|
|
119
|
-
// Apply
|
|
119
|
+
// Apply filtering, sorting, and pagination if data is an array
|
|
120
120
|
if (Array.isArray(data)) {
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
// 1. Apply search filter
|
|
122
|
+
if (query && query.trim()) {
|
|
123
|
+
const searchQuery = query.toLowerCase()
|
|
124
|
+
|
|
125
|
+
if (queryColumns) {
|
|
126
|
+
try {
|
|
127
|
+
const columns = typeof queryColumns === 'string' ? JSON.parse(queryColumns) : (Array.isArray(queryColumns) ? queryColumns : [queryColumns])
|
|
128
|
+
data = data.filter((item) => {
|
|
129
|
+
return columns.some((col) => {
|
|
130
|
+
const value = item[col]
|
|
131
|
+
if (value === null || value === undefined) return false
|
|
132
|
+
return String(value).toLowerCase().includes(searchQuery)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
} catch (err) {
|
|
136
|
+
console.error('Error parsing queryColumns:', err)
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
// Search across all fields
|
|
140
|
+
data = data.filter((item) => {
|
|
141
|
+
try {
|
|
142
|
+
const stringified = JSON.stringify(item).toLowerCase()
|
|
143
|
+
return stringified.includes(searchQuery)
|
|
144
|
+
} catch {
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 2. Apply custom filters
|
|
152
|
+
if (filters) {
|
|
153
|
+
try {
|
|
154
|
+
const parsedFilters = typeof filters === 'string' ? JSON.parse(filters) : filters
|
|
155
|
+
data = data.filter((item) => {
|
|
156
|
+
return Object.entries(parsedFilters).every(([key, value]) => {
|
|
157
|
+
if (Array.isArray(value)) {
|
|
158
|
+
return value.includes(item[key])
|
|
159
|
+
}
|
|
160
|
+
return item[key] === value
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
} catch (err) {
|
|
164
|
+
console.error('Error parsing filters:', err)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 3. Apply sorting
|
|
169
|
+
if (sortBy && sortBy.trim()) {
|
|
170
|
+
data.sort((a, b) => {
|
|
171
|
+
const aVal = a[sortBy]
|
|
172
|
+
const bVal = b[sortBy]
|
|
173
|
+
const sortOrderValue = sortOrder?.toLowerCase() === 'desc' ? -1 : 1
|
|
174
|
+
if (aVal < bVal) return -sortOrderValue
|
|
175
|
+
if (aVal > bVal) return sortOrderValue
|
|
176
|
+
return 0
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 4. Apply pagination
|
|
181
|
+
const limitValue = limit || perPage
|
|
182
|
+
const pageValue = page ? Math.max(1, parseInt(page)) : undefined
|
|
183
|
+
const offsetValue = offset !== undefined ? Math.max(0, parseInt(offset)) : (pageValue && perPage ? (pageValue - 1) * Math.max(1, parseInt(perPage)) : 0)
|
|
123
184
|
|
|
124
|
-
if (limitValue
|
|
125
|
-
|
|
185
|
+
if (limitValue) {
|
|
186
|
+
const limitInt = Math.max(1, parseInt(limitValue))
|
|
187
|
+
data = data.slice(offsetValue, offsetValue + limitInt)
|
|
126
188
|
} else if (offsetValue > 0) {
|
|
127
189
|
data = data.slice(offsetValue)
|
|
128
190
|
}
|
package/src/fetchers/turso.ts
CHANGED
|
@@ -48,14 +48,20 @@ export default async function handler(req, res) {
|
|
|
48
48
|
let sql = \`SELECT * FROM ${tableName}\`
|
|
49
49
|
const whereClauses = []
|
|
50
50
|
const queryParams = []
|
|
51
|
+
let searchQueryColumns = null
|
|
51
52
|
|
|
52
|
-
if (query
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
if (query) {
|
|
54
|
+
if (queryColumns) {
|
|
55
|
+
const columns = typeof queryColumns === 'string' ? JSON.parse(queryColumns) : (Array.isArray(queryColumns) ? queryColumns : [queryColumns])
|
|
56
|
+
const searchConditions = columns.map((col) => \`\${col} LIKE ?\`)
|
|
57
|
+
whereClauses.push(\`(\${searchConditions.join(' OR ')})\`)
|
|
58
|
+
columns.forEach(() => {
|
|
59
|
+
queryParams.push(\`%\${query}%\`)
|
|
60
|
+
})
|
|
61
|
+
} else {
|
|
62
|
+
// Store query for post-filtering if columns not specified
|
|
63
|
+
searchQueryColumns = query
|
|
64
|
+
}
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
if (filters) {
|
|
@@ -84,14 +90,17 @@ export default async function handler(req, res) {
|
|
|
84
90
|
const limitValue = limit || perPage
|
|
85
91
|
const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)
|
|
86
92
|
|
|
87
|
-
if
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
// Only apply SQL pagination if we're not doing post-filtering
|
|
94
|
+
if (!searchQueryColumns) {
|
|
95
|
+
if (limitValue) {
|
|
96
|
+
sql += \` LIMIT ?\`
|
|
97
|
+
queryParams.push(parseInt(limitValue))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (offsetValue !== undefined) {
|
|
101
|
+
sql += \` OFFSET ?\`
|
|
102
|
+
queryParams.push(offsetValue)
|
|
103
|
+
}
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
const result = await client.execute({
|
|
@@ -99,7 +108,7 @@ export default async function handler(req, res) {
|
|
|
99
108
|
args: queryParams
|
|
100
109
|
})
|
|
101
110
|
|
|
102
|
-
|
|
111
|
+
let data = result.rows.map((row) => {
|
|
103
112
|
const obj = {}
|
|
104
113
|
result.columns.forEach((col, idx) => {
|
|
105
114
|
obj[col] = row[col]
|
|
@@ -107,6 +116,27 @@ export default async function handler(req, res) {
|
|
|
107
116
|
return obj
|
|
108
117
|
})
|
|
109
118
|
|
|
119
|
+
// Apply post-filtering for search without queryColumns
|
|
120
|
+
if (searchQueryColumns) {
|
|
121
|
+
const searchQuery = searchQueryColumns.toLowerCase()
|
|
122
|
+
data = data.filter((item) => {
|
|
123
|
+
try {
|
|
124
|
+
const stringified = JSON.stringify(item).toLowerCase()
|
|
125
|
+
return stringified.includes(searchQuery)
|
|
126
|
+
} catch {
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// Apply pagination after filtering
|
|
132
|
+
if (limitValue) {
|
|
133
|
+
const start = offsetValue || 0
|
|
134
|
+
data = data.slice(start, start + parseInt(limitValue))
|
|
135
|
+
} else if (offsetValue) {
|
|
136
|
+
data = data.slice(offsetValue)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
110
140
|
const safeData = JSON.parse(JSON.stringify(data))
|
|
111
141
|
|
|
112
142
|
return res.status(200).json({
|