@squadbase/vite-server 0.1.12-dev.93b8799 → 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 +12128 -934
- package/dist/connectors/airtable-oauth.js +248 -46
- package/dist/connectors/airtable.js +285 -51
- package/dist/connectors/amplitude.js +288 -47
- package/dist/connectors/anthropic.js +126 -47
- package/dist/connectors/asana.js +293 -49
- package/dist/connectors/attio.js +268 -49
- package/dist/connectors/aws-billing.js +253 -46
- package/dist/connectors/azure-sql.js +387 -102
- package/dist/connectors/backlog-api-key.js +283 -47
- package/dist/connectors/clickup.js +304 -49
- package/dist/connectors/cosmosdb.js +271 -50
- package/dist/connectors/customerio.js +285 -47
- package/dist/connectors/dbt.js +306 -47
- package/dist/connectors/freshdesk.js +308 -53
- package/dist/connectors/freshsales.js +299 -52
- package/dist/connectors/freshservice.js +327 -53
- package/dist/connectors/gamma.js +293 -52
- package/dist/connectors/gemini.js +125 -47
- package/dist/connectors/github.js +352 -49
- package/dist/connectors/gmail-oauth.js +170 -7
- package/dist/connectors/gmail.js +316 -47
- package/dist/connectors/google-ads.js +254 -46
- package/dist/connectors/google-analytics-oauth.js +276 -46
- package/dist/connectors/google-analytics.js +378 -49
- package/dist/connectors/google-audit-log.js +404 -47
- package/dist/connectors/google-calendar-oauth.js +225 -46
- package/dist/connectors/google-calendar.js +325 -47
- package/dist/connectors/google-docs.js +186 -6
- package/dist/connectors/google-drive.js +228 -5
- package/dist/connectors/google-search-console-oauth.js +222 -46
- package/dist/connectors/google-sheets.js +238 -47
- package/dist/connectors/google-slides.js +171 -6
- package/dist/connectors/grafana.js +298 -49
- package/dist/connectors/hubspot-oauth.js +174 -5
- package/dist/connectors/hubspot.js +272 -49
- package/dist/connectors/influxdb.js +382 -51
- package/dist/connectors/intercom-oauth.js +176 -5
- package/dist/connectors/intercom.js +268 -49
- package/dist/connectors/jdbc.js +728 -110
- package/dist/connectors/jira-api-key.js +292 -47
- package/dist/connectors/kintone-api-token.js +247 -47
- package/dist/connectors/kintone.js +294 -47
- package/dist/connectors/linear.js +296 -49
- package/dist/connectors/linkedin-ads.js +234 -50
- package/dist/connectors/mailchimp-oauth.js +234 -46
- package/dist/connectors/mailchimp.js +286 -49
- package/dist/connectors/meta-ads-oauth.js +239 -48
- package/dist/connectors/meta-ads.js +251 -50
- package/dist/connectors/mixpanel.js +304 -47
- package/dist/connectors/monday.js +326 -49
- package/dist/connectors/mongodb.js +285 -57
- package/dist/connectors/notion-oauth.js +197 -5
- package/dist/connectors/notion.js +289 -51
- package/dist/connectors/openai.js +125 -47
- package/dist/connectors/oracle.js +405 -103
- package/dist/connectors/outlook-oauth.js +170 -5
- package/dist/connectors/powerbi-oauth.js +217 -5
- package/dist/connectors/salesforce.js +350 -49
- package/dist/connectors/semrush.js +280 -49
- package/dist/connectors/sentry.js +255 -50
- package/dist/connectors/shopify-oauth.js +153 -5
- package/dist/connectors/shopify.js +323 -47
- package/dist/connectors/sqlserver.js +381 -102
- package/dist/connectors/stripe-api-key.js +235 -46
- package/dist/connectors/stripe-oauth.js +168 -5
- package/dist/connectors/supabase.js +269 -48
- package/dist/connectors/tableau.js +337 -206
- package/dist/connectors/tiktok-ads.js +245 -48
- package/dist/connectors/wix-store.js +286 -49
- package/dist/connectors/zendesk-oauth.js +205 -5
- package/dist/connectors/zendesk.js +324 -47
- package/dist/index.d.ts +149 -1
- package/dist/index.js +18297 -6886
- package/dist/main.js +12785 -1382
- package/dist/vite-plugin.js +12140 -936
- 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/tableau/parameters.ts
|
|
57
|
+
init_parameter_definition();
|
|
46
58
|
var parameters = {
|
|
47
59
|
serverUrl: new ParameterDefinition({
|
|
48
60
|
slug: "server-url",
|
|
@@ -93,67 +105,16 @@ var parameters = {
|
|
|
93
105
|
|
|
94
106
|
// ../connectors/src/connectors/tableau/sdk/index.ts
|
|
95
107
|
var DEFAULT_API_VERSION = "3.28";
|
|
96
|
-
|
|
108
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
109
|
+
function createClient(params, fetchFn = fetch) {
|
|
97
110
|
const serverUrl = params[parameters.serverUrl.slug];
|
|
98
|
-
const siteContentUrl = params[parameters.siteContentUrl.slug];
|
|
99
|
-
const patName = params[parameters.patName.slug];
|
|
100
|
-
const patSecret = params[parameters.patSecret.slug];
|
|
101
111
|
const apiVersion = params[parameters.apiVersion.slug] || DEFAULT_API_VERSION;
|
|
102
|
-
if (!serverUrl
|
|
103
|
-
throw new Error(
|
|
104
|
-
`tableau: missing required parameters: ${parameters.serverUrl.slug}, ${parameters.siteContentUrl.slug}, ${parameters.patName.slug}, ${parameters.patSecret.slug}`
|
|
105
|
-
);
|
|
112
|
+
if (!serverUrl) {
|
|
113
|
+
throw new Error(`tableau: missing required parameter: ${parameters.serverUrl.slug}`);
|
|
106
114
|
}
|
|
107
115
|
const baseUrl = `${serverUrl.replace(/\/$/, "")}/api/${apiVersion}`;
|
|
108
|
-
|
|
109
|
-
let inFlightSignIn = null;
|
|
110
|
-
async function signIn2() {
|
|
111
|
-
const res = await fetch(`${baseUrl}/auth/signin`, {
|
|
112
|
-
method: "POST",
|
|
113
|
-
headers: {
|
|
114
|
-
"Content-Type": "application/json",
|
|
115
|
-
Accept: "application/json"
|
|
116
|
-
},
|
|
117
|
-
body: JSON.stringify({
|
|
118
|
-
credentials: {
|
|
119
|
-
personalAccessTokenName: patName,
|
|
120
|
-
personalAccessTokenSecret: patSecret,
|
|
121
|
-
site: { contentUrl: siteContentUrl }
|
|
122
|
-
}
|
|
123
|
-
})
|
|
124
|
-
});
|
|
125
|
-
if (!res.ok) {
|
|
126
|
-
const body = await res.text();
|
|
127
|
-
throw new Error(`tableau: sign-in failed (${res.status}): ${body}`);
|
|
128
|
-
}
|
|
129
|
-
const data = await res.json();
|
|
130
|
-
return {
|
|
131
|
-
authToken: data.credentials.token,
|
|
132
|
-
siteId: data.credentials.site.id,
|
|
133
|
-
userId: data.credentials.user.id,
|
|
134
|
-
expiresAt: Date.now() + 30 * 60 * 1e3
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
async function ensureSession(options) {
|
|
138
|
-
if (options?.forceRefresh) {
|
|
139
|
-
if (session && (!options.invalidateToken || session.authToken === options.invalidateToken)) {
|
|
140
|
-
session = null;
|
|
141
|
-
}
|
|
142
|
-
} else if (session && session.expiresAt > Date.now() + 6e4) {
|
|
143
|
-
return session;
|
|
144
|
-
}
|
|
145
|
-
if (inFlightSignIn) return inFlightSignIn;
|
|
146
|
-
inFlightSignIn = signIn2().then((s) => {
|
|
147
|
-
session = s;
|
|
148
|
-
return s;
|
|
149
|
-
}).finally(() => {
|
|
150
|
-
inFlightSignIn = null;
|
|
151
|
-
});
|
|
152
|
-
return inFlightSignIn;
|
|
153
|
-
}
|
|
154
|
-
function buildRequestInit(s, init) {
|
|
116
|
+
function buildRequestInit(init) {
|
|
155
117
|
const headers = new Headers(init?.headers);
|
|
156
|
-
headers.set("X-Tableau-Auth", s.authToken);
|
|
157
118
|
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
158
119
|
if (!headers.has("Content-Type") && init?.body) {
|
|
159
120
|
headers.set("Content-Type", "application/json");
|
|
@@ -162,22 +123,8 @@ function createClient(params) {
|
|
|
162
123
|
}
|
|
163
124
|
async function request(path2, init) {
|
|
164
125
|
const trimmedPath = path2.replace(/^([^/])/, "/$1");
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const s = await ensureSession();
|
|
168
|
-
const url = `${baseUrl}${trimmedPath.replace(/\{siteId\}/g, s.siteId)}`;
|
|
169
|
-
const response = await fetch(url, buildRequestInit(s, init));
|
|
170
|
-
if (response.status === 401 && attempt === 0) {
|
|
171
|
-
await response.text().catch(() => void 0);
|
|
172
|
-
await ensureSession({
|
|
173
|
-
forceRefresh: true,
|
|
174
|
-
invalidateToken: s.authToken
|
|
175
|
-
});
|
|
176
|
-
attempt++;
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
return response;
|
|
180
|
-
}
|
|
126
|
+
const url = `${baseUrl}${trimmedPath}`;
|
|
127
|
+
return fetchFn(url, buildRequestInit(init));
|
|
181
128
|
}
|
|
182
129
|
async function getJson(path2) {
|
|
183
130
|
const res = await request(path2);
|
|
@@ -200,8 +147,19 @@ function createClient(params) {
|
|
|
200
147
|
return {
|
|
201
148
|
request,
|
|
202
149
|
async getSession() {
|
|
203
|
-
const
|
|
204
|
-
|
|
150
|
+
const res = await fetchFn(TABLEAU_SESSION_SENTINEL_URL, { method: "POST" });
|
|
151
|
+
if (!res.ok) {
|
|
152
|
+
const body = await res.text().catch(() => "(unreadable)");
|
|
153
|
+
throw new Error(
|
|
154
|
+
`tableau: failed to fetch session (${res.status}): ${body}`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
const data = await res.json();
|
|
158
|
+
return {
|
|
159
|
+
authToken: data.authToken,
|
|
160
|
+
siteId: data.siteId,
|
|
161
|
+
userId: data.userId
|
|
162
|
+
};
|
|
205
163
|
},
|
|
206
164
|
async listProjects(options) {
|
|
207
165
|
return getJson(
|
|
@@ -306,6 +264,20 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
306
264
|
* `runSetupFlow` from `setup-flow.ts`.
|
|
307
265
|
*/
|
|
308
266
|
setup;
|
|
267
|
+
/**
|
|
268
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
269
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
270
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
271
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
272
|
+
*
|
|
273
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
274
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
275
|
+
* connectionId, which doesn't exist until the row is saved
|
|
276
|
+
*
|
|
277
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
278
|
+
* the default verify-on-create behavior without opt-in.
|
|
279
|
+
*/
|
|
280
|
+
skipConnectionCheckOnCreate;
|
|
309
281
|
constructor(config) {
|
|
310
282
|
this.slug = config.slug;
|
|
311
283
|
this.authType = config.authType;
|
|
@@ -323,6 +295,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
323
295
|
this.query = config.query;
|
|
324
296
|
this.checkConnection = config.checkConnection;
|
|
325
297
|
this.setup = config.setup;
|
|
298
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
326
299
|
}
|
|
327
300
|
get connectorKey() {
|
|
328
301
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -387,6 +360,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
387
360
|
}
|
|
388
361
|
};
|
|
389
362
|
|
|
363
|
+
// ../connectors/src/setup-flow.ts
|
|
364
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
365
|
+
const runtime = {
|
|
366
|
+
params,
|
|
367
|
+
language: ctx.language,
|
|
368
|
+
config
|
|
369
|
+
};
|
|
370
|
+
let state = flow.initialState();
|
|
371
|
+
let answerIdx = 0;
|
|
372
|
+
for (const step of flow.steps) {
|
|
373
|
+
const ans = ctx.answers[answerIdx];
|
|
374
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
375
|
+
state = step.applyAnswer(state, ans.answer);
|
|
376
|
+
answerIdx += 1;
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (step.type === "text") {
|
|
380
|
+
return {
|
|
381
|
+
type: "nextQuestion",
|
|
382
|
+
questionSlug: step.slug,
|
|
383
|
+
question: step.question[ctx.language],
|
|
384
|
+
questionType: "text"
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
388
|
+
if (options.length === 0) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
type: "nextQuestion",
|
|
393
|
+
questionSlug: step.slug,
|
|
394
|
+
question: step.question[ctx.language],
|
|
395
|
+
questionType: step.type,
|
|
396
|
+
options
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
400
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
401
|
+
}
|
|
402
|
+
async function resolveSetupSelection(params) {
|
|
403
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
404
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
405
|
+
return resolved.slice(0, limit);
|
|
406
|
+
}
|
|
407
|
+
|
|
390
408
|
// ../connectors/src/auth-types.ts
|
|
391
409
|
var AUTH_TYPES = {
|
|
392
410
|
OAUTH: "oauth",
|
|
@@ -429,82 +447,190 @@ var tableauOnboarding = new ConnectorOnboarding({
|
|
|
429
447
|
}
|
|
430
448
|
});
|
|
431
449
|
|
|
432
|
-
// ../connectors/src/connectors/tableau/
|
|
433
|
-
import { z } from "zod";
|
|
450
|
+
// ../connectors/src/connectors/tableau/utils.ts
|
|
434
451
|
var DEFAULT_API_VERSION2 = "3.28";
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
|
|
452
|
+
function buildTableauBaseUrl(params) {
|
|
453
|
+
const serverUrl = params[parameters.serverUrl.slug];
|
|
454
|
+
const apiVersion = params[parameters.apiVersion.slug] || DEFAULT_API_VERSION2;
|
|
455
|
+
if (!serverUrl) {
|
|
456
|
+
throw new Error(
|
|
457
|
+
`tableau: missing required parameter: ${parameters.serverUrl.slug}`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
442
460
|
return `${serverUrl.replace(/\/$/, "")}/api/${apiVersion}`;
|
|
443
461
|
}
|
|
444
|
-
async function
|
|
445
|
-
const
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
462
|
+
async function tableauProxyApiFetch(proxyFetch, params, path2, init) {
|
|
463
|
+
const baseUrl = buildTableauBaseUrl(params);
|
|
464
|
+
const trimmedPath = path2.replace(/^([^/])/, "/$1");
|
|
465
|
+
return proxyFetch(`${baseUrl}${trimmedPath}`, init);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// ../connectors/src/connectors/tableau/setup-flow.ts
|
|
469
|
+
var ALL_PROJECTS = "__ALL_PROJECTS__";
|
|
470
|
+
var TABLEAU_SETUP_MAX_PROJECTS = 10;
|
|
471
|
+
var PAGE_SIZE = 100;
|
|
472
|
+
async function listAllProjects(rt) {
|
|
473
|
+
const all = [];
|
|
474
|
+
let pageNumber = 1;
|
|
475
|
+
while (all.length < 500) {
|
|
476
|
+
const res = await tableauProxyApiFetch(
|
|
477
|
+
rt.config.proxyFetch,
|
|
478
|
+
rt.params,
|
|
479
|
+
`/sites/{siteId}/projects?pageSize=${PAGE_SIZE}&pageNumber=${pageNumber}`
|
|
480
|
+
);
|
|
481
|
+
if (!res.ok) {
|
|
482
|
+
const body = await res.text().catch(() => res.statusText);
|
|
483
|
+
throw new Error(`tableau: listProjects failed (${res.status}): ${body}`);
|
|
451
484
|
}
|
|
452
|
-
|
|
485
|
+
const data = await res.json();
|
|
486
|
+
const batch = data.projects?.project ?? [];
|
|
487
|
+
all.push(...batch);
|
|
488
|
+
const total = Number(data.pagination?.totalAvailable ?? "0");
|
|
489
|
+
if (!batch.length || all.length >= total) break;
|
|
490
|
+
pageNumber += 1;
|
|
491
|
+
}
|
|
492
|
+
return all;
|
|
493
|
+
}
|
|
494
|
+
async function listProjectResources(rt, resource) {
|
|
495
|
+
const all = [];
|
|
496
|
+
let pageNumber = 1;
|
|
497
|
+
while (all.length < 1e3) {
|
|
498
|
+
const res = await tableauProxyApiFetch(
|
|
499
|
+
rt.config.proxyFetch,
|
|
500
|
+
rt.params,
|
|
501
|
+
`/sites/{siteId}/${resource}?pageSize=${PAGE_SIZE}&pageNumber=${pageNumber}`
|
|
502
|
+
);
|
|
503
|
+
if (!res.ok) {
|
|
504
|
+
const body = await res.text().catch(() => res.statusText);
|
|
505
|
+
throw new Error(
|
|
506
|
+
`tableau: list ${resource} failed (${res.status}): ${body}`
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
const data = await res.json();
|
|
510
|
+
const container = data[resource];
|
|
511
|
+
const batch = (resource === "workbooks" ? container?.workbook : container?.datasource) ?? [];
|
|
512
|
+
all.push(...batch);
|
|
513
|
+
const total = Number(data.pagination?.totalAvailable ?? "0");
|
|
514
|
+
if (!batch.length || all.length >= total) break;
|
|
515
|
+
pageNumber += 1;
|
|
516
|
+
}
|
|
517
|
+
return all;
|
|
518
|
+
}
|
|
519
|
+
var tableauSetupFlow = {
|
|
520
|
+
initialState: () => ({}),
|
|
521
|
+
steps: [
|
|
522
|
+
{
|
|
523
|
+
slug: "projects",
|
|
524
|
+
type: "multiSelect",
|
|
525
|
+
question: {
|
|
526
|
+
ja: "\u5BFE\u8C61\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
527
|
+
en: "Select target projects (multi-select allowed)"
|
|
528
|
+
},
|
|
529
|
+
async fetchOptions(_state, rt) {
|
|
530
|
+
const projects = await listAllProjects(rt);
|
|
531
|
+
const projectOptions = projects.filter((p) => p.id && p.name).map((p) => ({ value: p.id, label: p.name }));
|
|
532
|
+
return [
|
|
533
|
+
{
|
|
534
|
+
value: ALL_PROJECTS,
|
|
535
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8" : "All projects"
|
|
536
|
+
},
|
|
537
|
+
...projectOptions
|
|
538
|
+
];
|
|
539
|
+
},
|
|
540
|
+
applyAnswer: (state, answer) => ({ ...state, projects: answer })
|
|
541
|
+
}
|
|
542
|
+
],
|
|
543
|
+
async finalize(state, rt) {
|
|
544
|
+
if (!state.projects) {
|
|
545
|
+
throw new Error("Tableau setup: incomplete state on finalize");
|
|
546
|
+
}
|
|
547
|
+
const allProjects = await listAllProjects(rt);
|
|
548
|
+
const projectById = new Map(allProjects.map((p) => [p.id, p]));
|
|
549
|
+
const targetIds = await resolveSetupSelection({
|
|
550
|
+
selected: state.projects,
|
|
551
|
+
allSentinel: ALL_PROJECTS,
|
|
552
|
+
fetchAll: async () => allProjects.map((p) => p.id).filter((id) => id),
|
|
553
|
+
limit: TABLEAU_SETUP_MAX_PROJECTS
|
|
554
|
+
});
|
|
555
|
+
const sections = ["## Tableau", ""];
|
|
556
|
+
if (!targetIds.length) {
|
|
557
|
+
sections.push("_No projects selected._", "");
|
|
558
|
+
return sections.join("\n");
|
|
559
|
+
}
|
|
560
|
+
const [workbooks, datasources] = await Promise.all([
|
|
561
|
+
listProjectResources(rt, "workbooks"),
|
|
562
|
+
listProjectResources(rt, "datasources")
|
|
563
|
+
]);
|
|
564
|
+
const workbooksByProject = /* @__PURE__ */ new Map();
|
|
565
|
+
for (const wb of workbooks) {
|
|
566
|
+
const pid = wb.project?.id;
|
|
567
|
+
if (!pid) continue;
|
|
568
|
+
const bucket = workbooksByProject.get(pid) ?? [];
|
|
569
|
+
bucket.push(wb);
|
|
570
|
+
workbooksByProject.set(pid, bucket);
|
|
571
|
+
}
|
|
572
|
+
const datasourcesByProject = /* @__PURE__ */ new Map();
|
|
573
|
+
for (const ds of datasources) {
|
|
574
|
+
const pid = ds.project?.id;
|
|
575
|
+
if (!pid) continue;
|
|
576
|
+
const bucket = datasourcesByProject.get(pid) ?? [];
|
|
577
|
+
bucket.push(ds);
|
|
578
|
+
datasourcesByProject.set(pid, bucket);
|
|
579
|
+
}
|
|
580
|
+
for (const id of targetIds) {
|
|
581
|
+
const project = projectById.get(id);
|
|
582
|
+
const name = project?.name ?? id;
|
|
583
|
+
sections.push(`### Project: ${name}`, "", `- id: \`${id}\``);
|
|
584
|
+
const projectWorkbooks = workbooksByProject.get(id) ?? [];
|
|
585
|
+
sections.push(`- Workbooks (${projectWorkbooks.length}):`);
|
|
586
|
+
for (const wb of projectWorkbooks.slice(0, 25)) {
|
|
587
|
+
sections.push(` - ${wb.name ?? wb.id ?? "(unknown)"}`);
|
|
588
|
+
}
|
|
589
|
+
if (projectWorkbooks.length > 25) {
|
|
590
|
+
sections.push(` - \u2026and ${projectWorkbooks.length - 25} more`);
|
|
591
|
+
}
|
|
592
|
+
const projectDatasources = datasourcesByProject.get(id) ?? [];
|
|
593
|
+
sections.push(`- Datasources (${projectDatasources.length}):`);
|
|
594
|
+
for (const ds of projectDatasources.slice(0, 25)) {
|
|
595
|
+
sections.push(` - ${ds.name ?? ds.id ?? "(unknown)"}`);
|
|
596
|
+
}
|
|
597
|
+
if (projectDatasources.length > 25) {
|
|
598
|
+
sections.push(` - \u2026and ${projectDatasources.length - 25} more`);
|
|
599
|
+
}
|
|
600
|
+
sections.push("");
|
|
601
|
+
}
|
|
602
|
+
return sections.join("\n");
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
// ../connectors/src/connectors/tableau/tools/request.ts
|
|
607
|
+
import { z } from "zod";
|
|
608
|
+
var DEFAULT_API_VERSION3 = "3.28";
|
|
609
|
+
var REQUEST_TIMEOUT_MS = 6e4;
|
|
610
|
+
async function fetchTableauSession(connectionId) {
|
|
611
|
+
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
612
|
+
const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
|
|
613
|
+
if (!token || !sandboxId) {
|
|
614
|
+
throw new Error(
|
|
615
|
+
"Tableau session manager is not configured. Missing INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL or INTERNAL_SQUADBASE_SANDBOX_ID."
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
619
|
+
const url = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
453
620
|
const res = await fetch(url, {
|
|
454
621
|
method: "POST",
|
|
455
|
-
headers: {
|
|
456
|
-
"Content-Type": "application/json",
|
|
457
|
-
Accept: "application/json"
|
|
458
|
-
},
|
|
459
|
-
body: JSON.stringify(body)
|
|
622
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
460
623
|
});
|
|
461
624
|
if (!res.ok) {
|
|
462
|
-
const
|
|
625
|
+
const errBody = await res.text().catch(() => res.statusText);
|
|
463
626
|
throw new Error(
|
|
464
|
-
`Tableau
|
|
627
|
+
`Failed to fetch Tableau session from backend-api (HTTP ${res.status}): ${errBody}`
|
|
465
628
|
);
|
|
466
629
|
}
|
|
467
|
-
|
|
468
|
-
return {
|
|
469
|
-
authToken: data.credentials.token,
|
|
470
|
-
siteId: data.credentials.site.id,
|
|
471
|
-
userId: data.credentials.user.id,
|
|
472
|
-
expiresAt: Date.now() + 30 * 60 * 1e3
|
|
473
|
-
};
|
|
630
|
+
return await res.json();
|
|
474
631
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
if (forceRefresh) {
|
|
478
|
-
sessionCache.delete(cacheKey);
|
|
479
|
-
} else {
|
|
480
|
-
const cached = sessionCache.get(cacheKey);
|
|
481
|
-
if (cached && cached.expiresAt > Date.now() + 6e4) {
|
|
482
|
-
return cached;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
const existing = inFlightSignIns.get(cacheKey);
|
|
486
|
-
if (existing) return existing;
|
|
487
|
-
const promise = signIn(
|
|
488
|
-
serverUrl,
|
|
489
|
-
apiVersion,
|
|
490
|
-
siteContentUrl,
|
|
491
|
-
patName,
|
|
492
|
-
patSecret
|
|
493
|
-
).then((session) => {
|
|
494
|
-
sessionCache.set(cacheKey, session);
|
|
495
|
-
return session;
|
|
496
|
-
}).finally(() => {
|
|
497
|
-
inFlightSignIns.delete(cacheKey);
|
|
498
|
-
});
|
|
499
|
-
inFlightSignIns.set(cacheKey, promise);
|
|
500
|
-
return promise;
|
|
501
|
-
}
|
|
502
|
-
function invalidateSession(serverUrl, siteContentUrl, patName, staleAuthToken) {
|
|
503
|
-
const cacheKey = sessionCacheKey(serverUrl, siteContentUrl, patName);
|
|
504
|
-
const current = sessionCache.get(cacheKey);
|
|
505
|
-
if (current && current.authToken === staleAuthToken) {
|
|
506
|
-
sessionCache.delete(cacheKey);
|
|
507
|
-
}
|
|
632
|
+
function buildBaseUrl(serverUrl, apiVersion) {
|
|
633
|
+
return `${serverUrl.replace(/\/$/, "")}/api/${apiVersion}`;
|
|
508
634
|
}
|
|
509
635
|
var inputSchema = z.object({
|
|
510
636
|
toolUseIntent: z.string().optional().describe(
|
|
@@ -538,8 +664,8 @@ var outputSchema = z.discriminatedUnion("success", [
|
|
|
538
664
|
var requestTool = new ConnectorTool({
|
|
539
665
|
name: "request",
|
|
540
666
|
description: `Send authenticated requests to the Tableau REST API.
|
|
541
|
-
The
|
|
542
|
-
All paths are relative to {serverUrl}/api/{apiVersion}. Use the literal placeholder \`{siteId}\` in the path \u2014 it is automatically substituted with the site ID
|
|
667
|
+
The X-Tableau-Auth token is issued and managed centrally by the Squadbase backend so concurrent processes (chat & deployed app) share a single PAT sign-in.
|
|
668
|
+
All paths are relative to {serverUrl}/api/{apiVersion}. Use the literal placeholder \`{siteId}\` in the path \u2014 it is automatically substituted with the signed-in site ID.
|
|
543
669
|
Accept and Content-Type headers default to application/json so list responses come back as JSON instead of Tableau's default XML.`,
|
|
544
670
|
inputSchema,
|
|
545
671
|
outputSchema,
|
|
@@ -556,10 +682,7 @@ Accept and Content-Type headers default to application/json so list responses co
|
|
|
556
682
|
);
|
|
557
683
|
try {
|
|
558
684
|
const serverUrl = parameters.serverUrl.getValue(connection2);
|
|
559
|
-
const
|
|
560
|
-
const patName = parameters.patName.getValue(connection2);
|
|
561
|
-
const patSecret = parameters.patSecret.getValue(connection2);
|
|
562
|
-
const apiVersion = parameters.apiVersion.tryGetValue(connection2) || DEFAULT_API_VERSION2;
|
|
685
|
+
const apiVersion = parameters.apiVersion.tryGetValue(connection2) || DEFAULT_API_VERSION3;
|
|
563
686
|
const trimmedPath = path2.trim().replace(/^([^/])/, "/$1");
|
|
564
687
|
const queryString = queryParams ? `?${new URLSearchParams(queryParams).toString()}` : "";
|
|
565
688
|
const baseUrl = buildBaseUrl(serverUrl, apiVersion);
|
|
@@ -568,14 +691,7 @@ Accept and Content-Type headers default to application/json so list responses co
|
|
|
568
691
|
try {
|
|
569
692
|
let attempt = 0;
|
|
570
693
|
while (true) {
|
|
571
|
-
const session = await
|
|
572
|
-
serverUrl,
|
|
573
|
-
apiVersion,
|
|
574
|
-
siteContentUrl,
|
|
575
|
-
patName,
|
|
576
|
-
patSecret,
|
|
577
|
-
{ forceRefresh: attempt > 0 }
|
|
578
|
-
);
|
|
694
|
+
const session = await fetchTableauSession(connectionId);
|
|
579
695
|
const resolvedPath = trimmedPath.replace(
|
|
580
696
|
/\{siteId\}/g,
|
|
581
697
|
session.siteId
|
|
@@ -603,12 +719,6 @@ Accept and Content-Type headers default to application/json so list responses co
|
|
|
603
719
|
}
|
|
604
720
|
})() : null;
|
|
605
721
|
if (response.status === 401 && attempt === 0) {
|
|
606
|
-
invalidateSession(
|
|
607
|
-
serverUrl,
|
|
608
|
-
siteContentUrl,
|
|
609
|
-
patName,
|
|
610
|
-
session.authToken
|
|
611
|
-
);
|
|
612
722
|
attempt++;
|
|
613
723
|
continue;
|
|
614
724
|
}
|
|
@@ -643,7 +753,7 @@ var tableauConnector = new ConnectorPlugin({
|
|
|
643
753
|
systemPrompt: {
|
|
644
754
|
en: `### Tools
|
|
645
755
|
|
|
646
|
-
- \`tableau_request\`: The only way to call the Tableau REST API. Use it for projects, workbooks, views, data sources, users, and permissions. The
|
|
756
|
+
- \`tableau_request\`: The only way to call the Tableau REST API. Use it for projects, workbooks, views, data sources, users, and permissions. The \`X-Tableau-Auth\` token is issued and managed centrally by the Squadbase backend so concurrent processes (chat & deployed app) share a single PAT sign-in. Use the literal \`{siteId}\` placeholder in the path \u2014 it is replaced with the session's site ID automatically.
|
|
647
757
|
|
|
648
758
|
### Business Logic
|
|
649
759
|
|
|
@@ -700,7 +810,7 @@ export default async function handler(c: Context) {
|
|
|
700
810
|
- Combine: \`filter=ownerName:eq:alice,tags:in:[finance]\``,
|
|
701
811
|
ja: `### \u30C4\u30FC\u30EB
|
|
702
812
|
|
|
703
|
-
- \`tableau_request\`: Tableau REST API \u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3001\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u3001\u30D3\u30E5\u30FC\u3001\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u3001\u30E6\u30FC\u30B6\u30FC\u3001\u6A29\u9650\u306A\u3069\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\
|
|
813
|
+
- \`tableau_request\`: Tableau REST API \u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3001\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u3001\u30D3\u30E5\u30FC\u3001\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u3001\u30E6\u30FC\u30B6\u30FC\u3001\u6A29\u9650\u306A\u3069\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\`X-Tableau-Auth\` \u30C8\u30FC\u30AF\u30F3\u306F Squadbase \u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u3067\u4E00\u5143\u7BA1\u7406\u3055\u308C\u3001\u8907\u6570\u30D7\u30ED\u30BB\u30B9 (\u30C1\u30E3\u30C3\u30C8\u30FB\u30C7\u30D7\u30ED\u30A4\u6E08\u307F\u30A2\u30D7\u30EA) \u3067\u540C\u3058 PAT \u30B5\u30A4\u30F3\u30A4\u30F3\u3092\u5171\u6709\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306B\u306F \`{siteId}\` \u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044 \u2014 \u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u30B5\u30A4\u30C8 ID \u3067\u81EA\u52D5\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002
|
|
704
814
|
|
|
705
815
|
### Business Logic
|
|
706
816
|
|
|
@@ -757,6 +867,7 @@ export default async function handler(c: Context) {
|
|
|
757
867
|
- \u7D44\u307F\u5408\u308F\u305B: \`filter=ownerName:eq:alice,tags:in:[finance]\``
|
|
758
868
|
},
|
|
759
869
|
tools,
|
|
870
|
+
setup: (params, ctx, config) => runSetupFlow(tableauSetupFlow, params, ctx, config),
|
|
760
871
|
async checkConnection(params) {
|
|
761
872
|
const serverUrl = params[parameters.serverUrl.slug];
|
|
762
873
|
const siteContentUrl = params[parameters.siteContentUrl.slug];
|
|
@@ -830,6 +941,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
830
941
|
import { getContext } from "hono/context-storage";
|
|
831
942
|
import { getCookie } from "hono/cookie";
|
|
832
943
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
944
|
+
var TABLEAU_SESSION_SENTINEL_URL2 = "squadbase://tableau-session/";
|
|
833
945
|
function normalizeHeaders(input) {
|
|
834
946
|
const out = {};
|
|
835
947
|
if (!input) return out;
|
|
@@ -838,6 +950,11 @@ function normalizeHeaders(input) {
|
|
|
838
950
|
});
|
|
839
951
|
return out;
|
|
840
952
|
}
|
|
953
|
+
function extractInputUrl(input) {
|
|
954
|
+
if (typeof input === "string") return input;
|
|
955
|
+
if (input instanceof URL) return input.href;
|
|
956
|
+
return input.url;
|
|
957
|
+
}
|
|
841
958
|
function createSandboxProxyFetch(connectionId) {
|
|
842
959
|
return async (input, init) => {
|
|
843
960
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -847,10 +964,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
847
964
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
848
965
|
);
|
|
849
966
|
}
|
|
850
|
-
const originalUrl =
|
|
967
|
+
const originalUrl = extractInputUrl(input);
|
|
968
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
969
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL2) {
|
|
970
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
971
|
+
return fetch(sessionUrl, {
|
|
972
|
+
method: "POST",
|
|
973
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
974
|
+
});
|
|
975
|
+
}
|
|
851
976
|
const originalMethod = init?.method ?? "GET";
|
|
852
977
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
853
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
854
978
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
855
979
|
return fetch(proxyUrl, {
|
|
856
980
|
method: "POST",
|
|
@@ -876,10 +1000,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
876
1000
|
}
|
|
877
1001
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
878
1002
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1003
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
879
1004
|
return async (input, init) => {
|
|
880
|
-
const originalUrl =
|
|
881
|
-
const originalMethod = init?.method ?? "GET";
|
|
882
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
1005
|
+
const originalUrl = extractInputUrl(input);
|
|
883
1006
|
const c = getContext();
|
|
884
1007
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
885
1008
|
if (!appSession) {
|
|
@@ -887,6 +1010,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
887
1010
|
"No authentication method available for connection proxy."
|
|
888
1011
|
);
|
|
889
1012
|
}
|
|
1013
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL2) {
|
|
1014
|
+
return fetch(sessionUrl, {
|
|
1015
|
+
method: "POST",
|
|
1016
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
const originalMethod = init?.method ?? "GET";
|
|
1020
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
890
1021
|
return fetch(proxyUrl, {
|
|
891
1022
|
method: "POST",
|
|
892
1023
|
headers: {
|