@squadbase/vite-server 0.0.1-build-2 → 0.0.1-build-4
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/cli/index.js +5 -194
- package/dist/index.d.ts +1 -120
- package/dist/index.js +27 -485
- package/dist/main.js +26 -215
- package/dist/types/data-source.d.ts +12 -5
- package/dist/vite-plugin.js +5 -194
- package/package.json +2 -9
package/dist/index.js
CHANGED
|
@@ -37,11 +37,6 @@ function resolveEnvVar(entry, key, slug) {
|
|
|
37
37
|
}
|
|
38
38
|
return value;
|
|
39
39
|
}
|
|
40
|
-
function resolveEnvVarOptional(entry, key) {
|
|
41
|
-
const envVarName = entry.envVars[key];
|
|
42
|
-
if (!envVarName) return void 0;
|
|
43
|
-
return process.env[envVarName] || void 0;
|
|
44
|
-
}
|
|
45
40
|
|
|
46
41
|
// src/connector-client/bigquery.ts
|
|
47
42
|
function createBigQueryClient(entry, slug) {
|
|
@@ -110,176 +105,6 @@ function createSnowflakeClient(entry, slug) {
|
|
|
110
105
|
};
|
|
111
106
|
}
|
|
112
107
|
|
|
113
|
-
// src/connector-client/mysql.ts
|
|
114
|
-
function createMySQLClient(entry, slug) {
|
|
115
|
-
const connectionUrl = resolveEnvVar(entry, "connection-url", slug);
|
|
116
|
-
let poolPromise = null;
|
|
117
|
-
function getPool() {
|
|
118
|
-
if (!poolPromise) {
|
|
119
|
-
poolPromise = import("mysql2/promise").then(
|
|
120
|
-
(mysql) => mysql.default.createPool(connectionUrl)
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
return poolPromise;
|
|
124
|
-
}
|
|
125
|
-
return {
|
|
126
|
-
async query(sql, params) {
|
|
127
|
-
const pool = await getPool();
|
|
128
|
-
const [rows] = await pool.execute(sql, params);
|
|
129
|
-
return { rows };
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// src/connector-client/aws-athena.ts
|
|
135
|
-
function createAthenaClient(entry, slug) {
|
|
136
|
-
const region = resolveEnvVar(entry, "aws-region", slug);
|
|
137
|
-
const accessKeyId = resolveEnvVar(entry, "aws-access-key-id", slug);
|
|
138
|
-
const secretAccessKey = resolveEnvVar(entry, "aws-secret-access-key", slug);
|
|
139
|
-
const workgroup = resolveEnvVarOptional(entry, "workgroup") ?? "primary";
|
|
140
|
-
const outputLocation = resolveEnvVarOptional(entry, "output-location");
|
|
141
|
-
return {
|
|
142
|
-
async query(sql) {
|
|
143
|
-
const {
|
|
144
|
-
AthenaClient,
|
|
145
|
-
StartQueryExecutionCommand,
|
|
146
|
-
GetQueryExecutionCommand,
|
|
147
|
-
GetQueryResultsCommand
|
|
148
|
-
} = await import("@aws-sdk/client-athena");
|
|
149
|
-
const client = new AthenaClient({
|
|
150
|
-
region,
|
|
151
|
-
credentials: { accessKeyId, secretAccessKey }
|
|
152
|
-
});
|
|
153
|
-
const startParams = {
|
|
154
|
-
QueryString: sql,
|
|
155
|
-
WorkGroup: workgroup
|
|
156
|
-
};
|
|
157
|
-
if (outputLocation) {
|
|
158
|
-
startParams.ResultConfiguration = { OutputLocation: outputLocation };
|
|
159
|
-
}
|
|
160
|
-
const { QueryExecutionId } = await client.send(
|
|
161
|
-
new StartQueryExecutionCommand(startParams)
|
|
162
|
-
);
|
|
163
|
-
if (!QueryExecutionId) throw new Error("Athena: failed to start query execution");
|
|
164
|
-
while (true) {
|
|
165
|
-
const { QueryExecution } = await client.send(
|
|
166
|
-
new GetQueryExecutionCommand({ QueryExecutionId })
|
|
167
|
-
);
|
|
168
|
-
const state = QueryExecution?.Status?.State;
|
|
169
|
-
if (state === "SUCCEEDED") break;
|
|
170
|
-
if (state === "FAILED") {
|
|
171
|
-
throw new Error(
|
|
172
|
-
`Athena query failed: ${QueryExecution?.Status?.StateChangeReason ?? "unknown"}`
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
if (state === "CANCELLED") throw new Error("Athena query was cancelled");
|
|
176
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
177
|
-
}
|
|
178
|
-
const { ResultSet } = await client.send(
|
|
179
|
-
new GetQueryResultsCommand({ QueryExecutionId })
|
|
180
|
-
);
|
|
181
|
-
const resultRows = ResultSet?.Rows ?? [];
|
|
182
|
-
if (resultRows.length === 0) return { rows: [] };
|
|
183
|
-
const headers = resultRows[0].Data?.map((d) => d.VarCharValue ?? "") ?? [];
|
|
184
|
-
const rows = resultRows.slice(1).map((row) => {
|
|
185
|
-
const obj = {};
|
|
186
|
-
row.Data?.forEach((d, i) => {
|
|
187
|
-
obj[headers[i]] = d.VarCharValue ?? null;
|
|
188
|
-
});
|
|
189
|
-
return obj;
|
|
190
|
-
});
|
|
191
|
-
return { rows };
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// src/connector-client/redshift.ts
|
|
197
|
-
function createRedshiftClient(entry, slug) {
|
|
198
|
-
const region = resolveEnvVar(entry, "aws-region", slug);
|
|
199
|
-
const accessKeyId = resolveEnvVar(entry, "aws-access-key-id", slug);
|
|
200
|
-
const secretAccessKey = resolveEnvVar(entry, "aws-secret-access-key", slug);
|
|
201
|
-
const database = resolveEnvVar(entry, "database", slug);
|
|
202
|
-
const clusterIdentifier = resolveEnvVarOptional(entry, "cluster-identifier");
|
|
203
|
-
const workgroupName = resolveEnvVarOptional(entry, "workgroup-name");
|
|
204
|
-
const secretArn = resolveEnvVarOptional(entry, "secret-arn");
|
|
205
|
-
const dbUser = resolveEnvVarOptional(entry, "db-user");
|
|
206
|
-
return {
|
|
207
|
-
async query(sql) {
|
|
208
|
-
const {
|
|
209
|
-
RedshiftDataClient,
|
|
210
|
-
ExecuteStatementCommand,
|
|
211
|
-
DescribeStatementCommand,
|
|
212
|
-
GetStatementResultCommand
|
|
213
|
-
} = await import("@aws-sdk/client-redshift-data");
|
|
214
|
-
const client = new RedshiftDataClient({
|
|
215
|
-
region,
|
|
216
|
-
credentials: { accessKeyId, secretAccessKey }
|
|
217
|
-
});
|
|
218
|
-
const executeParams = {
|
|
219
|
-
Sql: sql,
|
|
220
|
-
Database: database
|
|
221
|
-
};
|
|
222
|
-
if (clusterIdentifier) executeParams.ClusterIdentifier = clusterIdentifier;
|
|
223
|
-
if (workgroupName) executeParams.WorkgroupName = workgroupName;
|
|
224
|
-
if (secretArn) executeParams.SecretArn = secretArn;
|
|
225
|
-
if (dbUser) executeParams.DbUser = dbUser;
|
|
226
|
-
const { Id } = await client.send(
|
|
227
|
-
new ExecuteStatementCommand(executeParams)
|
|
228
|
-
);
|
|
229
|
-
if (!Id) throw new Error("Redshift: failed to start statement execution");
|
|
230
|
-
while (true) {
|
|
231
|
-
const desc = await client.send(new DescribeStatementCommand({ Id }));
|
|
232
|
-
const status = desc.Status;
|
|
233
|
-
if (status === "FINISHED") break;
|
|
234
|
-
if (status === "FAILED") {
|
|
235
|
-
throw new Error(`Redshift query failed: ${desc.Error ?? "unknown"}`);
|
|
236
|
-
}
|
|
237
|
-
if (status === "ABORTED") throw new Error("Redshift query was aborted");
|
|
238
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
239
|
-
}
|
|
240
|
-
const result = await client.send(new GetStatementResultCommand({ Id }));
|
|
241
|
-
const columns = result.ColumnMetadata?.map((c) => c.name ?? "") ?? [];
|
|
242
|
-
const rows = (result.Records ?? []).map((record) => {
|
|
243
|
-
const obj = {};
|
|
244
|
-
record.forEach((field, i) => {
|
|
245
|
-
const col = columns[i];
|
|
246
|
-
const value = field.stringValue ?? field.longValue ?? field.doubleValue ?? field.booleanValue ?? (field.isNull ? null : field.blobValue ?? null);
|
|
247
|
-
obj[col] = value;
|
|
248
|
-
});
|
|
249
|
-
return obj;
|
|
250
|
-
});
|
|
251
|
-
return { rows };
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// src/connector-client/databricks.ts
|
|
257
|
-
function createDatabricksClient(entry, slug) {
|
|
258
|
-
const host = resolveEnvVar(entry, "host", slug);
|
|
259
|
-
const httpPath = resolveEnvVar(entry, "http-path", slug);
|
|
260
|
-
const token = resolveEnvVar(entry, "token", slug);
|
|
261
|
-
return {
|
|
262
|
-
async query(sql) {
|
|
263
|
-
const { DBSQLClient } = await import("@databricks/sql");
|
|
264
|
-
const client = new DBSQLClient();
|
|
265
|
-
await client.connect({ host, path: httpPath, token });
|
|
266
|
-
try {
|
|
267
|
-
const session = await client.openSession();
|
|
268
|
-
try {
|
|
269
|
-
const operation = await session.executeStatement(sql);
|
|
270
|
-
const result = await operation.fetchAll();
|
|
271
|
-
await operation.close();
|
|
272
|
-
return { rows: result };
|
|
273
|
-
} finally {
|
|
274
|
-
await session.close();
|
|
275
|
-
}
|
|
276
|
-
} finally {
|
|
277
|
-
await client.close();
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
|
|
283
108
|
// src/connector-client/registry.ts
|
|
284
109
|
function createConnectorRegistry() {
|
|
285
110
|
let connectionsCache = null;
|
|
@@ -287,7 +112,7 @@ function createConnectorRegistry() {
|
|
|
287
112
|
function getConnectionsFilePath() {
|
|
288
113
|
return process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), "../../.squadbase/connections.json");
|
|
289
114
|
}
|
|
290
|
-
function
|
|
115
|
+
function loadConnections() {
|
|
291
116
|
if (connectionsCache !== null) return connectionsCache;
|
|
292
117
|
const filePath = getConnectionsFilePath();
|
|
293
118
|
try {
|
|
@@ -311,7 +136,7 @@ function createConnectorRegistry() {
|
|
|
311
136
|
}
|
|
312
137
|
const cached = clientCache.get(connectorSlug);
|
|
313
138
|
if (cached) return cached;
|
|
314
|
-
const connections =
|
|
139
|
+
const connections = loadConnections();
|
|
315
140
|
const entry = connections[connectorSlug];
|
|
316
141
|
if (!entry) {
|
|
317
142
|
throw new Error(`connector slug '${connectorSlug}' not found in .squadbase/connections.json`);
|
|
@@ -328,20 +153,6 @@ function createConnectorRegistry() {
|
|
|
328
153
|
if (resolvedType === "bigquery") {
|
|
329
154
|
return createBigQueryClient(entry, connectorSlug);
|
|
330
155
|
}
|
|
331
|
-
if (resolvedType === "athena") {
|
|
332
|
-
return createAthenaClient(entry, connectorSlug);
|
|
333
|
-
}
|
|
334
|
-
if (resolvedType === "redshift") {
|
|
335
|
-
return createRedshiftClient(entry, connectorSlug);
|
|
336
|
-
}
|
|
337
|
-
if (resolvedType === "databricks") {
|
|
338
|
-
return createDatabricksClient(entry, connectorSlug);
|
|
339
|
-
}
|
|
340
|
-
if (resolvedType === "mysql") {
|
|
341
|
-
const client = createMySQLClient(entry, connectorSlug);
|
|
342
|
-
clientCache.set(connectorSlug, client);
|
|
343
|
-
return client;
|
|
344
|
-
}
|
|
345
156
|
if (resolvedType === "postgresql" || resolvedType === "squadbase-db") {
|
|
346
157
|
const urlEnvName = entry.envVars["connection-url"];
|
|
347
158
|
if (!urlEnvName) {
|
|
@@ -358,7 +169,7 @@ function createConnectorRegistry() {
|
|
|
358
169
|
return client;
|
|
359
170
|
}
|
|
360
171
|
throw new Error(
|
|
361
|
-
`connector type '${resolvedType}' is not supported
|
|
172
|
+
`connector type '${resolvedType}' is not supported. Supported: "snowflake", "bigquery", "postgresql", "squadbase-db"`
|
|
362
173
|
);
|
|
363
174
|
}
|
|
364
175
|
function reloadEnvFile2(envPath) {
|
|
@@ -390,276 +201,15 @@ function createConnectorRegistry() {
|
|
|
390
201
|
} catch {
|
|
391
202
|
}
|
|
392
203
|
}
|
|
393
|
-
return { getClient: getClient2,
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// src/connector-client/airtable.ts
|
|
397
|
-
function createAirtableClient(entry, slug) {
|
|
398
|
-
const baseId = resolveEnvVar(entry, "base-id", slug);
|
|
399
|
-
const apiKey = resolveEnvVar(entry, "api-key", slug);
|
|
400
|
-
const baseUrl = `https://api.airtable.com/v0/${baseId}`;
|
|
401
|
-
const headers = {
|
|
402
|
-
Authorization: `Bearer ${apiKey}`,
|
|
403
|
-
"Content-Type": "application/json"
|
|
404
|
-
};
|
|
405
|
-
return {
|
|
406
|
-
async listRecords(tableIdOrName, options) {
|
|
407
|
-
const params = new URLSearchParams();
|
|
408
|
-
if (options?.filterByFormula) params.set("filterByFormula", options.filterByFormula);
|
|
409
|
-
if (options?.maxRecords) params.set("maxRecords", String(options.maxRecords));
|
|
410
|
-
if (options?.pageSize) params.set("pageSize", String(options.pageSize));
|
|
411
|
-
if (options?.offset) params.set("offset", options.offset);
|
|
412
|
-
options?.fields?.forEach((f) => params.append("fields[]", f));
|
|
413
|
-
options?.sort?.forEach((s, i) => {
|
|
414
|
-
params.set(`sort[${i}][field]`, s.field);
|
|
415
|
-
if (s.direction) params.set(`sort[${i}][direction]`, s.direction);
|
|
416
|
-
});
|
|
417
|
-
const qs = params.toString();
|
|
418
|
-
const url = `${baseUrl}/${encodeURIComponent(tableIdOrName)}${qs ? `?${qs}` : ""}`;
|
|
419
|
-
const res = await fetch(url, { headers });
|
|
420
|
-
if (!res.ok) throw new Error(`Airtable API error: ${res.status} ${await res.text()}`);
|
|
421
|
-
return res.json();
|
|
422
|
-
},
|
|
423
|
-
async getRecord(tableIdOrName, recordId) {
|
|
424
|
-
const url = `${baseUrl}/${encodeURIComponent(tableIdOrName)}/${recordId}`;
|
|
425
|
-
const res = await fetch(url, { headers });
|
|
426
|
-
if (!res.ok) throw new Error(`Airtable API error: ${res.status} ${await res.text()}`);
|
|
427
|
-
return res.json();
|
|
428
|
-
}
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// src/connector-client/google-analytics.ts
|
|
433
|
-
function createGoogleAnalyticsClient(entry, slug) {
|
|
434
|
-
const serviceAccountJsonBase64 = resolveEnvVar(entry, "service-account-json-base64", slug);
|
|
435
|
-
const propertyId = resolveEnvVar(entry, "property-id", slug);
|
|
436
|
-
const serviceAccountJson = Buffer.from(serviceAccountJsonBase64, "base64").toString("utf-8");
|
|
437
|
-
let credentials;
|
|
438
|
-
try {
|
|
439
|
-
credentials = JSON.parse(serviceAccountJson);
|
|
440
|
-
} catch {
|
|
441
|
-
throw new Error(
|
|
442
|
-
`Google Analytics service account JSON (decoded from base64) is not valid JSON for slug "${slug}"`
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
return {
|
|
446
|
-
async runReport(request) {
|
|
447
|
-
const { BetaAnalyticsDataClient } = await import("@google-analytics/data");
|
|
448
|
-
const client = new BetaAnalyticsDataClient({
|
|
449
|
-
credentials: {
|
|
450
|
-
client_email: credentials.client_email,
|
|
451
|
-
private_key: credentials.private_key
|
|
452
|
-
}
|
|
453
|
-
});
|
|
454
|
-
const response = await client.runReport({
|
|
455
|
-
property: `properties/${propertyId}`,
|
|
456
|
-
dateRanges: request.dateRanges,
|
|
457
|
-
dimensions: request.dimensions,
|
|
458
|
-
metrics: request.metrics,
|
|
459
|
-
limit: request.limit != null ? String(request.limit) : void 0,
|
|
460
|
-
offset: request.offset != null ? String(request.offset) : void 0
|
|
461
|
-
});
|
|
462
|
-
const reportResponse = Array.isArray(response) ? response[0] : response;
|
|
463
|
-
const rawRows = reportResponse.rows;
|
|
464
|
-
const rows = (rawRows ?? []).map((row) => ({
|
|
465
|
-
dimensionValues: (row.dimensionValues ?? []).map((d) => ({
|
|
466
|
-
value: d.value ?? ""
|
|
467
|
-
})),
|
|
468
|
-
metricValues: (row.metricValues ?? []).map((m) => ({
|
|
469
|
-
value: m.value ?? ""
|
|
470
|
-
}))
|
|
471
|
-
}));
|
|
472
|
-
return {
|
|
473
|
-
rows,
|
|
474
|
-
rowCount: Number(reportResponse.rowCount ?? 0)
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// src/connector-client/kintone.ts
|
|
481
|
-
function createKintoneClient(entry, slug) {
|
|
482
|
-
const baseUrl = resolveEnvVar(entry, "base-url", slug);
|
|
483
|
-
const username = resolveEnvVar(entry, "username", slug);
|
|
484
|
-
const password = resolveEnvVar(entry, "password", slug);
|
|
485
|
-
return {
|
|
486
|
-
async getRecords(appId, options) {
|
|
487
|
-
const { KintoneRestAPIClient } = await import("@kintone/rest-api-client");
|
|
488
|
-
const client = new KintoneRestAPIClient({
|
|
489
|
-
baseUrl,
|
|
490
|
-
auth: { username, password }
|
|
491
|
-
});
|
|
492
|
-
const result = await client.record.getRecords({
|
|
493
|
-
app: appId,
|
|
494
|
-
query: options?.query,
|
|
495
|
-
fields: options?.fields,
|
|
496
|
-
totalCount: options?.totalCount
|
|
497
|
-
});
|
|
498
|
-
return {
|
|
499
|
-
records: result.records,
|
|
500
|
-
totalCount: result.totalCount
|
|
501
|
-
};
|
|
502
|
-
},
|
|
503
|
-
async getRecord(appId, recordId) {
|
|
504
|
-
const { KintoneRestAPIClient } = await import("@kintone/rest-api-client");
|
|
505
|
-
const client = new KintoneRestAPIClient({
|
|
506
|
-
baseUrl,
|
|
507
|
-
auth: { username, password }
|
|
508
|
-
});
|
|
509
|
-
const result = await client.record.getRecord({
|
|
510
|
-
app: appId,
|
|
511
|
-
id: recordId
|
|
512
|
-
});
|
|
513
|
-
return { record: result.record };
|
|
514
|
-
},
|
|
515
|
-
async listApps(options) {
|
|
516
|
-
const { KintoneRestAPIClient } = await import("@kintone/rest-api-client");
|
|
517
|
-
const client = new KintoneRestAPIClient({
|
|
518
|
-
baseUrl,
|
|
519
|
-
auth: { username, password }
|
|
520
|
-
});
|
|
521
|
-
const result = await client.app.getApps({
|
|
522
|
-
ids: options?.ids,
|
|
523
|
-
name: options?.name,
|
|
524
|
-
limit: options?.limit,
|
|
525
|
-
offset: options?.offset
|
|
526
|
-
});
|
|
527
|
-
return { apps: result.apps };
|
|
528
|
-
}
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// src/connector-client/wix-store.ts
|
|
533
|
-
function createWixStoreClient(entry, slug) {
|
|
534
|
-
const siteId = resolveEnvVar(entry, "site-id", slug);
|
|
535
|
-
const apiKey = resolveEnvVar(entry, "api-key", slug);
|
|
536
|
-
const headers = {
|
|
537
|
-
Authorization: apiKey,
|
|
538
|
-
"wix-site-id": siteId,
|
|
539
|
-
"Content-Type": "application/json"
|
|
540
|
-
};
|
|
541
|
-
return {
|
|
542
|
-
async queryProducts(options) {
|
|
543
|
-
const body = {};
|
|
544
|
-
if (options?.query) body.query = options.query;
|
|
545
|
-
if (options?.limit) {
|
|
546
|
-
body.query = { ...body.query ?? {}, paging: { limit: options.limit, offset: options?.offset ?? 0 } };
|
|
547
|
-
}
|
|
548
|
-
const res = await fetch(
|
|
549
|
-
"https://www.wixapis.com/stores/v1/products/query",
|
|
550
|
-
{ method: "POST", headers, body: JSON.stringify(body) }
|
|
551
|
-
);
|
|
552
|
-
if (!res.ok) throw new Error(`Wix Store API error: ${res.status} ${await res.text()}`);
|
|
553
|
-
const data = await res.json();
|
|
554
|
-
return { products: data.products ?? [], totalResults: data.totalResults ?? 0 };
|
|
555
|
-
},
|
|
556
|
-
async queryOrders(options) {
|
|
557
|
-
const body = {};
|
|
558
|
-
if (options?.query) body.query = options.query;
|
|
559
|
-
if (options?.limit) {
|
|
560
|
-
body.query = { ...body.query ?? {}, paging: { limit: options.limit, offset: options?.offset ?? 0 } };
|
|
561
|
-
}
|
|
562
|
-
const res = await fetch(
|
|
563
|
-
"https://www.wixapis.com/stores/v2/orders/query",
|
|
564
|
-
{ method: "POST", headers, body: JSON.stringify(body) }
|
|
565
|
-
);
|
|
566
|
-
if (!res.ok) throw new Error(`Wix Store API error: ${res.status} ${await res.text()}`);
|
|
567
|
-
const data = await res.json();
|
|
568
|
-
return { orders: data.orders ?? [], totalResults: data.totalResults ?? 0 };
|
|
569
|
-
}
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// src/connector-client/dbt.ts
|
|
574
|
-
function createDbtClient(entry, slug) {
|
|
575
|
-
const host = resolveEnvVar(entry, "host", slug);
|
|
576
|
-
const prodEnvId = resolveEnvVar(entry, "prod-env-id", slug);
|
|
577
|
-
const token = resolveEnvVar(entry, "token", slug);
|
|
578
|
-
const discoveryUrl = `https://${host}/graphql`;
|
|
579
|
-
const headers = {
|
|
580
|
-
Authorization: `Bearer ${token}`,
|
|
581
|
-
"Content-Type": "application/json"
|
|
582
|
-
};
|
|
583
|
-
async function gqlRequest(query, variables) {
|
|
584
|
-
const res = await fetch(discoveryUrl, {
|
|
585
|
-
method: "POST",
|
|
586
|
-
headers,
|
|
587
|
-
body: JSON.stringify({ query, variables })
|
|
588
|
-
});
|
|
589
|
-
if (!res.ok) throw new Error(`dbt Discovery API error: ${res.status} ${await res.text()}`);
|
|
590
|
-
const json = await res.json();
|
|
591
|
-
if (json.errors) {
|
|
592
|
-
throw new Error(`dbt Discovery API GraphQL error: ${JSON.stringify(json.errors)}`);
|
|
593
|
-
}
|
|
594
|
-
return json.data ?? {};
|
|
595
|
-
}
|
|
596
|
-
return {
|
|
597
|
-
query: gqlRequest,
|
|
598
|
-
async getModels(options) {
|
|
599
|
-
const limit = options?.limit ?? 100;
|
|
600
|
-
const data = await gqlRequest(
|
|
601
|
-
`query ($environmentId: BigInt!, $first: Int) {
|
|
602
|
-
environment(id: $environmentId) {
|
|
603
|
-
applied {
|
|
604
|
-
models(first: $first) {
|
|
605
|
-
edges {
|
|
606
|
-
node {
|
|
607
|
-
uniqueId
|
|
608
|
-
name
|
|
609
|
-
description
|
|
610
|
-
materializedType
|
|
611
|
-
database
|
|
612
|
-
schema
|
|
613
|
-
alias
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}`,
|
|
620
|
-
{ environmentId: Number(prodEnvId), first: limit }
|
|
621
|
-
);
|
|
622
|
-
const env = data.environment;
|
|
623
|
-
const applied = env?.applied;
|
|
624
|
-
const models = applied?.models;
|
|
625
|
-
return models?.edges.map((e) => e.node) ?? [];
|
|
626
|
-
},
|
|
627
|
-
async getModelByName(uniqueId) {
|
|
628
|
-
const data = await gqlRequest(
|
|
629
|
-
`query ($environmentId: BigInt!, $uniqueId: String!) {
|
|
630
|
-
environment(id: $environmentId) {
|
|
631
|
-
applied {
|
|
632
|
-
modelByUniqueId(uniqueId: $uniqueId) {
|
|
633
|
-
uniqueId
|
|
634
|
-
name
|
|
635
|
-
description
|
|
636
|
-
materializedType
|
|
637
|
-
database
|
|
638
|
-
schema
|
|
639
|
-
alias
|
|
640
|
-
columns {
|
|
641
|
-
name
|
|
642
|
-
description
|
|
643
|
-
type
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
}`,
|
|
649
|
-
{ environmentId: Number(prodEnvId), uniqueId }
|
|
650
|
-
);
|
|
651
|
-
const env = data.environment;
|
|
652
|
-
const applied = env?.applied;
|
|
653
|
-
return applied?.modelByUniqueId ?? null;
|
|
654
|
-
}
|
|
655
|
-
};
|
|
204
|
+
return { getClient: getClient2, reloadEnvFile: reloadEnvFile2, watchConnectionsFile: watchConnectionsFile2 };
|
|
656
205
|
}
|
|
657
206
|
|
|
658
207
|
// src/connector-client/index.ts
|
|
659
|
-
var { getClient,
|
|
208
|
+
var { getClient, reloadEnvFile, watchConnectionsFile } = createConnectorRegistry();
|
|
660
209
|
|
|
661
210
|
// src/registry.ts
|
|
662
211
|
var dataSources = /* @__PURE__ */ new Map();
|
|
212
|
+
var currentDirPath = "";
|
|
663
213
|
var viteServer = null;
|
|
664
214
|
function validateHandlerPath(dirPath, handlerPath) {
|
|
665
215
|
const absolute = path2.resolve(dirPath, handlerPath);
|
|
@@ -714,6 +264,7 @@ async function initialize() {
|
|
|
714
264
|
);
|
|
715
265
|
dataSources.clear();
|
|
716
266
|
const dirPath = process.env.DATA_SOURCE_DIR || defaultDataSourceDir;
|
|
267
|
+
currentDirPath = dirPath;
|
|
717
268
|
await mkdir(dirPath, { recursive: true });
|
|
718
269
|
const files = await readdir(dirPath);
|
|
719
270
|
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
@@ -757,12 +308,14 @@ async function initialize() {
|
|
|
757
308
|
response: sqlDef.response,
|
|
758
309
|
connectorSlug: sqlDef.connectorSlug,
|
|
759
310
|
cacheConfig: sqlDef.cache,
|
|
311
|
+
_query: sqlDef.query,
|
|
312
|
+
_connectorType: sqlDef.connectorType,
|
|
760
313
|
handler: async (runtimeParams) => {
|
|
761
314
|
const client = await getClient(sqlDef.connectorSlug, sqlDef.connectorType);
|
|
762
|
-
const
|
|
315
|
+
const isExternalConnector = sqlDef.connectorType === "snowflake" || sqlDef.connectorType === "bigquery";
|
|
763
316
|
let queryText;
|
|
764
317
|
let queryValues;
|
|
765
|
-
if (
|
|
318
|
+
if (isExternalConnector) {
|
|
766
319
|
const defaults = new Map(
|
|
767
320
|
(sqlDef.parameters ?? []).map((p) => [p.name, p.default ?? null])
|
|
768
321
|
);
|
|
@@ -780,14 +333,6 @@ async function initialize() {
|
|
|
780
333
|
}
|
|
781
334
|
);
|
|
782
335
|
queryValues = [];
|
|
783
|
-
} else if (sqlDef.connectorType === "mysql") {
|
|
784
|
-
const built = buildQuery(
|
|
785
|
-
sqlDef.query,
|
|
786
|
-
sqlDef.parameters ?? [],
|
|
787
|
-
runtimeParams
|
|
788
|
-
);
|
|
789
|
-
queryText = built.text.replace(/\$(\d+)/g, "?");
|
|
790
|
-
queryValues = built.values;
|
|
791
336
|
} else {
|
|
792
337
|
const built = buildQuery(
|
|
793
338
|
sqlDef.query,
|
|
@@ -838,25 +383,29 @@ function startWatching() {
|
|
|
838
383
|
function getDataSource(slug) {
|
|
839
384
|
return dataSources.get(slug);
|
|
840
385
|
}
|
|
841
|
-
function
|
|
842
|
-
return
|
|
386
|
+
function buildMeta(slug, def) {
|
|
387
|
+
return {
|
|
843
388
|
slug,
|
|
844
389
|
description: def.description,
|
|
390
|
+
type: def._isTypescript ? "typescript" : "sql",
|
|
845
391
|
parameters: def.parameters,
|
|
846
392
|
response: def.response,
|
|
847
|
-
|
|
848
|
-
|
|
393
|
+
query: def._query,
|
|
394
|
+
connectorType: def._connectorType,
|
|
395
|
+
connectorSlug: def.connectorSlug,
|
|
396
|
+
handlerPath: def._tsHandlerPath ? path2.relative(currentDirPath, def._tsHandlerPath) : void 0,
|
|
397
|
+
cache: def.cacheConfig
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function getAllMeta() {
|
|
401
|
+
return Array.from(dataSources.entries()).map(
|
|
402
|
+
([slug, def]) => buildMeta(slug, def)
|
|
403
|
+
);
|
|
849
404
|
}
|
|
850
405
|
function getMeta(slug) {
|
|
851
406
|
const def = dataSources.get(slug);
|
|
852
407
|
if (!def) return void 0;
|
|
853
|
-
return
|
|
854
|
-
slug,
|
|
855
|
-
description: def.description,
|
|
856
|
-
parameters: def.parameters,
|
|
857
|
-
response: def.response,
|
|
858
|
-
connectorSlug: def.connectorSlug
|
|
859
|
-
};
|
|
408
|
+
return buildMeta(slug, def);
|
|
860
409
|
}
|
|
861
410
|
|
|
862
411
|
// src/routes/data-source.ts
|
|
@@ -1198,12 +747,5 @@ app5.get("/healthz", (c) => c.json({ status: "ok" }));
|
|
|
1198
747
|
app5.route("/api", apiApp);
|
|
1199
748
|
var src_default = app5;
|
|
1200
749
|
export {
|
|
1201
|
-
|
|
1202
|
-
createDbtClient,
|
|
1203
|
-
createGoogleAnalyticsClient,
|
|
1204
|
-
createKintoneClient,
|
|
1205
|
-
createWixStoreClient,
|
|
1206
|
-
src_default as default,
|
|
1207
|
-
getClient,
|
|
1208
|
-
loadConnections
|
|
750
|
+
src_default as default
|
|
1209
751
|
};
|