@squadbase/vite-server 0.1.12-dev.a9ac647 → 0.1.17-dev.24af54e
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 +12374 -883
- package/dist/connectors/airtable-oauth.js +257 -46
- package/dist/connectors/airtable.js +294 -51
- package/dist/connectors/amplitude.js +297 -47
- package/dist/connectors/anthropic.js +135 -47
- package/dist/connectors/asana.js +302 -49
- package/dist/connectors/attio.js +277 -49
- package/dist/connectors/aws-billing.js +262 -46
- package/dist/connectors/azure-sql.js +396 -102
- package/dist/connectors/backlog-api-key.js +292 -47
- package/dist/connectors/clickup.js +313 -49
- package/dist/connectors/cosmosdb.js +280 -50
- package/dist/connectors/customerio.js +294 -47
- package/dist/connectors/dbt.js +315 -47
- package/dist/connectors/freshdesk.js +317 -53
- package/dist/connectors/freshsales.js +308 -52
- package/dist/connectors/freshservice.js +336 -53
- package/dist/connectors/gamma.js +302 -52
- package/dist/connectors/gemini.js +134 -47
- package/dist/connectors/github.js +361 -49
- package/dist/connectors/gmail-oauth.js +179 -7
- package/dist/connectors/gmail.js +325 -47
- package/dist/connectors/google-ads.js +263 -46
- package/dist/connectors/google-analytics-oauth.js +285 -46
- package/dist/connectors/google-analytics.js +387 -49
- package/dist/connectors/google-audit-log.js +413 -47
- package/dist/connectors/google-calendar-oauth.js +234 -46
- package/dist/connectors/google-calendar.js +334 -47
- package/dist/connectors/google-docs.js +195 -6
- package/dist/connectors/google-drive.js +237 -5
- package/dist/connectors/google-search-console-oauth.js +231 -46
- package/dist/connectors/google-sheets.js +247 -47
- package/dist/connectors/google-slides.js +180 -6
- package/dist/connectors/grafana.js +307 -49
- package/dist/connectors/hubspot-oauth.js +183 -5
- package/dist/connectors/hubspot.js +281 -49
- package/dist/connectors/influxdb.js +391 -51
- package/dist/connectors/intercom-oauth.js +185 -5
- package/dist/connectors/intercom.js +277 -49
- package/dist/connectors/jdbc.js +737 -110
- package/dist/connectors/jira-api-key.js +301 -47
- package/dist/connectors/kintone-api-token.js +256 -47
- package/dist/connectors/kintone.js +303 -47
- package/dist/connectors/linear.js +305 -49
- package/dist/connectors/linkedin-ads.js +243 -50
- package/dist/connectors/mailchimp-oauth.js +243 -46
- package/dist/connectors/mailchimp.js +295 -49
- package/dist/connectors/meta-ads-oauth.js +248 -48
- package/dist/connectors/meta-ads.js +260 -50
- package/dist/connectors/mixpanel.js +313 -47
- package/dist/connectors/monday.js +335 -49
- package/dist/connectors/mongodb.js +294 -57
- package/dist/connectors/notion-oauth.js +206 -5
- package/dist/connectors/notion.js +298 -51
- package/dist/connectors/openai.js +134 -47
- package/dist/connectors/oracle.js +414 -103
- package/dist/connectors/outlook-oauth.js +179 -5
- package/dist/connectors/powerbi-oauth.js +226 -5
- package/dist/connectors/salesforce.js +359 -49
- package/dist/connectors/semrush.js +289 -49
- package/dist/connectors/sentry.js +264 -50
- package/dist/connectors/shopify-oauth.js +162 -5
- package/dist/connectors/shopify.js +332 -47
- package/dist/connectors/sqlserver.js +390 -102
- package/dist/connectors/stripe-api-key.js +244 -46
- package/dist/connectors/stripe-oauth.js +177 -5
- package/dist/connectors/supabase.js +278 -48
- package/dist/connectors/tableau.js +389 -184
- package/dist/connectors/tiktok-ads.js +254 -48
- package/dist/connectors/wix-store.js +295 -49
- package/dist/connectors/zendesk-oauth.js +214 -5
- package/dist/connectors/zendesk.js +333 -47
- package/dist/index.d.ts +149 -1
- package/dist/index.js +13677 -1969
- package/dist/main.js +13627 -1927
- package/dist/vite-plugin.js +12391 -890
- 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,51 @@ 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
|
+
for (const step of flow.steps) {
|
|
384
|
+
const ans = ctx.answers[answerIdx];
|
|
385
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
386
|
+
state = step.applyAnswer(state, ans.answer);
|
|
387
|
+
answerIdx += 1;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
if (step.type === "text") {
|
|
391
|
+
return {
|
|
392
|
+
type: "nextQuestion",
|
|
393
|
+
questionSlug: step.slug,
|
|
394
|
+
question: step.question[ctx.language],
|
|
395
|
+
questionType: "text"
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
399
|
+
if (options.length === 0) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
type: "nextQuestion",
|
|
404
|
+
questionSlug: step.slug,
|
|
405
|
+
question: step.question[ctx.language],
|
|
406
|
+
questionType: step.type,
|
|
407
|
+
options
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
411
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
412
|
+
}
|
|
413
|
+
async function resolveSetupSelection(params) {
|
|
414
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
415
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
416
|
+
return resolved.slice(0, limit);
|
|
417
|
+
}
|
|
418
|
+
|
|
338
419
|
// ../connectors/src/auth-types.ts
|
|
339
420
|
var AUTH_TYPES = {
|
|
340
421
|
OAUTH: "oauth",
|
|
@@ -403,6 +484,244 @@ var influxdbOnboarding = new ConnectorOnboarding({
|
|
|
403
484
|
}
|
|
404
485
|
});
|
|
405
486
|
|
|
487
|
+
// ../connectors/src/connectors/influxdb/utils.ts
|
|
488
|
+
function authHeaders(token, extra) {
|
|
489
|
+
const headers = new Headers(extra);
|
|
490
|
+
headers.set("Authorization", `Token ${token}`);
|
|
491
|
+
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
492
|
+
return headers;
|
|
493
|
+
}
|
|
494
|
+
async function detectVariant(params) {
|
|
495
|
+
const url = (params[parameters.url.slug] ?? "").replace(/\/$/, "");
|
|
496
|
+
const token = params[parameters.token.slug];
|
|
497
|
+
const database = params[parameters.database.slug];
|
|
498
|
+
const res = await fetch(`${url}/api/v3/query_sql`, {
|
|
499
|
+
method: "POST",
|
|
500
|
+
headers: authHeaders(token, { "Content-Type": "application/json" }),
|
|
501
|
+
body: JSON.stringify({ db: database, q: "SELECT 1" })
|
|
502
|
+
});
|
|
503
|
+
if (res.ok) {
|
|
504
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
505
|
+
if (contentType.includes("application/json")) return "v3";
|
|
506
|
+
}
|
|
507
|
+
return "v2";
|
|
508
|
+
}
|
|
509
|
+
async function runSql(params, sql) {
|
|
510
|
+
const url = (params[parameters.url.slug] ?? "").replace(/\/$/, "");
|
|
511
|
+
const token = params[parameters.token.slug];
|
|
512
|
+
const database = params[parameters.database.slug];
|
|
513
|
+
const res = await fetch(`${url}/api/v3/query_sql`, {
|
|
514
|
+
method: "POST",
|
|
515
|
+
headers: authHeaders(token, { "Content-Type": "application/json" }),
|
|
516
|
+
body: JSON.stringify({ db: database, q: sql })
|
|
517
|
+
});
|
|
518
|
+
if (!res.ok) {
|
|
519
|
+
const body = await res.text().catch(() => res.statusText);
|
|
520
|
+
throw new Error(
|
|
521
|
+
`InfluxDB SQL query failed: HTTP ${res.status} \u2014 ${body.slice(0, 200)}`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
return await res.json();
|
|
525
|
+
}
|
|
526
|
+
async function runFlux(params, flux) {
|
|
527
|
+
const url = (params[parameters.url.slug] ?? "").replace(/\/$/, "");
|
|
528
|
+
const token = params[parameters.token.slug];
|
|
529
|
+
const org = params[parameters.org.slug];
|
|
530
|
+
if (!org) {
|
|
531
|
+
throw new Error(
|
|
532
|
+
"InfluxDB 2 Flux queries require the 'org' connection parameter, which is not configured"
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
const res = await fetch(
|
|
536
|
+
`${url}/api/v2/query?org=${encodeURIComponent(org)}`,
|
|
537
|
+
{
|
|
538
|
+
method: "POST",
|
|
539
|
+
headers: authHeaders(token, {
|
|
540
|
+
"Content-Type": "application/vnd.flux",
|
|
541
|
+
Accept: "application/csv"
|
|
542
|
+
}),
|
|
543
|
+
body: flux
|
|
544
|
+
}
|
|
545
|
+
);
|
|
546
|
+
if (!res.ok) {
|
|
547
|
+
const body = await res.text().catch(() => res.statusText);
|
|
548
|
+
throw new Error(
|
|
549
|
+
`InfluxDB Flux query failed: HTTP ${res.status} \u2014 ${body.slice(0, 200)}`
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
return await res.text();
|
|
553
|
+
}
|
|
554
|
+
function parseAnnotatedCsv(csv) {
|
|
555
|
+
const lines = csv.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
556
|
+
const rows = [];
|
|
557
|
+
let headers = null;
|
|
558
|
+
for (const line of lines) {
|
|
559
|
+
if (line.startsWith("#")) {
|
|
560
|
+
headers = null;
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
const fields = splitCsvLine(line);
|
|
564
|
+
if (headers === null) {
|
|
565
|
+
headers = fields;
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
const row = {};
|
|
569
|
+
headers.forEach((h, i) => {
|
|
570
|
+
row[h] = fields[i] ?? "";
|
|
571
|
+
});
|
|
572
|
+
rows.push(row);
|
|
573
|
+
}
|
|
574
|
+
return { columns: headers ?? [], rows };
|
|
575
|
+
}
|
|
576
|
+
function splitCsvLine(line) {
|
|
577
|
+
const out = [];
|
|
578
|
+
let cur = "";
|
|
579
|
+
let inQuotes = false;
|
|
580
|
+
for (let i = 0; i < line.length; i++) {
|
|
581
|
+
const c = line[i];
|
|
582
|
+
if (inQuotes) {
|
|
583
|
+
if (c === '"' && line[i + 1] === '"') {
|
|
584
|
+
cur += '"';
|
|
585
|
+
i += 1;
|
|
586
|
+
} else if (c === '"') {
|
|
587
|
+
inQuotes = false;
|
|
588
|
+
} else {
|
|
589
|
+
cur += c;
|
|
590
|
+
}
|
|
591
|
+
} else if (c === '"') {
|
|
592
|
+
inQuotes = true;
|
|
593
|
+
} else if (c === ",") {
|
|
594
|
+
out.push(cur);
|
|
595
|
+
cur = "";
|
|
596
|
+
} else {
|
|
597
|
+
cur += c;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
out.push(cur);
|
|
601
|
+
return out;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// ../connectors/src/connectors/influxdb/setup-flow.ts
|
|
605
|
+
var ALL_MEASUREMENTS = "__ALL_MEASUREMENTS__";
|
|
606
|
+
var INFLUXDB_SETUP_MAX_MEASUREMENTS = 20;
|
|
607
|
+
async function listMeasurements(params, variant) {
|
|
608
|
+
if (variant === "v3") {
|
|
609
|
+
const rows2 = await runSql(params, "SHOW TABLES");
|
|
610
|
+
return rows2.map((r) => String(r["table_name"] ?? r["name"] ?? "")).filter((name) => name).filter((name) => !name.includes("::"));
|
|
611
|
+
}
|
|
612
|
+
const bucket = params[parameters.database.slug];
|
|
613
|
+
if (!bucket) {
|
|
614
|
+
throw new Error(
|
|
615
|
+
"InfluxDB 2 setup: 'database' (bucket) parameter is required to list measurements"
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
const flux = [
|
|
619
|
+
`import "influxdata/influxdb/schema"`,
|
|
620
|
+
`schema.measurements(bucket: ${JSON.stringify(bucket)})`
|
|
621
|
+
].join("\n");
|
|
622
|
+
const csv = await runFlux(params, flux);
|
|
623
|
+
const { rows } = parseAnnotatedCsv(csv);
|
|
624
|
+
return rows.map((r) => r["_value"] ?? "").filter((name) => name);
|
|
625
|
+
}
|
|
626
|
+
async function listMeasurementColumns(params, variant, measurement) {
|
|
627
|
+
if (variant === "v3") {
|
|
628
|
+
const safeName2 = measurement.replace(/"/g, '""');
|
|
629
|
+
const rows = await runSql(params, `SHOW COLUMNS FROM "${safeName2}"`);
|
|
630
|
+
return rows.map((r) => ({
|
|
631
|
+
name: String(r["column_name"] ?? r["name"] ?? ""),
|
|
632
|
+
kind: String(r["data_type"] ?? r["type"] ?? "")
|
|
633
|
+
}));
|
|
634
|
+
}
|
|
635
|
+
const bucket = params[parameters.database.slug];
|
|
636
|
+
const safeName = measurement.replace(/"/g, '\\"');
|
|
637
|
+
const fieldsFlux = [
|
|
638
|
+
`import "influxdata/influxdb/schema"`,
|
|
639
|
+
`schema.measurementFieldKeys(bucket: ${JSON.stringify(bucket)}, measurement: "${safeName}")`
|
|
640
|
+
].join("\n");
|
|
641
|
+
const tagsFlux = [
|
|
642
|
+
`import "influxdata/influxdb/schema"`,
|
|
643
|
+
`schema.measurementTagKeys(bucket: ${JSON.stringify(bucket)}, measurement: "${safeName}")`
|
|
644
|
+
].join("\n");
|
|
645
|
+
const [fieldsCsv, tagsCsv] = await Promise.all([
|
|
646
|
+
runFlux(params, fieldsFlux),
|
|
647
|
+
runFlux(params, tagsFlux)
|
|
648
|
+
]);
|
|
649
|
+
const fields = parseAnnotatedCsv(fieldsCsv).rows.map((r) => ({
|
|
650
|
+
name: r["_value"] ?? "",
|
|
651
|
+
kind: "field"
|
|
652
|
+
}));
|
|
653
|
+
const tags = parseAnnotatedCsv(tagsCsv).rows.map((r) => ({
|
|
654
|
+
name: r["_value"] ?? "",
|
|
655
|
+
kind: "tag"
|
|
656
|
+
}));
|
|
657
|
+
return [...tags, ...fields].filter((c) => c.name);
|
|
658
|
+
}
|
|
659
|
+
var influxdbSetupFlow = {
|
|
660
|
+
initialState: () => ({}),
|
|
661
|
+
steps: [
|
|
662
|
+
{
|
|
663
|
+
slug: "measurements",
|
|
664
|
+
type: "multiSelect",
|
|
665
|
+
question: {
|
|
666
|
+
ja: "\u5BFE\u8C61 measurement \u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
667
|
+
en: "Select target measurements (multi-select allowed)"
|
|
668
|
+
},
|
|
669
|
+
async fetchOptions(_state, rt) {
|
|
670
|
+
const variant = await detectVariant(rt.params);
|
|
671
|
+
const ms = await listMeasurements(rt.params, variant);
|
|
672
|
+
const options = ms.map((value) => ({ value }));
|
|
673
|
+
return [
|
|
674
|
+
{
|
|
675
|
+
value: ALL_MEASUREMENTS,
|
|
676
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E measurement" : "All measurements"
|
|
677
|
+
},
|
|
678
|
+
...options
|
|
679
|
+
];
|
|
680
|
+
},
|
|
681
|
+
applyAnswer: (state, answer) => ({ ...state, measurements: answer })
|
|
682
|
+
}
|
|
683
|
+
],
|
|
684
|
+
async finalize(state, rt) {
|
|
685
|
+
if (!state.measurements) {
|
|
686
|
+
throw new Error("InfluxDB setup: incomplete state on finalize");
|
|
687
|
+
}
|
|
688
|
+
const variant = await detectVariant(rt.params);
|
|
689
|
+
const database = rt.params[parameters.database.slug];
|
|
690
|
+
const org = rt.params[parameters.org.slug];
|
|
691
|
+
const targetMeasurements = await resolveSetupSelection({
|
|
692
|
+
selected: state.measurements,
|
|
693
|
+
allSentinel: ALL_MEASUREMENTS,
|
|
694
|
+
fetchAll: () => listMeasurements(rt.params, variant),
|
|
695
|
+
limit: INFLUXDB_SETUP_MAX_MEASUREMENTS
|
|
696
|
+
});
|
|
697
|
+
const sections = [
|
|
698
|
+
"## InfluxDB",
|
|
699
|
+
"",
|
|
700
|
+
`### Variant: ${variant === "v3" ? "InfluxDB 3 (SQL)" : "InfluxDB 2 (Flux)"}`,
|
|
701
|
+
`### ${variant === "v3" ? "Database" : "Bucket"}: ${database}`
|
|
702
|
+
];
|
|
703
|
+
if (variant === "v2" && org) {
|
|
704
|
+
sections.push(`### Organization: ${org}`);
|
|
705
|
+
}
|
|
706
|
+
sections.push("");
|
|
707
|
+
for (const measurement of targetMeasurements) {
|
|
708
|
+
const cols = await listMeasurementColumns(rt.params, variant, measurement);
|
|
709
|
+
sections.push(`#### Measurement: ${measurement}`, "");
|
|
710
|
+
if (cols.length === 0) {
|
|
711
|
+
sections.push("_(no fields or tags found)_", "");
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
sections.push("| Column | Kind |");
|
|
715
|
+
sections.push("|--------|------|");
|
|
716
|
+
for (const c of cols) {
|
|
717
|
+
sections.push(`| ${c.name} | ${c.kind || "-"} |`);
|
|
718
|
+
}
|
|
719
|
+
sections.push("");
|
|
720
|
+
}
|
|
721
|
+
return sections.join("\n");
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
|
|
406
725
|
// ../connectors/src/connectors/influxdb/tools/request.ts
|
|
407
726
|
import { z } from "zod";
|
|
408
727
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
@@ -669,6 +988,7 @@ export default async function handler(c: Context) {
|
|
|
669
988
|
- measurement \u4E00\u89A7: \`SHOW TABLES\`\u3001\u5217\u4E00\u89A7: \`SHOW COLUMNS FROM <measurement>\``
|
|
670
989
|
},
|
|
671
990
|
tools,
|
|
991
|
+
setup: (params, ctx, config) => runSetupFlow(influxdbSetupFlow, params, ctx, config),
|
|
672
992
|
async checkConnection(params) {
|
|
673
993
|
const url = (params.url ?? "").replace(/\/$/, "");
|
|
674
994
|
const token = params.token;
|
|
@@ -737,6 +1057,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
737
1057
|
import { getContext } from "hono/context-storage";
|
|
738
1058
|
import { getCookie } from "hono/cookie";
|
|
739
1059
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
1060
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
740
1061
|
function normalizeHeaders(input) {
|
|
741
1062
|
const out = {};
|
|
742
1063
|
if (!input) return out;
|
|
@@ -745,6 +1066,11 @@ function normalizeHeaders(input) {
|
|
|
745
1066
|
});
|
|
746
1067
|
return out;
|
|
747
1068
|
}
|
|
1069
|
+
function extractInputUrl(input) {
|
|
1070
|
+
if (typeof input === "string") return input;
|
|
1071
|
+
if (input instanceof URL) return input.href;
|
|
1072
|
+
return input.url;
|
|
1073
|
+
}
|
|
748
1074
|
function createSandboxProxyFetch(connectionId) {
|
|
749
1075
|
return async (input, init) => {
|
|
750
1076
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -754,10 +1080,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
754
1080
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
755
1081
|
);
|
|
756
1082
|
}
|
|
757
|
-
const originalUrl =
|
|
1083
|
+
const originalUrl = extractInputUrl(input);
|
|
1084
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
1085
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1086
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
1087
|
+
return fetch(sessionUrl, {
|
|
1088
|
+
method: "POST",
|
|
1089
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
758
1092
|
const originalMethod = init?.method ?? "GET";
|
|
759
1093
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
760
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
761
1094
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
762
1095
|
return fetch(proxyUrl, {
|
|
763
1096
|
method: "POST",
|
|
@@ -783,10 +1116,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
783
1116
|
}
|
|
784
1117
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
785
1118
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1119
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
786
1120
|
return async (input, init) => {
|
|
787
|
-
const originalUrl =
|
|
788
|
-
const originalMethod = init?.method ?? "GET";
|
|
789
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
1121
|
+
const originalUrl = extractInputUrl(input);
|
|
790
1122
|
const c = getContext();
|
|
791
1123
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
792
1124
|
if (!appSession) {
|
|
@@ -794,6 +1126,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
794
1126
|
"No authentication method available for connection proxy."
|
|
795
1127
|
);
|
|
796
1128
|
}
|
|
1129
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1130
|
+
return fetch(sessionUrl, {
|
|
1131
|
+
method: "POST",
|
|
1132
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
const originalMethod = init?.method ?? "GET";
|
|
1136
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
797
1137
|
return fetch(proxyUrl, {
|
|
798
1138
|
method: "POST",
|
|
799
1139
|
headers: {
|