@teleporthq/teleport-plugin-next-data-source 0.40.15
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/ARRAY_MAPPER_PAGINATION.md +1128 -0
- package/LICENSE +21 -0
- package/README.md +40 -0
- package/SEARCH_IMPLEMENTATION_SUMMARY.md +983 -0
- package/__tests__/fetchers.test.ts +545 -0
- package/__tests__/integration.test.ts +561 -0
- package/__tests__/mocks.ts +241 -0
- package/__tests__/pagination.test.ts +31 -0
- package/__tests__/plugin.test.ts +577 -0
- package/__tests__/utils.test.ts +430 -0
- package/__tests__/validation.test.ts +348 -0
- package/dist/cjs/array-mapper-pagination.d.ts +32 -0
- package/dist/cjs/array-mapper-pagination.d.ts.map +1 -0
- package/dist/cjs/array-mapper-pagination.js +77 -0
- package/dist/cjs/array-mapper-pagination.js.map +1 -0
- package/dist/cjs/count-fetchers.d.ts +12 -0
- package/dist/cjs/count-fetchers.d.ts.map +1 -0
- package/dist/cjs/count-fetchers.js +46 -0
- package/dist/cjs/count-fetchers.js.map +1 -0
- package/dist/cjs/data-source-fetchers.d.ts +14 -0
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -0
- package/dist/cjs/data-source-fetchers.js +185 -0
- package/dist/cjs/data-source-fetchers.js.map +1 -0
- package/dist/cjs/fetchers/airtable.d.ts +6 -0
- package/dist/cjs/fetchers/airtable.d.ts.map +1 -0
- package/dist/cjs/fetchers/airtable.js +27 -0
- package/dist/cjs/fetchers/airtable.js.map +1 -0
- package/dist/cjs/fetchers/clickhouse.d.ts +6 -0
- package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -0
- package/dist/cjs/fetchers/clickhouse.js +29 -0
- package/dist/cjs/fetchers/clickhouse.js.map +1 -0
- package/dist/cjs/fetchers/csv-file.d.ts +7 -0
- package/dist/cjs/fetchers/csv-file.d.ts.map +1 -0
- package/dist/cjs/fetchers/csv-file.js +36 -0
- package/dist/cjs/fetchers/csv-file.js.map +1 -0
- package/dist/cjs/fetchers/firestore.d.ts +6 -0
- package/dist/cjs/fetchers/firestore.d.ts.map +1 -0
- package/dist/cjs/fetchers/firestore.js +35 -0
- package/dist/cjs/fetchers/firestore.js.map +1 -0
- package/dist/cjs/fetchers/google-sheets.d.ts +6 -0
- package/dist/cjs/fetchers/google-sheets.d.ts.map +1 -0
- package/dist/cjs/fetchers/google-sheets.js +30 -0
- package/dist/cjs/fetchers/google-sheets.js.map +1 -0
- package/dist/cjs/fetchers/index.d.ts +17 -0
- package/dist/cjs/fetchers/index.d.ts.map +1 -0
- package/dist/cjs/fetchers/index.js +56 -0
- package/dist/cjs/fetchers/index.js.map +1 -0
- package/dist/cjs/fetchers/javascript.d.ts +7 -0
- package/dist/cjs/fetchers/javascript.d.ts.map +1 -0
- package/dist/cjs/fetchers/javascript.js +40 -0
- package/dist/cjs/fetchers/javascript.js.map +1 -0
- package/dist/cjs/fetchers/mariadb.d.ts +3 -0
- package/dist/cjs/fetchers/mariadb.d.ts.map +1 -0
- package/dist/cjs/fetchers/mariadb.js +23 -0
- package/dist/cjs/fetchers/mariadb.js.map +1 -0
- package/dist/cjs/fetchers/mongodb.d.ts +7 -0
- package/dist/cjs/fetchers/mongodb.d.ts.map +1 -0
- package/dist/cjs/fetchers/mongodb.js +52 -0
- package/dist/cjs/fetchers/mongodb.js.map +1 -0
- package/dist/cjs/fetchers/mysql.d.ts +3 -0
- package/dist/cjs/fetchers/mysql.d.ts.map +1 -0
- package/dist/cjs/fetchers/mysql.js +30 -0
- package/dist/cjs/fetchers/mysql.js.map +1 -0
- package/dist/cjs/fetchers/postgresql.d.ts +3 -0
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -0
- package/dist/cjs/fetchers/postgresql.js +25 -0
- package/dist/cjs/fetchers/postgresql.js.map +1 -0
- package/dist/cjs/fetchers/redis.d.ts +6 -0
- package/dist/cjs/fetchers/redis.d.ts.map +1 -0
- package/dist/cjs/fetchers/redis.js +46 -0
- package/dist/cjs/fetchers/redis.js.map +1 -0
- package/dist/cjs/fetchers/redshift.d.ts +2 -0
- package/dist/cjs/fetchers/redshift.d.ts.map +1 -0
- package/dist/cjs/fetchers/redshift.js +24 -0
- package/dist/cjs/fetchers/redshift.js.map +1 -0
- package/dist/cjs/fetchers/rest-api.d.ts +6 -0
- package/dist/cjs/fetchers/rest-api.d.ts.map +1 -0
- package/dist/cjs/fetchers/rest-api.js +58 -0
- package/dist/cjs/fetchers/rest-api.js.map +1 -0
- package/dist/cjs/fetchers/static-collection.d.ts +7 -0
- package/dist/cjs/fetchers/static-collection.d.ts.map +1 -0
- package/dist/cjs/fetchers/static-collection.js +24 -0
- package/dist/cjs/fetchers/static-collection.js.map +1 -0
- package/dist/cjs/fetchers/supabase.d.ts +7 -0
- package/dist/cjs/fetchers/supabase.d.ts.map +1 -0
- package/dist/cjs/fetchers/supabase.js +42 -0
- package/dist/cjs/fetchers/supabase.js.map +1 -0
- package/dist/cjs/fetchers/turso.d.ts +6 -0
- package/dist/cjs/fetchers/turso.d.ts.map +1 -0
- package/dist/cjs/fetchers/turso.js +25 -0
- package/dist/cjs/fetchers/turso.js.map +1 -0
- package/dist/cjs/index.d.ts +9 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +325 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/pagination-plugin.d.ts +5 -0
- package/dist/cjs/pagination-plugin.d.ts.map +1 -0
- package/dist/cjs/pagination-plugin.js +1484 -0
- package/dist/cjs/pagination-plugin.js.map +1 -0
- package/dist/cjs/pagination-with-count.d.ts +6 -0
- package/dist/cjs/pagination-with-count.d.ts.map +1 -0
- package/dist/cjs/pagination-with-count.js +63 -0
- package/dist/cjs/pagination-with-count.js.map +1 -0
- package/dist/cjs/tsconfig.tsbuildinfo +1 -0
- package/dist/cjs/utils.d.ts +31 -0
- package/dist/cjs/utils.d.ts.map +1 -0
- package/dist/cjs/utils.js +763 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/cjs/validation.d.ts +5 -0
- package/dist/cjs/validation.d.ts.map +1 -0
- package/dist/cjs/validation.js +29 -0
- package/dist/cjs/validation.js.map +1 -0
- package/dist/esm/array-mapper-pagination.d.ts +32 -0
- package/dist/esm/array-mapper-pagination.d.ts.map +1 -0
- package/dist/esm/array-mapper-pagination.js +72 -0
- package/dist/esm/array-mapper-pagination.js.map +1 -0
- package/dist/esm/count-fetchers.d.ts +12 -0
- package/dist/esm/count-fetchers.d.ts.map +1 -0
- package/dist/esm/count-fetchers.js +35 -0
- package/dist/esm/count-fetchers.js.map +1 -0
- package/dist/esm/data-source-fetchers.d.ts +14 -0
- package/dist/esm/data-source-fetchers.d.ts.map +1 -0
- package/dist/esm/data-source-fetchers.js +179 -0
- package/dist/esm/data-source-fetchers.js.map +1 -0
- package/dist/esm/fetchers/airtable.d.ts +6 -0
- package/dist/esm/fetchers/airtable.d.ts.map +1 -0
- package/dist/esm/fetchers/airtable.js +22 -0
- package/dist/esm/fetchers/airtable.js.map +1 -0
- package/dist/esm/fetchers/clickhouse.d.ts +6 -0
- package/dist/esm/fetchers/clickhouse.d.ts.map +1 -0
- package/dist/esm/fetchers/clickhouse.js +24 -0
- package/dist/esm/fetchers/clickhouse.js.map +1 -0
- package/dist/esm/fetchers/csv-file.d.ts +7 -0
- package/dist/esm/fetchers/csv-file.d.ts.map +1 -0
- package/dist/esm/fetchers/csv-file.js +30 -0
- package/dist/esm/fetchers/csv-file.js.map +1 -0
- package/dist/esm/fetchers/firestore.d.ts +6 -0
- package/dist/esm/fetchers/firestore.d.ts.map +1 -0
- package/dist/esm/fetchers/firestore.js +30 -0
- package/dist/esm/fetchers/firestore.js.map +1 -0
- package/dist/esm/fetchers/google-sheets.d.ts +6 -0
- package/dist/esm/fetchers/google-sheets.d.ts.map +1 -0
- package/dist/esm/fetchers/google-sheets.js +25 -0
- package/dist/esm/fetchers/google-sheets.js.map +1 -0
- package/dist/esm/fetchers/index.d.ts +17 -0
- package/dist/esm/fetchers/index.d.ts.map +1 -0
- package/dist/esm/fetchers/index.js +17 -0
- package/dist/esm/fetchers/index.js.map +1 -0
- package/dist/esm/fetchers/javascript.d.ts +7 -0
- package/dist/esm/fetchers/javascript.d.ts.map +1 -0
- package/dist/esm/fetchers/javascript.js +34 -0
- package/dist/esm/fetchers/javascript.js.map +1 -0
- package/dist/esm/fetchers/mariadb.d.ts +3 -0
- package/dist/esm/fetchers/mariadb.d.ts.map +1 -0
- package/dist/esm/fetchers/mariadb.js +18 -0
- package/dist/esm/fetchers/mariadb.js.map +1 -0
- package/dist/esm/fetchers/mongodb.d.ts +7 -0
- package/dist/esm/fetchers/mongodb.d.ts.map +1 -0
- package/dist/esm/fetchers/mongodb.js +46 -0
- package/dist/esm/fetchers/mongodb.js.map +1 -0
- package/dist/esm/fetchers/mysql.d.ts +3 -0
- package/dist/esm/fetchers/mysql.d.ts.map +1 -0
- package/dist/esm/fetchers/mysql.js +25 -0
- package/dist/esm/fetchers/mysql.js.map +1 -0
- package/dist/esm/fetchers/postgresql.d.ts +3 -0
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -0
- package/dist/esm/fetchers/postgresql.js +20 -0
- package/dist/esm/fetchers/postgresql.js.map +1 -0
- package/dist/esm/fetchers/redis.d.ts +6 -0
- package/dist/esm/fetchers/redis.d.ts.map +1 -0
- package/dist/esm/fetchers/redis.js +41 -0
- package/dist/esm/fetchers/redis.js.map +1 -0
- package/dist/esm/fetchers/redshift.d.ts +2 -0
- package/dist/esm/fetchers/redshift.d.ts.map +1 -0
- package/dist/esm/fetchers/redshift.js +20 -0
- package/dist/esm/fetchers/redshift.js.map +1 -0
- package/dist/esm/fetchers/rest-api.d.ts +6 -0
- package/dist/esm/fetchers/rest-api.d.ts.map +1 -0
- package/dist/esm/fetchers/rest-api.js +53 -0
- package/dist/esm/fetchers/rest-api.js.map +1 -0
- package/dist/esm/fetchers/static-collection.d.ts +7 -0
- package/dist/esm/fetchers/static-collection.d.ts.map +1 -0
- package/dist/esm/fetchers/static-collection.js +18 -0
- package/dist/esm/fetchers/static-collection.js.map +1 -0
- package/dist/esm/fetchers/supabase.d.ts +7 -0
- package/dist/esm/fetchers/supabase.d.ts.map +1 -0
- package/dist/esm/fetchers/supabase.js +36 -0
- package/dist/esm/fetchers/supabase.js.map +1 -0
- package/dist/esm/fetchers/turso.d.ts +6 -0
- package/dist/esm/fetchers/turso.d.ts.map +1 -0
- package/dist/esm/fetchers/turso.js +20 -0
- package/dist/esm/fetchers/turso.js.map +1 -0
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +306 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/pagination-plugin.d.ts +5 -0
- package/dist/esm/pagination-plugin.d.ts.map +1 -0
- package/dist/esm/pagination-plugin.js +1457 -0
- package/dist/esm/pagination-plugin.js.map +1 -0
- package/dist/esm/pagination-with-count.d.ts +6 -0
- package/dist/esm/pagination-with-count.d.ts.map +1 -0
- package/dist/esm/pagination-with-count.js +34 -0
- package/dist/esm/pagination-with-count.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -0
- package/dist/esm/utils.d.ts +31 -0
- package/dist/esm/utils.d.ts.map +1 -0
- package/dist/esm/utils.js +722 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/esm/validation.d.ts +5 -0
- package/dist/esm/validation.d.ts.map +1 -0
- package/dist/esm/validation.js +25 -0
- package/dist/esm/validation.js.map +1 -0
- package/package.json +33 -0
- package/src/array-mapper-pagination.ts +113 -0
- package/src/count-fetchers.ts +99 -0
- package/src/data-source-fetchers.ts +313 -0
- package/src/fetchers/airtable.ts +153 -0
- package/src/fetchers/clickhouse.ts +127 -0
- package/src/fetchers/csv-file.ts +163 -0
- package/src/fetchers/firestore.ts +138 -0
- package/src/fetchers/google-sheets.ts +189 -0
- package/src/fetchers/index.ts +32 -0
- package/src/fetchers/javascript.ts +150 -0
- package/src/fetchers/mariadb.ts +230 -0
- package/src/fetchers/mongodb.ts +239 -0
- package/src/fetchers/mysql.ts +237 -0
- package/src/fetchers/postgresql.ts +247 -0
- package/src/fetchers/redis.ts +152 -0
- package/src/fetchers/redshift.ts +138 -0
- package/src/fetchers/rest-api.ts +148 -0
- package/src/fetchers/static-collection.ts +149 -0
- package/src/fetchers/supabase.ts +246 -0
- package/src/fetchers/turso.ts +131 -0
- package/src/index.ts +352 -0
- package/src/pagination-plugin.ts +2335 -0
- package/src/pagination-with-count.ts +89 -0
- package/src/utils.ts +1013 -0
- package/src/validation.ts +32 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
export const validateStaticCollectionConfig = (
|
|
2
|
+
config: Record<string, unknown>
|
|
3
|
+
): { isValid: boolean; error?: string } => {
|
|
4
|
+
if (!config || typeof config !== 'object') {
|
|
5
|
+
return { isValid: false, error: 'Config must be a valid object' }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (!config.data || !Array.isArray(config.data)) {
|
|
9
|
+
return { isValid: false, error: 'Data must be an array' }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return { isValid: true }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface StaticCollectionConfig {
|
|
16
|
+
data?: unknown[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const generateStaticCollectionFetcher = (config: Record<string, unknown>): string => {
|
|
20
|
+
const staticConfig = config as StaticCollectionConfig
|
|
21
|
+
return `const data = ${JSON.stringify(staticConfig.data || [])}
|
|
22
|
+
|
|
23
|
+
export default async function handler(req, res) {
|
|
24
|
+
try {
|
|
25
|
+
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, offset: offsetParam } = req.query
|
|
26
|
+
|
|
27
|
+
let filteredData = [...data]
|
|
28
|
+
|
|
29
|
+
if (query) {
|
|
30
|
+
const searchQuery = query.toLowerCase()
|
|
31
|
+
|
|
32
|
+
if (queryColumns) {
|
|
33
|
+
const columns = JSON.parse(queryColumns)
|
|
34
|
+
filteredData = filteredData.filter((item) => {
|
|
35
|
+
return columns.some((col) => {
|
|
36
|
+
const value = item[col]
|
|
37
|
+
return value && String(value).toLowerCase().includes(searchQuery)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
} else {
|
|
41
|
+
filteredData = filteredData.filter((item) => {
|
|
42
|
+
try {
|
|
43
|
+
const stringified = JSON.stringify(item).toLowerCase()
|
|
44
|
+
return stringified.includes(searchQuery)
|
|
45
|
+
} catch {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (filters) {
|
|
53
|
+
const parsedFilters = JSON.parse(filters)
|
|
54
|
+
filteredData = filteredData.filter((item) => {
|
|
55
|
+
return Object.entries(parsedFilters).every(([key, value]) => {
|
|
56
|
+
if (Array.isArray(value)) {
|
|
57
|
+
return value.includes(item[key])
|
|
58
|
+
}
|
|
59
|
+
return item[key] === value
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (sortBy) {
|
|
65
|
+
filteredData.sort((a, b) => {
|
|
66
|
+
const aVal = a[sortBy]
|
|
67
|
+
const bVal = b[sortBy]
|
|
68
|
+
const sortOrderValue = sortOrder?.toLowerCase() === 'desc' ? -1 : 1
|
|
69
|
+
if (aVal < bVal) return -sortOrderValue
|
|
70
|
+
if (aVal > bVal) return sortOrderValue
|
|
71
|
+
return 0
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const limitValue = limit || perPage
|
|
76
|
+
const offsetValue = offsetParam !== undefined ? parseInt(offsetParam) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : 0)
|
|
77
|
+
|
|
78
|
+
if (limitValue) {
|
|
79
|
+
filteredData = filteredData.slice(offsetValue, offsetValue + parseInt(limitValue))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const safeData = JSON.parse(JSON.stringify(filteredData))
|
|
83
|
+
|
|
84
|
+
return res.status(200).json({
|
|
85
|
+
success: true,
|
|
86
|
+
data: safeData,
|
|
87
|
+
timestamp: Date.now()
|
|
88
|
+
})
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error('Static collection fetch error:', error)
|
|
91
|
+
return res.status(500).json({
|
|
92
|
+
success: false,
|
|
93
|
+
error: error.message || 'Failed to fetch data',
|
|
94
|
+
timestamp: Date.now()
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
`
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// tslint:disable-next-line:variable-name
|
|
102
|
+
export const generateStaticCollectionCountFetcher = (_config: any): string => {
|
|
103
|
+
return `
|
|
104
|
+
async function getCount(req, res) {
|
|
105
|
+
try {
|
|
106
|
+
const { query, queryColumns, filters } = req.query
|
|
107
|
+
const fakeReq = { query: { query, queryColumns, filters }, method: 'GET' }
|
|
108
|
+
let result = null
|
|
109
|
+
let statusCode = 200
|
|
110
|
+
|
|
111
|
+
const fakeRes = {
|
|
112
|
+
status: (code) => {
|
|
113
|
+
statusCode = code
|
|
114
|
+
return fakeRes
|
|
115
|
+
},
|
|
116
|
+
json: (data) => {
|
|
117
|
+
result = data
|
|
118
|
+
return fakeRes
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await handler(fakeReq, fakeRes)
|
|
123
|
+
|
|
124
|
+
if (statusCode !== 200 || !result || !result.success) {
|
|
125
|
+
return res.status(500).json({
|
|
126
|
+
success: false,
|
|
127
|
+
error: 'Failed to get data for counting',
|
|
128
|
+
timestamp: Date.now()
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const count = Array.isArray(result.data) ? result.data.length : 0
|
|
133
|
+
|
|
134
|
+
return res.status(200).json({
|
|
135
|
+
success: true,
|
|
136
|
+
count: count,
|
|
137
|
+
timestamp: Date.now()
|
|
138
|
+
})
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error('Error getting count:', error)
|
|
141
|
+
return res.status(500).json({
|
|
142
|
+
success: false,
|
|
143
|
+
error: error.message || 'Failed to get count',
|
|
144
|
+
timestamp: Date.now()
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
`
|
|
149
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { replaceSecretReference } from '../utils'
|
|
2
|
+
|
|
3
|
+
export const validateSupabaseConfig = (
|
|
4
|
+
config: Record<string, unknown>
|
|
5
|
+
): { isValid: boolean; error?: string } => {
|
|
6
|
+
if (!config || typeof config !== 'object') {
|
|
7
|
+
return { isValid: false, error: 'Config must be a valid object' }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!config.supabaseUrl || typeof config.supabaseUrl !== 'string') {
|
|
11
|
+
return { isValid: false, error: 'Supabase URL is required' }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const url = new URL(config.supabaseUrl)
|
|
16
|
+
if (!url.hostname.endsWith('.supabase.co') && !url.hostname.endsWith('.supabase.in')) {
|
|
17
|
+
console.warn('[Data Source] Warning: Supabase URL does not match expected format')
|
|
18
|
+
}
|
|
19
|
+
} catch {
|
|
20
|
+
return { isValid: false, error: 'Invalid Supabase URL format' }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!config.serviceRoleKey && !config.publicApiKey) {
|
|
24
|
+
return {
|
|
25
|
+
isValid: false,
|
|
26
|
+
error: 'Supabase API key (serviceRoleKey or publicApiKey) is required',
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { isValid: true }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface SupabaseConfig {
|
|
34
|
+
url?: string
|
|
35
|
+
anonKey?: string
|
|
36
|
+
supabaseUrl?: string
|
|
37
|
+
serviceRoleKey?: string
|
|
38
|
+
publicApiKey?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const generateSupabaseFetcher = (
|
|
42
|
+
config: Record<string, unknown>,
|
|
43
|
+
tableName: string
|
|
44
|
+
): string => {
|
|
45
|
+
const supabaseConfig = config as SupabaseConfig
|
|
46
|
+
const supabaseUrl = supabaseConfig.supabaseUrl
|
|
47
|
+
const apiKey = supabaseConfig.serviceRoleKey || supabaseConfig.publicApiKey
|
|
48
|
+
|
|
49
|
+
return `import { createClient } from '@supabase/supabase-js'
|
|
50
|
+
|
|
51
|
+
let client = null
|
|
52
|
+
|
|
53
|
+
const getClient = () => {
|
|
54
|
+
if (client) return client
|
|
55
|
+
|
|
56
|
+
client = createClient(
|
|
57
|
+
${JSON.stringify(supabaseUrl)},
|
|
58
|
+
${replaceSecretReference(apiKey)}
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return client
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default async function handler(req, res) {
|
|
65
|
+
try {
|
|
66
|
+
const client = getClient()
|
|
67
|
+
const { query, queryColumns, select, limit, page, perPage, sortBy, sortOrder, filters, offset } = req.query
|
|
68
|
+
|
|
69
|
+
let queryRef = client.from('${tableName}').select(select || '*')
|
|
70
|
+
|
|
71
|
+
if (query) {
|
|
72
|
+
let columns = []
|
|
73
|
+
|
|
74
|
+
if (queryColumns) {
|
|
75
|
+
// Use specified columns
|
|
76
|
+
columns = JSON.parse(queryColumns)
|
|
77
|
+
} else {
|
|
78
|
+
// Fallback: Get all column names from a sample row
|
|
79
|
+
try {
|
|
80
|
+
const { data: sampleData, error: sampleError } = await client.from('${tableName}').select('*').limit(1).single()
|
|
81
|
+
if (sampleError) {
|
|
82
|
+
throw sampleError
|
|
83
|
+
}
|
|
84
|
+
if (sampleData) {
|
|
85
|
+
columns = Object.keys(sampleData)
|
|
86
|
+
}
|
|
87
|
+
} catch (schemaError) {
|
|
88
|
+
console.warn('Failed to fetch sample row for column names:', schemaError.message)
|
|
89
|
+
// Continue without search if we can't get columns
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (columns.length > 0) {
|
|
94
|
+
const searchPattern = \`%\${query}%\`
|
|
95
|
+
const orConditions = columns.map((col) => \`\${col}.ilike.\${searchPattern}\`).join(',')
|
|
96
|
+
queryRef = queryRef.or(orConditions)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (filters) {
|
|
101
|
+
const parsedFilters = JSON.parse(filters)
|
|
102
|
+
Object.entries(parsedFilters).forEach(([key, value]) => {
|
|
103
|
+
if (Array.isArray(value)) {
|
|
104
|
+
const processedValues = value.map((v) => {
|
|
105
|
+
if (typeof v === 'string' && !isNaN(Number(v))) {
|
|
106
|
+
return Number(v)
|
|
107
|
+
}
|
|
108
|
+
return v
|
|
109
|
+
})
|
|
110
|
+
queryRef = queryRef.in(key, processedValues)
|
|
111
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
112
|
+
const operator = Object.keys(value)[0]
|
|
113
|
+
let operatorValue = value[operator]
|
|
114
|
+
if (typeof operatorValue === 'string' && !isNaN(Number(operatorValue))) {
|
|
115
|
+
operatorValue = Number(operatorValue)
|
|
116
|
+
}
|
|
117
|
+
switch (operator) {
|
|
118
|
+
case 'eq': queryRef = queryRef.eq(key, operatorValue); break
|
|
119
|
+
case 'neq': queryRef = queryRef.neq(key, operatorValue); break
|
|
120
|
+
case 'gt': queryRef = queryRef.gt(key, operatorValue); break
|
|
121
|
+
case 'gte': queryRef = queryRef.gte(key, operatorValue); break
|
|
122
|
+
case 'lt': queryRef = queryRef.lt(key, operatorValue); break
|
|
123
|
+
case 'lte': queryRef = queryRef.lte(key, operatorValue); break
|
|
124
|
+
case 'like': queryRef = queryRef.like(key, operatorValue); break
|
|
125
|
+
case 'ilike': queryRef = queryRef.ilike(key, operatorValue); break
|
|
126
|
+
case 'in': queryRef = queryRef.in(key, operatorValue); break
|
|
127
|
+
default: queryRef = queryRef.eq(key, operatorValue)
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
let processedValue = value
|
|
131
|
+
if (typeof value === 'string' && !isNaN(Number(value))) {
|
|
132
|
+
processedValue = Number(value)
|
|
133
|
+
}
|
|
134
|
+
queryRef = queryRef.eq(key, processedValue)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (sortBy) {
|
|
140
|
+
queryRef = queryRef.order(sortBy, { ascending: sortOrder !== 'desc' })
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const limitValue = limit || perPage
|
|
144
|
+
const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)
|
|
145
|
+
|
|
146
|
+
if (offsetValue !== undefined && limitValue) {
|
|
147
|
+
queryRef = queryRef.range(offsetValue, offsetValue + parseInt(limitValue) - 1)
|
|
148
|
+
} else if (limitValue) {
|
|
149
|
+
queryRef = queryRef.limit(parseInt(limitValue))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const { data, error } = await queryRef
|
|
153
|
+
|
|
154
|
+
if (error) {
|
|
155
|
+
return res.status(500).json({
|
|
156
|
+
success: false,
|
|
157
|
+
error: error.message,
|
|
158
|
+
timestamp: Date.now()
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const safeData = JSON.parse(JSON.stringify(data))
|
|
163
|
+
|
|
164
|
+
return res.status(200).json({
|
|
165
|
+
success: true,
|
|
166
|
+
data: safeData,
|
|
167
|
+
timestamp: Date.now()
|
|
168
|
+
})
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error('Supabase fetch error:', error)
|
|
171
|
+
return res.status(500).json({
|
|
172
|
+
success: false,
|
|
173
|
+
error: error.message || 'Failed to fetch data',
|
|
174
|
+
timestamp: Date.now()
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
`
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// tslint:disable-next-line:variable-name
|
|
182
|
+
export const generateSupabaseCountFetcher = (_config: any, tableName: string): string => {
|
|
183
|
+
return `
|
|
184
|
+
async function getCount(req, res) {
|
|
185
|
+
const supabase = getClient()
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
const { query, queryColumns, filters } = req.query
|
|
189
|
+
let countQuery = supabase.from('${tableName}').select('*', { count: 'exact', head: true })
|
|
190
|
+
|
|
191
|
+
if (query) {
|
|
192
|
+
let columns = []
|
|
193
|
+
|
|
194
|
+
if (queryColumns) {
|
|
195
|
+
// Use specified columns
|
|
196
|
+
columns = typeof queryColumns === 'string' ? JSON.parse(queryColumns) : (Array.isArray(queryColumns) ? queryColumns : [queryColumns])
|
|
197
|
+
} else {
|
|
198
|
+
// Fallback: Get all column names from a sample row
|
|
199
|
+
try {
|
|
200
|
+
const { data: sampleData, error: sampleError } = await supabase.from('${tableName}').select('*').limit(1).single()
|
|
201
|
+
if (sampleError) {
|
|
202
|
+
throw sampleError
|
|
203
|
+
}
|
|
204
|
+
if (sampleData) {
|
|
205
|
+
columns = Object.keys(sampleData)
|
|
206
|
+
}
|
|
207
|
+
} catch (schemaError) {
|
|
208
|
+
console.warn('Failed to fetch sample row for column names:', schemaError.message)
|
|
209
|
+
// Continue without search if we can't get columns
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (columns.length > 0) {
|
|
214
|
+
const searchPattern = \`%\${query}%\`
|
|
215
|
+
const orConditions = columns.map((col) => \`\${col}.ilike.\${searchPattern}\`).join(',')
|
|
216
|
+
countQuery = countQuery.or(orConditions)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (filters) {
|
|
221
|
+
const parsedFilters = JSON.parse(filters)
|
|
222
|
+
for (const filter of parsedFilters) {
|
|
223
|
+
countQuery = countQuery.eq(filter.column, filter.value)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const { count, error } = await countQuery
|
|
228
|
+
|
|
229
|
+
if (error) throw error
|
|
230
|
+
|
|
231
|
+
return res.status(200).json({
|
|
232
|
+
success: true,
|
|
233
|
+
count: count || 0,
|
|
234
|
+
timestamp: Date.now()
|
|
235
|
+
})
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error('Error getting count:', error)
|
|
238
|
+
return res.status(500).json({
|
|
239
|
+
success: false,
|
|
240
|
+
error: error.message || 'Failed to get count',
|
|
241
|
+
timestamp: Date.now()
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
`
|
|
246
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { replaceSecretReference } from '../utils'
|
|
2
|
+
|
|
3
|
+
export const validateTursoConfig = (
|
|
4
|
+
config: Record<string, unknown>
|
|
5
|
+
): { isValid: boolean; error?: string } => {
|
|
6
|
+
if (!config || typeof config !== 'object') {
|
|
7
|
+
return { isValid: false, error: 'Config must be a valid object' }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!config.databaseUrl || typeof config.databaseUrl !== 'string') {
|
|
11
|
+
return { isValid: false, error: 'Turso database URL is required' }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!config.token || typeof config.token !== 'string') {
|
|
15
|
+
return { isValid: false, error: 'Turso authentication token is required' }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return { isValid: true }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface TursoConfig {
|
|
22
|
+
url?: string
|
|
23
|
+
authToken?: string
|
|
24
|
+
databaseUrl?: string
|
|
25
|
+
token?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const generateTursoFetcher = (
|
|
29
|
+
config: Record<string, unknown>,
|
|
30
|
+
tableName: string
|
|
31
|
+
): string => {
|
|
32
|
+
const tursoConfig = config as TursoConfig
|
|
33
|
+
const databaseUrl = tursoConfig.databaseUrl
|
|
34
|
+
const token = tursoConfig.token
|
|
35
|
+
|
|
36
|
+
return `import { createClient } from '@libsql/client'
|
|
37
|
+
|
|
38
|
+
export default async function handler(req, res) {
|
|
39
|
+
let client = null
|
|
40
|
+
try {
|
|
41
|
+
client = createClient({
|
|
42
|
+
url: ${JSON.stringify(databaseUrl)},
|
|
43
|
+
authToken: ${replaceSecretReference(token)}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, offset } = req.query
|
|
47
|
+
|
|
48
|
+
let sql = \`SELECT * FROM ${tableName}\`
|
|
49
|
+
const whereClauses = []
|
|
50
|
+
const queryParams = []
|
|
51
|
+
|
|
52
|
+
if (query && queryColumns) {
|
|
53
|
+
const columns = JSON.parse(queryColumns)
|
|
54
|
+
const searchConditions = columns.map((col) => \`\${col} LIKE ?\`)
|
|
55
|
+
whereClauses.push(\`(\${searchConditions.join(' OR ')})\`)
|
|
56
|
+
columns.forEach(() => {
|
|
57
|
+
queryParams.push(\`%\${query}%\`)
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (filters) {
|
|
62
|
+
const parsedFilters = JSON.parse(filters)
|
|
63
|
+
Object.entries(parsedFilters).forEach(([key, value]) => {
|
|
64
|
+
if (Array.isArray(value)) {
|
|
65
|
+
const placeholders = value.map(() => '?').join(', ')
|
|
66
|
+
queryParams.push(...value)
|
|
67
|
+
whereClauses.push(\`\${key} IN (\${placeholders})\`)
|
|
68
|
+
} else {
|
|
69
|
+
whereClauses.push(\`\${key} = ?\`)
|
|
70
|
+
queryParams.push(value)
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (whereClauses.length > 0) {
|
|
76
|
+
sql += \` WHERE \${whereClauses.join(' AND ')}\`
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (sortBy) {
|
|
80
|
+
const sortOrderValue = sortOrder?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC'
|
|
81
|
+
sql += \` ORDER BY \${sortBy} \${sortOrderValue}\`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const limitValue = limit || perPage
|
|
85
|
+
const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)
|
|
86
|
+
|
|
87
|
+
if (limitValue) {
|
|
88
|
+
sql += \` LIMIT ?\`
|
|
89
|
+
queryParams.push(parseInt(limitValue))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (offsetValue !== undefined) {
|
|
93
|
+
sql += \` OFFSET ?\`
|
|
94
|
+
queryParams.push(offsetValue)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const result = await client.execute({
|
|
98
|
+
sql,
|
|
99
|
+
args: queryParams
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const data = result.rows.map((row) => {
|
|
103
|
+
const obj = {}
|
|
104
|
+
result.columns.forEach((col, idx) => {
|
|
105
|
+
obj[col] = row[col]
|
|
106
|
+
})
|
|
107
|
+
return obj
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const safeData = JSON.parse(JSON.stringify(data))
|
|
111
|
+
|
|
112
|
+
return res.status(200).json({
|
|
113
|
+
success: true,
|
|
114
|
+
data: safeData,
|
|
115
|
+
timestamp: Date.now()
|
|
116
|
+
})
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error('Turso fetch error:', error)
|
|
119
|
+
return res.status(500).json({
|
|
120
|
+
success: false,
|
|
121
|
+
error: error.message || 'Failed to fetch data',
|
|
122
|
+
timestamp: Date.now()
|
|
123
|
+
})
|
|
124
|
+
} finally {
|
|
125
|
+
if (client) {
|
|
126
|
+
client.close()
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
`
|
|
131
|
+
}
|