@squadbase/vite-server 0.1.12-dev.a9ac647 → 0.1.17-dev.3b633bb
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 +14375 -1652
- package/dist/connectors/airtable-oauth.js +282 -46
- package/dist/connectors/airtable.js +319 -51
- package/dist/connectors/amplitude.js +322 -47
- package/dist/connectors/anthropic.js +135 -47
- package/dist/connectors/asana.js +327 -49
- package/dist/connectors/attio.js +302 -49
- package/dist/connectors/aws-billing.js +287 -46
- package/dist/connectors/azure-sql.js +421 -102
- package/dist/connectors/backlog-api-key.js +317 -47
- package/dist/connectors/clickup.js +338 -49
- package/dist/connectors/cosmosdb.js +305 -50
- package/dist/connectors/customerio.js +319 -47
- package/dist/connectors/dbt.js +340 -47
- package/dist/connectors/freshdesk.js +342 -53
- package/dist/connectors/freshsales.js +333 -52
- package/dist/connectors/freshservice.js +361 -53
- package/dist/connectors/gamma.js +327 -52
- package/dist/connectors/gemini.js +134 -47
- package/dist/connectors/github.js +386 -49
- package/dist/connectors/gmail-oauth.js +204 -7
- package/dist/connectors/gmail.js +350 -47
- package/dist/connectors/google-ads.js +288 -46
- package/dist/connectors/google-analytics-oauth.js +310 -46
- package/dist/connectors/google-analytics.js +547 -87
- package/dist/connectors/google-audit-log.js +438 -47
- package/dist/connectors/google-calendar-oauth.js +259 -46
- package/dist/connectors/google-calendar.js +359 -47
- package/dist/connectors/google-docs.js +220 -6
- package/dist/connectors/google-drive.js +262 -5
- package/dist/connectors/google-search-console-oauth.js +256 -46
- package/dist/connectors/google-sheets.js +272 -47
- package/dist/connectors/google-slides.js +205 -6
- package/dist/connectors/grafana.js +332 -49
- package/dist/connectors/hubspot-oauth.js +208 -5
- package/dist/connectors/hubspot.js +306 -49
- package/dist/connectors/influxdb.js +416 -51
- package/dist/connectors/intercom-oauth.js +210 -5
- package/dist/connectors/intercom.js +302 -49
- package/dist/connectors/jdbc.js +762 -110
- package/dist/connectors/jira-api-key.js +326 -47
- package/dist/connectors/kintone-api-token.js +281 -47
- package/dist/connectors/kintone.js +328 -47
- package/dist/connectors/linear.js +330 -49
- package/dist/connectors/linkedin-ads.js +268 -50
- package/dist/connectors/mailchimp-oauth.js +268 -46
- package/dist/connectors/mailchimp.js +320 -49
- package/dist/connectors/meta-ads-oauth.js +273 -48
- package/dist/connectors/meta-ads.js +285 -50
- package/dist/connectors/mixpanel.js +338 -47
- package/dist/connectors/monday.js +360 -49
- package/dist/connectors/mongodb.js +319 -57
- package/dist/connectors/notion-oauth.js +231 -5
- package/dist/connectors/notion.js +323 -51
- package/dist/connectors/openai.js +134 -47
- package/dist/connectors/oracle.js +454 -103
- package/dist/connectors/outlook-oauth.js +204 -5
- package/dist/connectors/powerbi-oauth.js +498 -5
- package/dist/connectors/salesforce.js +384 -49
- package/dist/connectors/semrush.js +609 -49
- package/dist/connectors/sentry.js +289 -50
- package/dist/connectors/shopify-oauth.js +187 -5
- package/dist/connectors/shopify.js +357 -47
- package/dist/connectors/sqlserver.js +415 -102
- package/dist/connectors/stripe-api-key.js +269 -46
- package/dist/connectors/stripe-oauth.js +202 -5
- package/dist/connectors/supabase.js +303 -48
- package/dist/connectors/tableau.js +536 -163
- package/dist/connectors/tiktok-ads.js +279 -48
- package/dist/connectors/wix-store.js +320 -49
- package/dist/connectors/zendesk-oauth.js +239 -5
- package/dist/connectors/zendesk.js +358 -47
- package/dist/index.d.ts +149 -1
- package/dist/index.js +15057 -2117
- package/dist/main.js +15005 -2073
- package/dist/vite-plugin.js +14752 -2019
- package/package.json +1 -1
|
@@ -1,48 +1,60 @@
|
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __esm = (fn, res) => function __init() {
|
|
3
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
4
|
+
};
|
|
5
|
+
|
|
1
6
|
// ../connectors/src/parameter-definition.ts
|
|
2
|
-
var ParameterDefinition
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
7
|
+
var ParameterDefinition;
|
|
8
|
+
var init_parameter_definition = __esm({
|
|
9
|
+
"../connectors/src/parameter-definition.ts"() {
|
|
10
|
+
"use strict";
|
|
11
|
+
ParameterDefinition = class {
|
|
12
|
+
slug;
|
|
13
|
+
name;
|
|
14
|
+
description;
|
|
15
|
+
envVarBaseKey;
|
|
16
|
+
type;
|
|
17
|
+
secret;
|
|
18
|
+
required;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.slug = config.slug;
|
|
21
|
+
this.name = config.name;
|
|
22
|
+
this.description = config.description;
|
|
23
|
+
this.envVarBaseKey = config.envVarBaseKey;
|
|
24
|
+
this.type = config.type;
|
|
25
|
+
this.secret = config.secret;
|
|
26
|
+
this.required = config.required;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the parameter value from a ConnectorConnectionObject.
|
|
30
|
+
*/
|
|
31
|
+
getValue(connection2) {
|
|
32
|
+
const param = connection2.parameters.find(
|
|
33
|
+
(p) => p.parameterSlug === this.slug
|
|
34
|
+
);
|
|
35
|
+
if (!param || param.value == null) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return param.value;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Try to get the parameter value. Returns undefined if not found (for optional params).
|
|
44
|
+
*/
|
|
45
|
+
tryGetValue(connection2) {
|
|
46
|
+
const param = connection2.parameters.find(
|
|
47
|
+
(p) => p.parameterSlug === this.slug
|
|
48
|
+
);
|
|
49
|
+
if (!param || param.value == null) return void 0;
|
|
50
|
+
return param.value;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
42
53
|
}
|
|
43
|
-
};
|
|
54
|
+
});
|
|
44
55
|
|
|
45
56
|
// ../connectors/src/connectors/influxdb/parameters.ts
|
|
57
|
+
init_parameter_definition();
|
|
46
58
|
var parameters = {
|
|
47
59
|
url: new ParameterDefinition({
|
|
48
60
|
slug: "url",
|
|
@@ -103,7 +115,7 @@ function createClient(params) {
|
|
|
103
115
|
`influxdb: missing required parameter: ${parameters.database.slug}`
|
|
104
116
|
);
|
|
105
117
|
}
|
|
106
|
-
function
|
|
118
|
+
function authHeaders2(extra) {
|
|
107
119
|
const headers = new Headers(extra);
|
|
108
120
|
headers.set("Authorization", `Token ${token}`);
|
|
109
121
|
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
@@ -139,7 +151,7 @@ function createClient(params) {
|
|
|
139
151
|
async querySql(sql, options) {
|
|
140
152
|
const res = await fetch(`${url}/api/v3/query_sql`, {
|
|
141
153
|
method: "POST",
|
|
142
|
-
headers:
|
|
154
|
+
headers: authHeaders2({ "Content-Type": "application/json" }),
|
|
143
155
|
body: JSON.stringify({
|
|
144
156
|
db: options?.database ?? database,
|
|
145
157
|
q: sql
|
|
@@ -151,7 +163,7 @@ function createClient(params) {
|
|
|
151
163
|
async queryInfluxql(influxql, options) {
|
|
152
164
|
const res = await fetch(`${url}/api/v3/query_influxql`, {
|
|
153
165
|
method: "POST",
|
|
154
|
-
headers:
|
|
166
|
+
headers: authHeaders2({ "Content-Type": "application/json" }),
|
|
155
167
|
body: JSON.stringify({
|
|
156
168
|
db: options?.database ?? database,
|
|
157
169
|
q: influxql
|
|
@@ -171,7 +183,7 @@ function createClient(params) {
|
|
|
171
183
|
`${url}/api/v2/query?org=${encodeURIComponent(resolvedOrg)}`,
|
|
172
184
|
{
|
|
173
185
|
method: "POST",
|
|
174
|
-
headers:
|
|
186
|
+
headers: authHeaders2({
|
|
175
187
|
"Content-Type": "application/vnd.flux",
|
|
176
188
|
Accept: "application/csv"
|
|
177
189
|
}),
|
|
@@ -188,7 +200,7 @@ function createClient(params) {
|
|
|
188
200
|
const endpoint = resolvedOrg ? `${url}/api/v2/write?org=${encodeURIComponent(resolvedOrg)}&bucket=${encodeURIComponent(resolvedDatabase)}&precision=${precision}` : `${url}/api/v3/write_lp?db=${encodeURIComponent(resolvedDatabase)}&precision=${precision}`;
|
|
189
201
|
const res = await fetch(endpoint, {
|
|
190
202
|
method: "POST",
|
|
191
|
-
headers:
|
|
203
|
+
headers: authHeaders2({ "Content-Type": "text/plain; charset=utf-8" }),
|
|
192
204
|
body: lineProtocol
|
|
193
205
|
});
|
|
194
206
|
await assertOk(res, "writeLineProtocol");
|
|
@@ -255,6 +267,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
255
267
|
tools;
|
|
256
268
|
query;
|
|
257
269
|
checkConnection;
|
|
270
|
+
/**
|
|
271
|
+
* SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
|
|
272
|
+
* implement this expose a step-by-step exploration flow (database/schema/
|
|
273
|
+
* table/etc. discovery) that the dashboard backend drives via the
|
|
274
|
+
* `/connections/:connectionId/setup` endpoint. Implement by delegating to
|
|
275
|
+
* `runSetupFlow` from `setup-flow.ts`.
|
|
276
|
+
*/
|
|
277
|
+
setup;
|
|
278
|
+
/**
|
|
279
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
280
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
281
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
282
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
283
|
+
*
|
|
284
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
285
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
286
|
+
* connectionId, which doesn't exist until the row is saved
|
|
287
|
+
*
|
|
288
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
289
|
+
* the default verify-on-create behavior without opt-in.
|
|
290
|
+
*/
|
|
291
|
+
skipConnectionCheckOnCreate;
|
|
258
292
|
constructor(config) {
|
|
259
293
|
this.slug = config.slug;
|
|
260
294
|
this.authType = config.authType;
|
|
@@ -271,6 +305,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
271
305
|
this.tools = config.tools;
|
|
272
306
|
this.query = config.query;
|
|
273
307
|
this.checkConnection = config.checkConnection;
|
|
308
|
+
this.setup = config.setup;
|
|
309
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
274
310
|
}
|
|
275
311
|
get connectorKey() {
|
|
276
312
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -335,6 +371,76 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
335
371
|
}
|
|
336
372
|
};
|
|
337
373
|
|
|
374
|
+
// ../connectors/src/setup-flow.ts
|
|
375
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
376
|
+
const runtime = {
|
|
377
|
+
params,
|
|
378
|
+
language: ctx.language,
|
|
379
|
+
config
|
|
380
|
+
};
|
|
381
|
+
let state = flow.initialState();
|
|
382
|
+
let answerIdx = 0;
|
|
383
|
+
const pendingParameterUpdates = [];
|
|
384
|
+
for (const step of flow.steps) {
|
|
385
|
+
const ans = ctx.answers[answerIdx];
|
|
386
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
387
|
+
state = step.applyAnswer(state, ans.answer);
|
|
388
|
+
if (step.toParameterUpdates) {
|
|
389
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
390
|
+
}
|
|
391
|
+
answerIdx += 1;
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
395
|
+
if (step.type === "text") {
|
|
396
|
+
if (step.fetchOptions) {
|
|
397
|
+
const options2 = await step.fetchOptions(state, runtime);
|
|
398
|
+
if (options2.length === 0) {
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
type: "nextQuestion",
|
|
404
|
+
questionSlug: step.slug,
|
|
405
|
+
question: step.question[ctx.language],
|
|
406
|
+
questionType: "text",
|
|
407
|
+
allowFreeText: resolvedAllowFreeText,
|
|
408
|
+
...pendingParameterUpdates.length > 0 && {
|
|
409
|
+
parameterUpdates: pendingParameterUpdates
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
414
|
+
if (options.length === 0) {
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
type: "nextQuestion",
|
|
419
|
+
questionSlug: step.slug,
|
|
420
|
+
question: step.question[ctx.language],
|
|
421
|
+
questionType: step.type,
|
|
422
|
+
options,
|
|
423
|
+
allowFreeText: resolvedAllowFreeText,
|
|
424
|
+
...pendingParameterUpdates.length > 0 && {
|
|
425
|
+
parameterUpdates: pendingParameterUpdates
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
430
|
+
return {
|
|
431
|
+
type: "fulfilled",
|
|
432
|
+
dataInvestigationResult,
|
|
433
|
+
...pendingParameterUpdates.length > 0 && {
|
|
434
|
+
parameterUpdates: pendingParameterUpdates
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
async function resolveSetupSelection(params) {
|
|
439
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
440
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
441
|
+
return resolved.slice(0, limit);
|
|
442
|
+
}
|
|
443
|
+
|
|
338
444
|
// ../connectors/src/auth-types.ts
|
|
339
445
|
var AUTH_TYPES = {
|
|
340
446
|
OAUTH: "oauth",
|
|
@@ -403,6 +509,244 @@ var influxdbOnboarding = new ConnectorOnboarding({
|
|
|
403
509
|
}
|
|
404
510
|
});
|
|
405
511
|
|
|
512
|
+
// ../connectors/src/connectors/influxdb/utils.ts
|
|
513
|
+
function authHeaders(token, extra) {
|
|
514
|
+
const headers = new Headers(extra);
|
|
515
|
+
headers.set("Authorization", `Token ${token}`);
|
|
516
|
+
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
517
|
+
return headers;
|
|
518
|
+
}
|
|
519
|
+
async function detectVariant(params) {
|
|
520
|
+
const url = (params[parameters.url.slug] ?? "").replace(/\/$/, "");
|
|
521
|
+
const token = params[parameters.token.slug];
|
|
522
|
+
const database = params[parameters.database.slug];
|
|
523
|
+
const res = await fetch(`${url}/api/v3/query_sql`, {
|
|
524
|
+
method: "POST",
|
|
525
|
+
headers: authHeaders(token, { "Content-Type": "application/json" }),
|
|
526
|
+
body: JSON.stringify({ db: database, q: "SELECT 1" })
|
|
527
|
+
});
|
|
528
|
+
if (res.ok) {
|
|
529
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
530
|
+
if (contentType.includes("application/json")) return "v3";
|
|
531
|
+
}
|
|
532
|
+
return "v2";
|
|
533
|
+
}
|
|
534
|
+
async function runSql(params, sql) {
|
|
535
|
+
const url = (params[parameters.url.slug] ?? "").replace(/\/$/, "");
|
|
536
|
+
const token = params[parameters.token.slug];
|
|
537
|
+
const database = params[parameters.database.slug];
|
|
538
|
+
const res = await fetch(`${url}/api/v3/query_sql`, {
|
|
539
|
+
method: "POST",
|
|
540
|
+
headers: authHeaders(token, { "Content-Type": "application/json" }),
|
|
541
|
+
body: JSON.stringify({ db: database, q: sql })
|
|
542
|
+
});
|
|
543
|
+
if (!res.ok) {
|
|
544
|
+
const body = await res.text().catch(() => res.statusText);
|
|
545
|
+
throw new Error(
|
|
546
|
+
`InfluxDB SQL query failed: HTTP ${res.status} \u2014 ${body.slice(0, 200)}`
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
return await res.json();
|
|
550
|
+
}
|
|
551
|
+
async function runFlux(params, flux) {
|
|
552
|
+
const url = (params[parameters.url.slug] ?? "").replace(/\/$/, "");
|
|
553
|
+
const token = params[parameters.token.slug];
|
|
554
|
+
const org = params[parameters.org.slug];
|
|
555
|
+
if (!org) {
|
|
556
|
+
throw new Error(
|
|
557
|
+
"InfluxDB 2 Flux queries require the 'org' connection parameter, which is not configured"
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
const res = await fetch(
|
|
561
|
+
`${url}/api/v2/query?org=${encodeURIComponent(org)}`,
|
|
562
|
+
{
|
|
563
|
+
method: "POST",
|
|
564
|
+
headers: authHeaders(token, {
|
|
565
|
+
"Content-Type": "application/vnd.flux",
|
|
566
|
+
Accept: "application/csv"
|
|
567
|
+
}),
|
|
568
|
+
body: flux
|
|
569
|
+
}
|
|
570
|
+
);
|
|
571
|
+
if (!res.ok) {
|
|
572
|
+
const body = await res.text().catch(() => res.statusText);
|
|
573
|
+
throw new Error(
|
|
574
|
+
`InfluxDB Flux query failed: HTTP ${res.status} \u2014 ${body.slice(0, 200)}`
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
return await res.text();
|
|
578
|
+
}
|
|
579
|
+
function parseAnnotatedCsv(csv) {
|
|
580
|
+
const lines = csv.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
581
|
+
const rows = [];
|
|
582
|
+
let headers = null;
|
|
583
|
+
for (const line of lines) {
|
|
584
|
+
if (line.startsWith("#")) {
|
|
585
|
+
headers = null;
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
const fields = splitCsvLine(line);
|
|
589
|
+
if (headers === null) {
|
|
590
|
+
headers = fields;
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
const row = {};
|
|
594
|
+
headers.forEach((h, i) => {
|
|
595
|
+
row[h] = fields[i] ?? "";
|
|
596
|
+
});
|
|
597
|
+
rows.push(row);
|
|
598
|
+
}
|
|
599
|
+
return { columns: headers ?? [], rows };
|
|
600
|
+
}
|
|
601
|
+
function splitCsvLine(line) {
|
|
602
|
+
const out = [];
|
|
603
|
+
let cur = "";
|
|
604
|
+
let inQuotes = false;
|
|
605
|
+
for (let i = 0; i < line.length; i++) {
|
|
606
|
+
const c = line[i];
|
|
607
|
+
if (inQuotes) {
|
|
608
|
+
if (c === '"' && line[i + 1] === '"') {
|
|
609
|
+
cur += '"';
|
|
610
|
+
i += 1;
|
|
611
|
+
} else if (c === '"') {
|
|
612
|
+
inQuotes = false;
|
|
613
|
+
} else {
|
|
614
|
+
cur += c;
|
|
615
|
+
}
|
|
616
|
+
} else if (c === '"') {
|
|
617
|
+
inQuotes = true;
|
|
618
|
+
} else if (c === ",") {
|
|
619
|
+
out.push(cur);
|
|
620
|
+
cur = "";
|
|
621
|
+
} else {
|
|
622
|
+
cur += c;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
out.push(cur);
|
|
626
|
+
return out;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// ../connectors/src/connectors/influxdb/setup-flow.ts
|
|
630
|
+
var ALL_MEASUREMENTS = "__ALL_MEASUREMENTS__";
|
|
631
|
+
var INFLUXDB_SETUP_MAX_MEASUREMENTS = 20;
|
|
632
|
+
async function listMeasurements(params, variant) {
|
|
633
|
+
if (variant === "v3") {
|
|
634
|
+
const rows2 = await runSql(params, "SHOW TABLES");
|
|
635
|
+
return rows2.map((r) => String(r["table_name"] ?? r["name"] ?? "")).filter((name) => name).filter((name) => !name.includes("::"));
|
|
636
|
+
}
|
|
637
|
+
const bucket = params[parameters.database.slug];
|
|
638
|
+
if (!bucket) {
|
|
639
|
+
throw new Error(
|
|
640
|
+
"InfluxDB 2 setup: 'database' (bucket) parameter is required to list measurements"
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
const flux = [
|
|
644
|
+
`import "influxdata/influxdb/schema"`,
|
|
645
|
+
`schema.measurements(bucket: ${JSON.stringify(bucket)})`
|
|
646
|
+
].join("\n");
|
|
647
|
+
const csv = await runFlux(params, flux);
|
|
648
|
+
const { rows } = parseAnnotatedCsv(csv);
|
|
649
|
+
return rows.map((r) => r["_value"] ?? "").filter((name) => name);
|
|
650
|
+
}
|
|
651
|
+
async function listMeasurementColumns(params, variant, measurement) {
|
|
652
|
+
if (variant === "v3") {
|
|
653
|
+
const safeName2 = measurement.replace(/"/g, '""');
|
|
654
|
+
const rows = await runSql(params, `SHOW COLUMNS FROM "${safeName2}"`);
|
|
655
|
+
return rows.map((r) => ({
|
|
656
|
+
name: String(r["column_name"] ?? r["name"] ?? ""),
|
|
657
|
+
kind: String(r["data_type"] ?? r["type"] ?? "")
|
|
658
|
+
}));
|
|
659
|
+
}
|
|
660
|
+
const bucket = params[parameters.database.slug];
|
|
661
|
+
const safeName = measurement.replace(/"/g, '\\"');
|
|
662
|
+
const fieldsFlux = [
|
|
663
|
+
`import "influxdata/influxdb/schema"`,
|
|
664
|
+
`schema.measurementFieldKeys(bucket: ${JSON.stringify(bucket)}, measurement: "${safeName}")`
|
|
665
|
+
].join("\n");
|
|
666
|
+
const tagsFlux = [
|
|
667
|
+
`import "influxdata/influxdb/schema"`,
|
|
668
|
+
`schema.measurementTagKeys(bucket: ${JSON.stringify(bucket)}, measurement: "${safeName}")`
|
|
669
|
+
].join("\n");
|
|
670
|
+
const [fieldsCsv, tagsCsv] = await Promise.all([
|
|
671
|
+
runFlux(params, fieldsFlux),
|
|
672
|
+
runFlux(params, tagsFlux)
|
|
673
|
+
]);
|
|
674
|
+
const fields = parseAnnotatedCsv(fieldsCsv).rows.map((r) => ({
|
|
675
|
+
name: r["_value"] ?? "",
|
|
676
|
+
kind: "field"
|
|
677
|
+
}));
|
|
678
|
+
const tags = parseAnnotatedCsv(tagsCsv).rows.map((r) => ({
|
|
679
|
+
name: r["_value"] ?? "",
|
|
680
|
+
kind: "tag"
|
|
681
|
+
}));
|
|
682
|
+
return [...tags, ...fields].filter((c) => c.name);
|
|
683
|
+
}
|
|
684
|
+
var influxdbSetupFlow = {
|
|
685
|
+
initialState: () => ({}),
|
|
686
|
+
steps: [
|
|
687
|
+
{
|
|
688
|
+
slug: "measurements",
|
|
689
|
+
type: "multiSelect",
|
|
690
|
+
question: {
|
|
691
|
+
ja: "\u5BFE\u8C61 measurement \u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
692
|
+
en: "Select target measurements (multi-select allowed)"
|
|
693
|
+
},
|
|
694
|
+
async fetchOptions(_state, rt) {
|
|
695
|
+
const variant = await detectVariant(rt.params);
|
|
696
|
+
const ms = await listMeasurements(rt.params, variant);
|
|
697
|
+
const options = ms.map((value) => ({ value }));
|
|
698
|
+
return [
|
|
699
|
+
{
|
|
700
|
+
value: ALL_MEASUREMENTS,
|
|
701
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E measurement" : "All measurements"
|
|
702
|
+
},
|
|
703
|
+
...options
|
|
704
|
+
];
|
|
705
|
+
},
|
|
706
|
+
applyAnswer: (state, answer) => ({ ...state, measurements: answer })
|
|
707
|
+
}
|
|
708
|
+
],
|
|
709
|
+
async finalize(state, rt) {
|
|
710
|
+
if (!state.measurements) {
|
|
711
|
+
throw new Error("InfluxDB setup: incomplete state on finalize");
|
|
712
|
+
}
|
|
713
|
+
const variant = await detectVariant(rt.params);
|
|
714
|
+
const database = rt.params[parameters.database.slug];
|
|
715
|
+
const org = rt.params[parameters.org.slug];
|
|
716
|
+
const targetMeasurements = await resolveSetupSelection({
|
|
717
|
+
selected: state.measurements,
|
|
718
|
+
allSentinel: ALL_MEASUREMENTS,
|
|
719
|
+
fetchAll: () => listMeasurements(rt.params, variant),
|
|
720
|
+
limit: INFLUXDB_SETUP_MAX_MEASUREMENTS
|
|
721
|
+
});
|
|
722
|
+
const sections = [
|
|
723
|
+
"## InfluxDB",
|
|
724
|
+
"",
|
|
725
|
+
`### Variant: ${variant === "v3" ? "InfluxDB 3 (SQL)" : "InfluxDB 2 (Flux)"}`,
|
|
726
|
+
`### ${variant === "v3" ? "Database" : "Bucket"}: ${database}`
|
|
727
|
+
];
|
|
728
|
+
if (variant === "v2" && org) {
|
|
729
|
+
sections.push(`### Organization: ${org}`);
|
|
730
|
+
}
|
|
731
|
+
sections.push("");
|
|
732
|
+
for (const measurement of targetMeasurements) {
|
|
733
|
+
const cols = await listMeasurementColumns(rt.params, variant, measurement);
|
|
734
|
+
sections.push(`#### Measurement: ${measurement}`, "");
|
|
735
|
+
if (cols.length === 0) {
|
|
736
|
+
sections.push("_(no fields or tags found)_", "");
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
sections.push("| Column | Kind |");
|
|
740
|
+
sections.push("|--------|------|");
|
|
741
|
+
for (const c of cols) {
|
|
742
|
+
sections.push(`| ${c.name} | ${c.kind || "-"} |`);
|
|
743
|
+
}
|
|
744
|
+
sections.push("");
|
|
745
|
+
}
|
|
746
|
+
return sections.join("\n");
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
|
|
406
750
|
// ../connectors/src/connectors/influxdb/tools/request.ts
|
|
407
751
|
import { z } from "zod";
|
|
408
752
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
@@ -669,6 +1013,7 @@ export default async function handler(c: Context) {
|
|
|
669
1013
|
- measurement \u4E00\u89A7: \`SHOW TABLES\`\u3001\u5217\u4E00\u89A7: \`SHOW COLUMNS FROM <measurement>\``
|
|
670
1014
|
},
|
|
671
1015
|
tools,
|
|
1016
|
+
setup: (params, ctx, config) => runSetupFlow(influxdbSetupFlow, params, ctx, config),
|
|
672
1017
|
async checkConnection(params) {
|
|
673
1018
|
const url = (params.url ?? "").replace(/\/$/, "");
|
|
674
1019
|
const token = params.token;
|
|
@@ -737,6 +1082,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
737
1082
|
import { getContext } from "hono/context-storage";
|
|
738
1083
|
import { getCookie } from "hono/cookie";
|
|
739
1084
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
1085
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
740
1086
|
function normalizeHeaders(input) {
|
|
741
1087
|
const out = {};
|
|
742
1088
|
if (!input) return out;
|
|
@@ -745,6 +1091,11 @@ function normalizeHeaders(input) {
|
|
|
745
1091
|
});
|
|
746
1092
|
return out;
|
|
747
1093
|
}
|
|
1094
|
+
function extractInputUrl(input) {
|
|
1095
|
+
if (typeof input === "string") return input;
|
|
1096
|
+
if (input instanceof URL) return input.href;
|
|
1097
|
+
return input.url;
|
|
1098
|
+
}
|
|
748
1099
|
function createSandboxProxyFetch(connectionId) {
|
|
749
1100
|
return async (input, init) => {
|
|
750
1101
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -754,10 +1105,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
754
1105
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
755
1106
|
);
|
|
756
1107
|
}
|
|
757
|
-
const originalUrl =
|
|
1108
|
+
const originalUrl = extractInputUrl(input);
|
|
1109
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
1110
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1111
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
1112
|
+
return fetch(sessionUrl, {
|
|
1113
|
+
method: "POST",
|
|
1114
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
758
1117
|
const originalMethod = init?.method ?? "GET";
|
|
759
1118
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
760
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
761
1119
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
762
1120
|
return fetch(proxyUrl, {
|
|
763
1121
|
method: "POST",
|
|
@@ -783,10 +1141,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
783
1141
|
}
|
|
784
1142
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
785
1143
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1144
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
786
1145
|
return async (input, init) => {
|
|
787
|
-
const originalUrl =
|
|
788
|
-
const originalMethod = init?.method ?? "GET";
|
|
789
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
1146
|
+
const originalUrl = extractInputUrl(input);
|
|
790
1147
|
const c = getContext();
|
|
791
1148
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
792
1149
|
if (!appSession) {
|
|
@@ -794,6 +1151,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
794
1151
|
"No authentication method available for connection proxy."
|
|
795
1152
|
);
|
|
796
1153
|
}
|
|
1154
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1155
|
+
return fetch(sessionUrl, {
|
|
1156
|
+
method: "POST",
|
|
1157
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
const originalMethod = init?.method ?? "GET";
|
|
1161
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
797
1162
|
return fetch(proxyUrl, {
|
|
798
1163
|
method: "POST",
|
|
799
1164
|
headers: {
|