@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
|
@@ -185,6 +185,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
185
185
|
tools;
|
|
186
186
|
query;
|
|
187
187
|
checkConnection;
|
|
188
|
+
/**
|
|
189
|
+
* SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
|
|
190
|
+
* implement this expose a step-by-step exploration flow (database/schema/
|
|
191
|
+
* table/etc. discovery) that the dashboard backend drives via the
|
|
192
|
+
* `/connections/:connectionId/setup` endpoint. Implement by delegating to
|
|
193
|
+
* `runSetupFlow` from `setup-flow.ts`.
|
|
194
|
+
*/
|
|
195
|
+
setup;
|
|
196
|
+
/**
|
|
197
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
198
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
199
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
200
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
201
|
+
*
|
|
202
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
203
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
204
|
+
* connectionId, which doesn't exist until the row is saved
|
|
205
|
+
*
|
|
206
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
207
|
+
* the default verify-on-create behavior without opt-in.
|
|
208
|
+
*/
|
|
209
|
+
skipConnectionCheckOnCreate;
|
|
188
210
|
constructor(config) {
|
|
189
211
|
this.slug = config.slug;
|
|
190
212
|
this.authType = config.authType;
|
|
@@ -201,6 +223,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
201
223
|
this.tools = config.tools;
|
|
202
224
|
this.query = config.query;
|
|
203
225
|
this.checkConnection = config.checkConnection;
|
|
226
|
+
this.setup = config.setup;
|
|
227
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
204
228
|
}
|
|
205
229
|
get connectorKey() {
|
|
206
230
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -265,6 +289,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
265
289
|
}
|
|
266
290
|
};
|
|
267
291
|
|
|
292
|
+
// ../connectors/src/setup-flow.ts
|
|
293
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
294
|
+
const runtime = {
|
|
295
|
+
params,
|
|
296
|
+
language: ctx.language,
|
|
297
|
+
config
|
|
298
|
+
};
|
|
299
|
+
let state = flow.initialState();
|
|
300
|
+
let answerIdx = 0;
|
|
301
|
+
for (const step of flow.steps) {
|
|
302
|
+
const ans = ctx.answers[answerIdx];
|
|
303
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
304
|
+
state = step.applyAnswer(state, ans.answer);
|
|
305
|
+
answerIdx += 1;
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (step.type === "text") {
|
|
309
|
+
return {
|
|
310
|
+
type: "nextQuestion",
|
|
311
|
+
questionSlug: step.slug,
|
|
312
|
+
question: step.question[ctx.language],
|
|
313
|
+
questionType: "text"
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
317
|
+
if (options.length === 0) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
type: "nextQuestion",
|
|
322
|
+
questionSlug: step.slug,
|
|
323
|
+
question: step.question[ctx.language],
|
|
324
|
+
questionType: step.type,
|
|
325
|
+
options
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
329
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
330
|
+
}
|
|
331
|
+
async function resolveSetupSelection(params) {
|
|
332
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
333
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
334
|
+
return resolved.slice(0, limit);
|
|
335
|
+
}
|
|
336
|
+
|
|
268
337
|
// ../connectors/src/auth-types.ts
|
|
269
338
|
var AUTH_TYPES = {
|
|
270
339
|
OAUTH: "oauth",
|
|
@@ -463,6 +532,89 @@ Calendar
|
|
|
463
532
|
}
|
|
464
533
|
});
|
|
465
534
|
|
|
535
|
+
// ../connectors/src/connectors/outlook-oauth/utils.ts
|
|
536
|
+
async function graphApiFetch(config, url) {
|
|
537
|
+
const res = await config.proxyFetch(url, { method: "GET" });
|
|
538
|
+
if (!res.ok) {
|
|
539
|
+
const text = await res.text().catch(() => res.statusText);
|
|
540
|
+
throw new Error(`Microsoft Graph ${url} failed: HTTP ${res.status} ${text}`);
|
|
541
|
+
}
|
|
542
|
+
return await res.json();
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// ../connectors/src/connectors/outlook-oauth/setup-flow.ts
|
|
546
|
+
var ALL_FOLDERS = "__ALL_FOLDERS__";
|
|
547
|
+
var OUTLOOK_SETUP_MAX_FOLDERS = 20;
|
|
548
|
+
async function listMailFolders(config) {
|
|
549
|
+
const url = "https://graph.microsoft.com/v1.0/me/mailFolders?$top=100&$select=id,displayName,parentFolderId,childFolderCount,unreadItemCount,totalItemCount";
|
|
550
|
+
const data = await graphApiFetch(config, url);
|
|
551
|
+
return data.value ?? [];
|
|
552
|
+
}
|
|
553
|
+
var outlookOauthSetupFlow = {
|
|
554
|
+
initialState: () => ({}),
|
|
555
|
+
steps: [
|
|
556
|
+
{
|
|
557
|
+
slug: "folders",
|
|
558
|
+
type: "multiSelect",
|
|
559
|
+
question: {
|
|
560
|
+
ja: "\u5BFE\u8C61\u306E\u30E1\u30FC\u30EB\u30D5\u30A9\u30EB\u30C0\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
561
|
+
en: "Select target mail folders (multi-select allowed)"
|
|
562
|
+
},
|
|
563
|
+
async fetchOptions(_state, rt) {
|
|
564
|
+
const folders = await listMailFolders(rt.config);
|
|
565
|
+
return [
|
|
566
|
+
{
|
|
567
|
+
value: ALL_FOLDERS,
|
|
568
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D5\u30A9\u30EB\u30C0" : "All folders"
|
|
569
|
+
},
|
|
570
|
+
...folders.map((f) => ({
|
|
571
|
+
value: f.id,
|
|
572
|
+
label: f.displayName ?? f.id
|
|
573
|
+
}))
|
|
574
|
+
];
|
|
575
|
+
},
|
|
576
|
+
applyAnswer: (state, answer) => ({ ...state, folders: answer })
|
|
577
|
+
}
|
|
578
|
+
],
|
|
579
|
+
async finalize(state, rt) {
|
|
580
|
+
if (!state.folders) {
|
|
581
|
+
throw new Error("Outlook setup: incomplete state on finalize");
|
|
582
|
+
}
|
|
583
|
+
const folders = await listMailFolders(rt.config);
|
|
584
|
+
const byId = new Map(folders.map((f) => [f.id, f]));
|
|
585
|
+
const targetIds = await resolveSetupSelection({
|
|
586
|
+
selected: state.folders,
|
|
587
|
+
allSentinel: ALL_FOLDERS,
|
|
588
|
+
fetchAll: async () => folders.map((f) => f.id),
|
|
589
|
+
limit: OUTLOOK_SETUP_MAX_FOLDERS
|
|
590
|
+
});
|
|
591
|
+
const sections = ["## Outlook", ""];
|
|
592
|
+
if (targetIds.length === 0) {
|
|
593
|
+
sections.push(
|
|
594
|
+
rt.language === "ja" ? "\u5BFE\u8C61\u306E\u30D5\u30A9\u30EB\u30C0\u304C\u9078\u629E\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002" : "No folders selected."
|
|
595
|
+
);
|
|
596
|
+
return sections.join("\n");
|
|
597
|
+
}
|
|
598
|
+
sections.push("| Folder | Total | Unread | Sub-folders |");
|
|
599
|
+
sections.push("|--------|-------|--------|-------------|");
|
|
600
|
+
for (const id of targetIds) {
|
|
601
|
+
const f = byId.get(id);
|
|
602
|
+
if (!f) {
|
|
603
|
+
sections.push(`| ${id} | - | - | - |`);
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
const total = f.totalItemCount ?? "-";
|
|
607
|
+
const unread = f.unreadItemCount ?? "-";
|
|
608
|
+
const subs = f.childFolderCount ?? 0;
|
|
609
|
+
sections.push(
|
|
610
|
+
`| ${f.displayName ?? id} | ${total} | ${unread} | ${subs} |`
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
sections.push("");
|
|
614
|
+
return sections.join("\n");
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
|
|
466
618
|
// ../connectors/src/connectors/outlook-oauth/parameters.ts
|
|
467
619
|
var parameters = {};
|
|
468
620
|
|
|
@@ -471,6 +623,7 @@ var tools = { request: requestTool };
|
|
|
471
623
|
var outlookOauthConnector = new ConnectorPlugin({
|
|
472
624
|
slug: "outlook",
|
|
473
625
|
authType: AUTH_TYPES.OAUTH,
|
|
626
|
+
skipConnectionCheckOnCreate: true,
|
|
474
627
|
name: "Outlook",
|
|
475
628
|
description: "Connect to Microsoft Outlook (Mail + Calendar) via Microsoft Graph using OAuth. Read-only access to the user's mailbox, mail folders, messages, attachments, calendars, and events.",
|
|
476
629
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/1J1FrRTYJjOh3CcSIqsz3I/6a467b4d926075ff99dc60820e0ae4b1/Microsoft_Outlook_Icon__2025%C3%A2__present_.svg",
|
|
@@ -755,6 +908,7 @@ events.value.forEach(e => console.log(e.start.dateTime, e.subject));
|
|
|
755
908
|
\`\`\``
|
|
756
909
|
},
|
|
757
910
|
tools,
|
|
911
|
+
setup: (params, ctx, config) => runSetupFlow(outlookOauthSetupFlow, params, ctx, config),
|
|
758
912
|
async checkConnection(_params, config) {
|
|
759
913
|
const { proxyFetch } = config;
|
|
760
914
|
const url = "https://graph.microsoft.com/v1.0/me";
|
|
@@ -803,6 +957,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
803
957
|
import { getContext } from "hono/context-storage";
|
|
804
958
|
import { getCookie } from "hono/cookie";
|
|
805
959
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
960
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
806
961
|
function normalizeHeaders(input) {
|
|
807
962
|
const out = {};
|
|
808
963
|
if (!input) return out;
|
|
@@ -811,6 +966,11 @@ function normalizeHeaders(input) {
|
|
|
811
966
|
});
|
|
812
967
|
return out;
|
|
813
968
|
}
|
|
969
|
+
function extractInputUrl(input) {
|
|
970
|
+
if (typeof input === "string") return input;
|
|
971
|
+
if (input instanceof URL) return input.href;
|
|
972
|
+
return input.url;
|
|
973
|
+
}
|
|
814
974
|
function createSandboxProxyFetch(connectionId) {
|
|
815
975
|
return async (input, init) => {
|
|
816
976
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -820,10 +980,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
820
980
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
821
981
|
);
|
|
822
982
|
}
|
|
823
|
-
const originalUrl =
|
|
983
|
+
const originalUrl = extractInputUrl(input);
|
|
984
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
985
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
986
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
987
|
+
return fetch(sessionUrl, {
|
|
988
|
+
method: "POST",
|
|
989
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
990
|
+
});
|
|
991
|
+
}
|
|
824
992
|
const originalMethod = init?.method ?? "GET";
|
|
825
993
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
826
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
827
994
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
828
995
|
return fetch(proxyUrl, {
|
|
829
996
|
method: "POST",
|
|
@@ -849,10 +1016,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
849
1016
|
}
|
|
850
1017
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
851
1018
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1019
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
852
1020
|
return async (input, init) => {
|
|
853
|
-
const originalUrl =
|
|
854
|
-
const originalMethod = init?.method ?? "GET";
|
|
855
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
1021
|
+
const originalUrl = extractInputUrl(input);
|
|
856
1022
|
const c = getContext();
|
|
857
1023
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
858
1024
|
if (!appSession) {
|
|
@@ -860,6 +1026,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
860
1026
|
"No authentication method available for connection proxy."
|
|
861
1027
|
);
|
|
862
1028
|
}
|
|
1029
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1030
|
+
return fetch(sessionUrl, {
|
|
1031
|
+
method: "POST",
|
|
1032
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
const originalMethod = init?.method ?? "GET";
|
|
1036
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
863
1037
|
return fetch(proxyUrl, {
|
|
864
1038
|
method: "POST",
|
|
865
1039
|
headers: {
|
|
@@ -143,6 +143,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
143
143
|
tools;
|
|
144
144
|
query;
|
|
145
145
|
checkConnection;
|
|
146
|
+
/**
|
|
147
|
+
* SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
|
|
148
|
+
* implement this expose a step-by-step exploration flow (database/schema/
|
|
149
|
+
* table/etc. discovery) that the dashboard backend drives via the
|
|
150
|
+
* `/connections/:connectionId/setup` endpoint. Implement by delegating to
|
|
151
|
+
* `runSetupFlow` from `setup-flow.ts`.
|
|
152
|
+
*/
|
|
153
|
+
setup;
|
|
154
|
+
/**
|
|
155
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
156
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
157
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
158
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
159
|
+
*
|
|
160
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
161
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
162
|
+
* connectionId, which doesn't exist until the row is saved
|
|
163
|
+
*
|
|
164
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
165
|
+
* the default verify-on-create behavior without opt-in.
|
|
166
|
+
*/
|
|
167
|
+
skipConnectionCheckOnCreate;
|
|
146
168
|
constructor(config) {
|
|
147
169
|
this.slug = config.slug;
|
|
148
170
|
this.authType = config.authType;
|
|
@@ -159,6 +181,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
159
181
|
this.tools = config.tools;
|
|
160
182
|
this.query = config.query;
|
|
161
183
|
this.checkConnection = config.checkConnection;
|
|
184
|
+
this.setup = config.setup;
|
|
185
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
162
186
|
}
|
|
163
187
|
get connectorKey() {
|
|
164
188
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -223,6 +247,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
223
247
|
}
|
|
224
248
|
};
|
|
225
249
|
|
|
250
|
+
// ../connectors/src/setup-flow.ts
|
|
251
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
252
|
+
const runtime = {
|
|
253
|
+
params,
|
|
254
|
+
language: ctx.language,
|
|
255
|
+
config
|
|
256
|
+
};
|
|
257
|
+
let state = flow.initialState();
|
|
258
|
+
let answerIdx = 0;
|
|
259
|
+
for (const step of flow.steps) {
|
|
260
|
+
const ans = ctx.answers[answerIdx];
|
|
261
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
262
|
+
state = step.applyAnswer(state, ans.answer);
|
|
263
|
+
answerIdx += 1;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (step.type === "text") {
|
|
267
|
+
return {
|
|
268
|
+
type: "nextQuestion",
|
|
269
|
+
questionSlug: step.slug,
|
|
270
|
+
question: step.question[ctx.language],
|
|
271
|
+
questionType: "text"
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
275
|
+
if (options.length === 0) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
type: "nextQuestion",
|
|
280
|
+
questionSlug: step.slug,
|
|
281
|
+
question: step.question[ctx.language],
|
|
282
|
+
questionType: step.type,
|
|
283
|
+
options
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
287
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
288
|
+
}
|
|
289
|
+
async function resolveSetupSelection(params) {
|
|
290
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
291
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
292
|
+
return resolved.slice(0, limit);
|
|
293
|
+
}
|
|
294
|
+
|
|
226
295
|
// ../connectors/src/auth-types.ts
|
|
227
296
|
var AUTH_TYPES = {
|
|
228
297
|
OAUTH: "oauth",
|
|
@@ -411,11 +480,142 @@ var powerbiOauthOnboarding = new ConnectorOnboarding({
|
|
|
411
480
|
// ../connectors/src/connectors/powerbi-oauth/parameters.ts
|
|
412
481
|
var parameters = {};
|
|
413
482
|
|
|
483
|
+
// ../connectors/src/connectors/powerbi-oauth/utils.ts
|
|
484
|
+
var BASE_URL3 = "https://api.powerbi.com/v1.0/myorg";
|
|
485
|
+
function apiFetch(proxyFetch, path2, init) {
|
|
486
|
+
const url = `${BASE_URL3}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
487
|
+
return proxyFetch(url, init);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ../connectors/src/connectors/powerbi-oauth/setup-flow.ts
|
|
491
|
+
var ALL_WORKSPACES = "__ALL_WORKSPACES__";
|
|
492
|
+
var POWERBI_SETUP_MAX_WORKSPACES = 10;
|
|
493
|
+
var RESOURCE_DISPLAY_LIMIT = 25;
|
|
494
|
+
var RESOURCE_DATASETS = "datasets";
|
|
495
|
+
var RESOURCE_REPORTS = "reports";
|
|
496
|
+
var RESOURCE_DASHBOARDS = "dashboards";
|
|
497
|
+
async function listGroups(proxyFetch) {
|
|
498
|
+
const res = await apiFetch(proxyFetch, "/groups");
|
|
499
|
+
if (!res.ok) {
|
|
500
|
+
const body = await res.text().catch(() => res.statusText);
|
|
501
|
+
throw new Error(`powerbi: listGroups failed (${res.status}): ${body}`);
|
|
502
|
+
}
|
|
503
|
+
const data = await res.json();
|
|
504
|
+
return data.value ?? [];
|
|
505
|
+
}
|
|
506
|
+
async function listResource(proxyFetch, groupId, resource) {
|
|
507
|
+
const res = await apiFetch(
|
|
508
|
+
proxyFetch,
|
|
509
|
+
`/groups/${encodeURIComponent(groupId)}/${resource}`
|
|
510
|
+
);
|
|
511
|
+
if (!res.ok) {
|
|
512
|
+
const body = await res.text().catch(() => res.statusText);
|
|
513
|
+
throw new Error(
|
|
514
|
+
`powerbi: list ${resource} for group ${groupId} failed (${res.status}): ${body}`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
const data = await res.json();
|
|
518
|
+
return data.value ?? [];
|
|
519
|
+
}
|
|
520
|
+
function resourceLabel(r) {
|
|
521
|
+
return r.name ?? r.displayName ?? r.id ?? "(unknown)";
|
|
522
|
+
}
|
|
523
|
+
var powerbiOauthSetupFlow = {
|
|
524
|
+
initialState: () => ({}),
|
|
525
|
+
steps: [
|
|
526
|
+
{
|
|
527
|
+
slug: "workspaces",
|
|
528
|
+
type: "multiSelect",
|
|
529
|
+
question: {
|
|
530
|
+
ja: "\u5BFE\u8C61\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
531
|
+
en: "Select target workspaces (multi-select allowed)"
|
|
532
|
+
},
|
|
533
|
+
async fetchOptions(_state, rt) {
|
|
534
|
+
const groups = await listGroups(rt.config.proxyFetch);
|
|
535
|
+
const options = groups.filter((g) => g.id && g.name).map((g) => ({ value: g.id, label: g.name }));
|
|
536
|
+
return [
|
|
537
|
+
{
|
|
538
|
+
value: ALL_WORKSPACES,
|
|
539
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "All workspaces"
|
|
540
|
+
},
|
|
541
|
+
...options
|
|
542
|
+
];
|
|
543
|
+
},
|
|
544
|
+
applyAnswer: (state, answer) => ({ ...state, workspaces: answer })
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
slug: "resources",
|
|
548
|
+
type: "multiSelect",
|
|
549
|
+
question: {
|
|
550
|
+
ja: "\u8981\u7D04\u306B\u542B\u3081\u308B\u30EA\u30BD\u30FC\u30B9\u7A2E\u5225\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
551
|
+
en: "Select which resource types to include in the summary"
|
|
552
|
+
},
|
|
553
|
+
async fetchOptions(state, rt) {
|
|
554
|
+
if (!state.workspaces?.length) return [];
|
|
555
|
+
const datasetsLabel = rt.language === "ja" ? "\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8" : "Datasets";
|
|
556
|
+
const reportsLabel = rt.language === "ja" ? "\u30EC\u30DD\u30FC\u30C8" : "Reports";
|
|
557
|
+
const dashboardsLabel = rt.language === "ja" ? "\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9" : "Dashboards";
|
|
558
|
+
return [
|
|
559
|
+
{ value: RESOURCE_DATASETS, label: datasetsLabel },
|
|
560
|
+
{ value: RESOURCE_REPORTS, label: reportsLabel },
|
|
561
|
+
{ value: RESOURCE_DASHBOARDS, label: dashboardsLabel }
|
|
562
|
+
];
|
|
563
|
+
},
|
|
564
|
+
applyAnswer: (state, answer) => ({ ...state, resources: answer })
|
|
565
|
+
}
|
|
566
|
+
],
|
|
567
|
+
async finalize(state, rt) {
|
|
568
|
+
if (!state.workspaces || !state.resources) {
|
|
569
|
+
throw new Error("Power BI setup: incomplete state on finalize");
|
|
570
|
+
}
|
|
571
|
+
const allGroups = await listGroups(rt.config.proxyFetch);
|
|
572
|
+
const groupById = new Map(allGroups.map((g) => [g.id, g]));
|
|
573
|
+
const targetIds = await resolveSetupSelection({
|
|
574
|
+
selected: state.workspaces,
|
|
575
|
+
allSentinel: ALL_WORKSPACES,
|
|
576
|
+
fetchAll: async () => allGroups.map((g) => g.id).filter((id) => id),
|
|
577
|
+
limit: POWERBI_SETUP_MAX_WORKSPACES
|
|
578
|
+
});
|
|
579
|
+
const selectedResources = new Set(state.resources);
|
|
580
|
+
const sections = ["## Power BI", ""];
|
|
581
|
+
if (!targetIds.length) {
|
|
582
|
+
sections.push("_No workspaces selected._", "");
|
|
583
|
+
return sections.join("\n");
|
|
584
|
+
}
|
|
585
|
+
for (const id of targetIds) {
|
|
586
|
+
const group = groupById.get(id);
|
|
587
|
+
const name = group?.name ?? id;
|
|
588
|
+
sections.push(`### Workspace: ${name}`, "", `- id: \`${id}\``);
|
|
589
|
+
for (const resource of [
|
|
590
|
+
RESOURCE_DATASETS,
|
|
591
|
+
RESOURCE_REPORTS,
|
|
592
|
+
RESOURCE_DASHBOARDS
|
|
593
|
+
]) {
|
|
594
|
+
if (!selectedResources.has(resource)) continue;
|
|
595
|
+
const items = await listResource(rt.config.proxyFetch, id, resource);
|
|
596
|
+
const heading = resource === RESOURCE_DATASETS ? "Datasets" : resource === RESOURCE_REPORTS ? "Reports" : "Dashboards";
|
|
597
|
+
sections.push(`- ${heading} (${items.length}):`);
|
|
598
|
+
for (const item of items.slice(0, RESOURCE_DISPLAY_LIMIT)) {
|
|
599
|
+
sections.push(` - ${resourceLabel(item)}`);
|
|
600
|
+
}
|
|
601
|
+
if (items.length > RESOURCE_DISPLAY_LIMIT) {
|
|
602
|
+
sections.push(
|
|
603
|
+
` - \u2026and ${items.length - RESOURCE_DISPLAY_LIMIT} more`
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
sections.push("");
|
|
608
|
+
}
|
|
609
|
+
return sections.join("\n");
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
|
|
414
613
|
// ../connectors/src/connectors/powerbi-oauth/index.ts
|
|
415
614
|
var tools = { request: requestTool };
|
|
416
615
|
var powerbiOauthConnector = new ConnectorPlugin({
|
|
417
616
|
slug: "powerbi",
|
|
418
617
|
authType: AUTH_TYPES.OAUTH,
|
|
618
|
+
skipConnectionCheckOnCreate: true,
|
|
419
619
|
name: "Power BI",
|
|
420
620
|
description: "Connect to Microsoft Power BI using OAuth (Microsoft Entra ID). Use it to enumerate workspaces, datasets, and reports the signed-in user has access to, and to run DAX queries.",
|
|
421
621
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/2vXQCKGpMJ9kGSaqkZl9IS/cc5669c267fc5d11e7b1f8c01723e461/power-bi-icon.png",
|
|
@@ -546,6 +746,7 @@ export default async function handler(c: Context) {
|
|
|
546
746
|
- \`executeQueries\` \u306F\u73FE\u72B6 1 \u30EA\u30AF\u30A8\u30B9\u30C8\u306B\u3064\u304D 1 \u30AF\u30A8\u30EA\u306E\u307F`
|
|
547
747
|
},
|
|
548
748
|
tools,
|
|
749
|
+
setup: (params, ctx, config) => runSetupFlow(powerbiOauthSetupFlow, params, ctx, config),
|
|
549
750
|
async checkConnection(_params, config) {
|
|
550
751
|
const { proxyFetch } = config;
|
|
551
752
|
const url = "https://api.powerbi.com/v1.0/myorg/groups?$top=1";
|
|
@@ -594,6 +795,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
594
795
|
import { getContext } from "hono/context-storage";
|
|
595
796
|
import { getCookie } from "hono/cookie";
|
|
596
797
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
798
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
597
799
|
function normalizeHeaders(input) {
|
|
598
800
|
const out = {};
|
|
599
801
|
if (!input) return out;
|
|
@@ -602,6 +804,11 @@ function normalizeHeaders(input) {
|
|
|
602
804
|
});
|
|
603
805
|
return out;
|
|
604
806
|
}
|
|
807
|
+
function extractInputUrl(input) {
|
|
808
|
+
if (typeof input === "string") return input;
|
|
809
|
+
if (input instanceof URL) return input.href;
|
|
810
|
+
return input.url;
|
|
811
|
+
}
|
|
605
812
|
function createSandboxProxyFetch(connectionId) {
|
|
606
813
|
return async (input, init) => {
|
|
607
814
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -611,10 +818,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
611
818
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
612
819
|
);
|
|
613
820
|
}
|
|
614
|
-
const originalUrl =
|
|
821
|
+
const originalUrl = extractInputUrl(input);
|
|
822
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
823
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
824
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
825
|
+
return fetch(sessionUrl, {
|
|
826
|
+
method: "POST",
|
|
827
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
828
|
+
});
|
|
829
|
+
}
|
|
615
830
|
const originalMethod = init?.method ?? "GET";
|
|
616
831
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
617
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
618
832
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
619
833
|
return fetch(proxyUrl, {
|
|
620
834
|
method: "POST",
|
|
@@ -640,10 +854,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
640
854
|
}
|
|
641
855
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
642
856
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
857
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
643
858
|
return async (input, init) => {
|
|
644
|
-
const originalUrl =
|
|
645
|
-
const originalMethod = init?.method ?? "GET";
|
|
646
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
859
|
+
const originalUrl = extractInputUrl(input);
|
|
647
860
|
const c = getContext();
|
|
648
861
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
649
862
|
if (!appSession) {
|
|
@@ -651,6 +864,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
651
864
|
"No authentication method available for connection proxy."
|
|
652
865
|
);
|
|
653
866
|
}
|
|
867
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
868
|
+
return fetch(sessionUrl, {
|
|
869
|
+
method: "POST",
|
|
870
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
const originalMethod = init?.method ?? "GET";
|
|
874
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
654
875
|
return fetch(proxyUrl, {
|
|
655
876
|
method: "POST",
|
|
656
877
|
headers: {
|