@squadbase/vite-server 0.1.12-dev.a9ac647 → 0.1.17-dev.3b633bb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +14375 -1652
- package/dist/connectors/airtable-oauth.js +282 -46
- package/dist/connectors/airtable.js +319 -51
- package/dist/connectors/amplitude.js +322 -47
- package/dist/connectors/anthropic.js +135 -47
- package/dist/connectors/asana.js +327 -49
- package/dist/connectors/attio.js +302 -49
- package/dist/connectors/aws-billing.js +287 -46
- package/dist/connectors/azure-sql.js +421 -102
- package/dist/connectors/backlog-api-key.js +317 -47
- package/dist/connectors/clickup.js +338 -49
- package/dist/connectors/cosmosdb.js +305 -50
- package/dist/connectors/customerio.js +319 -47
- package/dist/connectors/dbt.js +340 -47
- package/dist/connectors/freshdesk.js +342 -53
- package/dist/connectors/freshsales.js +333 -52
- package/dist/connectors/freshservice.js +361 -53
- package/dist/connectors/gamma.js +327 -52
- package/dist/connectors/gemini.js +134 -47
- package/dist/connectors/github.js +386 -49
- package/dist/connectors/gmail-oauth.js +204 -7
- package/dist/connectors/gmail.js +350 -47
- package/dist/connectors/google-ads.js +288 -46
- package/dist/connectors/google-analytics-oauth.js +310 -46
- package/dist/connectors/google-analytics.js +547 -87
- package/dist/connectors/google-audit-log.js +438 -47
- package/dist/connectors/google-calendar-oauth.js +259 -46
- package/dist/connectors/google-calendar.js +359 -47
- package/dist/connectors/google-docs.js +220 -6
- package/dist/connectors/google-drive.js +262 -5
- package/dist/connectors/google-search-console-oauth.js +256 -46
- package/dist/connectors/google-sheets.js +272 -47
- package/dist/connectors/google-slides.js +205 -6
- package/dist/connectors/grafana.js +332 -49
- package/dist/connectors/hubspot-oauth.js +208 -5
- package/dist/connectors/hubspot.js +306 -49
- package/dist/connectors/influxdb.js +416 -51
- package/dist/connectors/intercom-oauth.js +210 -5
- package/dist/connectors/intercom.js +302 -49
- package/dist/connectors/jdbc.js +762 -110
- package/dist/connectors/jira-api-key.js +326 -47
- package/dist/connectors/kintone-api-token.js +281 -47
- package/dist/connectors/kintone.js +328 -47
- package/dist/connectors/linear.js +330 -49
- package/dist/connectors/linkedin-ads.js +268 -50
- package/dist/connectors/mailchimp-oauth.js +268 -46
- package/dist/connectors/mailchimp.js +320 -49
- package/dist/connectors/meta-ads-oauth.js +273 -48
- package/dist/connectors/meta-ads.js +285 -50
- package/dist/connectors/mixpanel.js +338 -47
- package/dist/connectors/monday.js +360 -49
- package/dist/connectors/mongodb.js +319 -57
- package/dist/connectors/notion-oauth.js +231 -5
- package/dist/connectors/notion.js +323 -51
- package/dist/connectors/openai.js +134 -47
- package/dist/connectors/oracle.js +454 -103
- package/dist/connectors/outlook-oauth.js +204 -5
- package/dist/connectors/powerbi-oauth.js +498 -5
- package/dist/connectors/salesforce.js +384 -49
- package/dist/connectors/semrush.js +609 -49
- package/dist/connectors/sentry.js +289 -50
- package/dist/connectors/shopify-oauth.js +187 -5
- package/dist/connectors/shopify.js +357 -47
- package/dist/connectors/sqlserver.js +415 -102
- package/dist/connectors/stripe-api-key.js +269 -46
- package/dist/connectors/stripe-oauth.js +202 -5
- package/dist/connectors/supabase.js +303 -48
- package/dist/connectors/tableau.js +536 -163
- package/dist/connectors/tiktok-ads.js +279 -48
- package/dist/connectors/wix-store.js +320 -49
- package/dist/connectors/zendesk-oauth.js +239 -5
- package/dist/connectors/zendesk.js +358 -47
- package/dist/index.d.ts +149 -1
- package/dist/index.js +15057 -2117
- package/dist/main.js +15005 -2073
- package/dist/vite-plugin.js +14752 -2019
- package/package.json +1 -1
|
@@ -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,76 @@ 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
|
+
const pendingParameterUpdates = [];
|
|
260
|
+
for (const step of flow.steps) {
|
|
261
|
+
const ans = ctx.answers[answerIdx];
|
|
262
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
263
|
+
state = step.applyAnswer(state, ans.answer);
|
|
264
|
+
if (step.toParameterUpdates) {
|
|
265
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
266
|
+
}
|
|
267
|
+
answerIdx += 1;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
271
|
+
if (step.type === "text") {
|
|
272
|
+
if (step.fetchOptions) {
|
|
273
|
+
const options2 = await step.fetchOptions(state, runtime);
|
|
274
|
+
if (options2.length === 0) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
type: "nextQuestion",
|
|
280
|
+
questionSlug: step.slug,
|
|
281
|
+
question: step.question[ctx.language],
|
|
282
|
+
questionType: "text",
|
|
283
|
+
allowFreeText: resolvedAllowFreeText,
|
|
284
|
+
...pendingParameterUpdates.length > 0 && {
|
|
285
|
+
parameterUpdates: pendingParameterUpdates
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
290
|
+
if (options.length === 0) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
type: "nextQuestion",
|
|
295
|
+
questionSlug: step.slug,
|
|
296
|
+
question: step.question[ctx.language],
|
|
297
|
+
questionType: step.type,
|
|
298
|
+
options,
|
|
299
|
+
allowFreeText: resolvedAllowFreeText,
|
|
300
|
+
...pendingParameterUpdates.length > 0 && {
|
|
301
|
+
parameterUpdates: pendingParameterUpdates
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
306
|
+
return {
|
|
307
|
+
type: "fulfilled",
|
|
308
|
+
dataInvestigationResult,
|
|
309
|
+
...pendingParameterUpdates.length > 0 && {
|
|
310
|
+
parameterUpdates: pendingParameterUpdates
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
async function resolveSetupSelection(params) {
|
|
315
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
316
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
317
|
+
return resolved.slice(0, limit);
|
|
318
|
+
}
|
|
319
|
+
|
|
226
320
|
// ../connectors/src/auth-types.ts
|
|
227
321
|
var AUTH_TYPES = {
|
|
228
322
|
OAUTH: "oauth",
|
|
@@ -411,11 +505,389 @@ var powerbiOauthOnboarding = new ConnectorOnboarding({
|
|
|
411
505
|
// ../connectors/src/connectors/powerbi-oauth/parameters.ts
|
|
412
506
|
var parameters = {};
|
|
413
507
|
|
|
508
|
+
// ../connectors/src/connectors/powerbi-oauth/utils.ts
|
|
509
|
+
var BASE_URL3 = "https://api.powerbi.com/v1.0/myorg";
|
|
510
|
+
function apiFetch(proxyFetch, path2, init) {
|
|
511
|
+
const url = `${BASE_URL3}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
512
|
+
return proxyFetch(url, init);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// ../connectors/src/connectors/powerbi-oauth/setup-flow.ts
|
|
516
|
+
var ALL_WORKSPACES = "__ALL_WORKSPACES__";
|
|
517
|
+
var MY_WORKSPACE = "__MY_WORKSPACE__";
|
|
518
|
+
var ALL_DATASETS = "__ALL_DATASETS__";
|
|
519
|
+
var ALL_REPORTS = "__ALL_REPORTS__";
|
|
520
|
+
var ALL_DASHBOARDS = "__ALL_DASHBOARDS__";
|
|
521
|
+
var POWERBI_SETUP_MAX_WORKSPACES = 10;
|
|
522
|
+
var RESOURCE_DISPLAY_LIMIT = 25;
|
|
523
|
+
var RESOURCE_DATASETS = "datasets";
|
|
524
|
+
var RESOURCE_REPORTS = "reports";
|
|
525
|
+
var RESOURCE_DASHBOARDS = "dashboards";
|
|
526
|
+
async function listGroups(proxyFetch) {
|
|
527
|
+
const res = await apiFetch(proxyFetch, "/groups");
|
|
528
|
+
if (!res.ok) {
|
|
529
|
+
const body = await res.text().catch(() => res.statusText);
|
|
530
|
+
throw new Error(`powerbi: listGroups failed (${res.status}): ${body}`);
|
|
531
|
+
}
|
|
532
|
+
const data = await res.json();
|
|
533
|
+
return data.value ?? [];
|
|
534
|
+
}
|
|
535
|
+
async function listResource(proxyFetch, groupId, resource) {
|
|
536
|
+
const path2 = groupId === MY_WORKSPACE ? `/${resource}` : `/groups/${encodeURIComponent(groupId)}/${resource}`;
|
|
537
|
+
const res = await apiFetch(proxyFetch, path2);
|
|
538
|
+
if (!res.ok) {
|
|
539
|
+
const body = await res.text().catch(() => res.statusText);
|
|
540
|
+
throw new Error(
|
|
541
|
+
`powerbi: list ${resource} for ${groupId === MY_WORKSPACE ? "My workspace" : `group ${groupId}`} failed (${res.status}): ${body}`
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
const data = await res.json();
|
|
545
|
+
return data.value ?? [];
|
|
546
|
+
}
|
|
547
|
+
function resourceLabel(r) {
|
|
548
|
+
return r.name ?? r.displayName ?? r.id ?? "(unknown)";
|
|
549
|
+
}
|
|
550
|
+
var INTERNAL_TABLE_PREFIXES = [
|
|
551
|
+
"DateTableTemplate_",
|
|
552
|
+
"LocalDateTable_"
|
|
553
|
+
];
|
|
554
|
+
function isInternalColumn(name) {
|
|
555
|
+
return /^RowNumber-[0-9A-Fa-f-]+$/.test(name);
|
|
556
|
+
}
|
|
557
|
+
async function fetchDatasetSchema(proxyFetch, wsId, datasetId) {
|
|
558
|
+
const pathPrefix = wsId === MY_WORKSPACE ? "" : `/groups/${encodeURIComponent(wsId)}`;
|
|
559
|
+
const daxQuery = 'EVALUATE SELECTCOLUMNS(COLUMNSTATISTICS(), "T", [Table Name], "C", [Column Name])';
|
|
560
|
+
const res = await apiFetch(
|
|
561
|
+
proxyFetch,
|
|
562
|
+
`${pathPrefix}/datasets/${encodeURIComponent(datasetId)}/executeQueries`,
|
|
563
|
+
{
|
|
564
|
+
method: "POST",
|
|
565
|
+
headers: { "Content-Type": "application/json" },
|
|
566
|
+
body: JSON.stringify({
|
|
567
|
+
queries: [{ query: daxQuery }],
|
|
568
|
+
serializerSettings: { includeNulls: true }
|
|
569
|
+
})
|
|
570
|
+
}
|
|
571
|
+
);
|
|
572
|
+
if (!res.ok) return /* @__PURE__ */ new Map();
|
|
573
|
+
const data = await res.json();
|
|
574
|
+
const rows = data.results?.[0]?.tables?.[0]?.rows ?? [];
|
|
575
|
+
const schema = /* @__PURE__ */ new Map();
|
|
576
|
+
for (const row of rows) {
|
|
577
|
+
const table = row["[T]"] ?? "";
|
|
578
|
+
const column = row["[C]"] ?? "";
|
|
579
|
+
if (!table || !column) continue;
|
|
580
|
+
if (INTERNAL_TABLE_PREFIXES.some((p) => table.startsWith(p))) continue;
|
|
581
|
+
if (isInternalColumn(column)) continue;
|
|
582
|
+
if (!schema.has(table)) schema.set(table, []);
|
|
583
|
+
schema.get(table).push(column);
|
|
584
|
+
}
|
|
585
|
+
return schema;
|
|
586
|
+
}
|
|
587
|
+
function workspaceName(wsId, groupById, language) {
|
|
588
|
+
if (wsId === MY_WORKSPACE) {
|
|
589
|
+
return language === "ja" ? "\u30DE\u30A4 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "My workspace";
|
|
590
|
+
}
|
|
591
|
+
return groupById.get(wsId)?.name ?? wsId;
|
|
592
|
+
}
|
|
593
|
+
async function resolveWorkspaceIds(selected, proxyFetch) {
|
|
594
|
+
return resolveSetupSelection({
|
|
595
|
+
selected,
|
|
596
|
+
allSentinel: ALL_WORKSPACES,
|
|
597
|
+
fetchAll: async () => {
|
|
598
|
+
const groups = await listGroups(proxyFetch);
|
|
599
|
+
return [MY_WORKSPACE, ...groups.map((g) => g.id).filter(Boolean)];
|
|
600
|
+
},
|
|
601
|
+
limit: POWERBI_SETUP_MAX_WORKSPACES
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
function compoundKey(wsId, objectId) {
|
|
605
|
+
return `${wsId}:${objectId}`;
|
|
606
|
+
}
|
|
607
|
+
function parseCompoundKey(key) {
|
|
608
|
+
const idx = key.indexOf(":");
|
|
609
|
+
if (idx < 0) return { wsId: "", objectId: key };
|
|
610
|
+
return { wsId: key.slice(0, idx), objectId: key.slice(idx + 1) };
|
|
611
|
+
}
|
|
612
|
+
async function fetchObjectOptions(state, resource, allSentinel, allLabelJa, allLabelEn, rt) {
|
|
613
|
+
if (!state.workspaces?.length || !state.resources?.includes(resource))
|
|
614
|
+
return [];
|
|
615
|
+
const wsIds = await resolveWorkspaceIds(
|
|
616
|
+
state.workspaces,
|
|
617
|
+
rt.config.proxyFetch
|
|
618
|
+
);
|
|
619
|
+
const allGroups = await listGroups(rt.config.proxyFetch);
|
|
620
|
+
const groupById = new Map(allGroups.map((g) => [g.id, g]));
|
|
621
|
+
const multiWorkspace = wsIds.length > 1;
|
|
622
|
+
const options = [];
|
|
623
|
+
for (const wsId of wsIds) {
|
|
624
|
+
const wsLabel = workspaceName(wsId, groupById, rt.language);
|
|
625
|
+
const items = await listResource(rt.config.proxyFetch, wsId, resource);
|
|
626
|
+
for (const item of items) {
|
|
627
|
+
if (!item.id) continue;
|
|
628
|
+
options.push({
|
|
629
|
+
value: compoundKey(wsId, item.id),
|
|
630
|
+
label: multiWorkspace ? `${wsLabel} / ${resourceLabel(item)}` : resourceLabel(item)
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
if (options.length === 0) return [];
|
|
635
|
+
return [
|
|
636
|
+
{
|
|
637
|
+
value: allSentinel,
|
|
638
|
+
label: rt.language === "ja" ? allLabelJa : allLabelEn
|
|
639
|
+
},
|
|
640
|
+
...options
|
|
641
|
+
];
|
|
642
|
+
}
|
|
643
|
+
var powerbiOauthSetupFlow = {
|
|
644
|
+
initialState: () => ({}),
|
|
645
|
+
steps: [
|
|
646
|
+
{
|
|
647
|
+
slug: "workspaces",
|
|
648
|
+
type: "multiSelect",
|
|
649
|
+
question: {
|
|
650
|
+
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",
|
|
651
|
+
en: "Select target workspaces (multi-select allowed)"
|
|
652
|
+
},
|
|
653
|
+
async fetchOptions(_state, rt) {
|
|
654
|
+
const groups = await listGroups(rt.config.proxyFetch);
|
|
655
|
+
const options = groups.filter((g) => g.id && g.name).map((g) => ({ value: g.id, label: g.name }));
|
|
656
|
+
return [
|
|
657
|
+
{
|
|
658
|
+
value: ALL_WORKSPACES,
|
|
659
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "All workspaces"
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
value: MY_WORKSPACE,
|
|
663
|
+
label: rt.language === "ja" ? "\u30DE\u30A4 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "My workspace"
|
|
664
|
+
},
|
|
665
|
+
...options
|
|
666
|
+
];
|
|
667
|
+
},
|
|
668
|
+
applyAnswer: (state, answer) => ({ ...state, workspaces: answer })
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
slug: "resources",
|
|
672
|
+
type: "multiSelect",
|
|
673
|
+
question: {
|
|
674
|
+
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",
|
|
675
|
+
en: "Select which resource types to include in the summary"
|
|
676
|
+
},
|
|
677
|
+
async fetchOptions(state, rt) {
|
|
678
|
+
if (!state.workspaces?.length) return [];
|
|
679
|
+
return [
|
|
680
|
+
{
|
|
681
|
+
value: RESOURCE_DATASETS,
|
|
682
|
+
label: rt.language === "ja" ? "\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8" : "Datasets"
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
value: RESOURCE_REPORTS,
|
|
686
|
+
label: rt.language === "ja" ? "\u30EC\u30DD\u30FC\u30C8" : "Reports"
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
value: RESOURCE_DASHBOARDS,
|
|
690
|
+
label: rt.language === "ja" ? "\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9" : "Dashboards"
|
|
691
|
+
}
|
|
692
|
+
];
|
|
693
|
+
},
|
|
694
|
+
applyAnswer: (state, answer) => ({ ...state, resources: answer })
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
slug: "datasets",
|
|
698
|
+
type: "multiSelect",
|
|
699
|
+
question: {
|
|
700
|
+
ja: "\u4F7F\u7528\u3059\u308B\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
701
|
+
en: "Select the datasets you want to use (multi-select allowed)"
|
|
702
|
+
},
|
|
703
|
+
async fetchOptions(state, rt) {
|
|
704
|
+
return fetchObjectOptions(
|
|
705
|
+
state,
|
|
706
|
+
RESOURCE_DATASETS,
|
|
707
|
+
ALL_DATASETS,
|
|
708
|
+
"\u3059\u3079\u3066\u306E\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8",
|
|
709
|
+
"All datasets",
|
|
710
|
+
rt
|
|
711
|
+
);
|
|
712
|
+
},
|
|
713
|
+
applyAnswer: (state, answer) => ({ ...state, selectedDatasets: answer })
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
slug: "reports",
|
|
717
|
+
type: "multiSelect",
|
|
718
|
+
question: {
|
|
719
|
+
ja: "\u4F7F\u7528\u3059\u308B\u30EC\u30DD\u30FC\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
720
|
+
en: "Select the reports you want to use (multi-select allowed)"
|
|
721
|
+
},
|
|
722
|
+
async fetchOptions(state, rt) {
|
|
723
|
+
return fetchObjectOptions(
|
|
724
|
+
state,
|
|
725
|
+
RESOURCE_REPORTS,
|
|
726
|
+
ALL_REPORTS,
|
|
727
|
+
"\u3059\u3079\u3066\u306E\u30EC\u30DD\u30FC\u30C8",
|
|
728
|
+
"All reports",
|
|
729
|
+
rt
|
|
730
|
+
);
|
|
731
|
+
},
|
|
732
|
+
applyAnswer: (state, answer) => ({ ...state, selectedReports: answer })
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
slug: "dashboards",
|
|
736
|
+
type: "multiSelect",
|
|
737
|
+
question: {
|
|
738
|
+
ja: "\u4F7F\u7528\u3059\u308B\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
739
|
+
en: "Select the dashboards you want to use (multi-select allowed)"
|
|
740
|
+
},
|
|
741
|
+
async fetchOptions(state, rt) {
|
|
742
|
+
return fetchObjectOptions(
|
|
743
|
+
state,
|
|
744
|
+
RESOURCE_DASHBOARDS,
|
|
745
|
+
ALL_DASHBOARDS,
|
|
746
|
+
"\u3059\u3079\u3066\u306E\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9",
|
|
747
|
+
"All dashboards",
|
|
748
|
+
rt
|
|
749
|
+
);
|
|
750
|
+
},
|
|
751
|
+
applyAnswer: (state, answer) => ({
|
|
752
|
+
...state,
|
|
753
|
+
selectedDashboards: answer
|
|
754
|
+
})
|
|
755
|
+
}
|
|
756
|
+
],
|
|
757
|
+
async finalize(state, rt) {
|
|
758
|
+
if (!state.workspaces || !state.resources) {
|
|
759
|
+
throw new Error("Power BI setup: incomplete state on finalize");
|
|
760
|
+
}
|
|
761
|
+
const allGroups = await listGroups(rt.config.proxyFetch);
|
|
762
|
+
const groupById = new Map(allGroups.map((g) => [g.id, g]));
|
|
763
|
+
const wsIds = await resolveWorkspaceIds(
|
|
764
|
+
state.workspaces,
|
|
765
|
+
rt.config.proxyFetch
|
|
766
|
+
);
|
|
767
|
+
const selectedResources = new Set(state.resources);
|
|
768
|
+
async function resolveObjects(selected, allSentinel, resource) {
|
|
769
|
+
const byWorkspace = /* @__PURE__ */ new Map();
|
|
770
|
+
if (!selected || !selectedResources.has(resource)) return byWorkspace;
|
|
771
|
+
if (selected.includes(allSentinel)) {
|
|
772
|
+
for (const wsId of wsIds) {
|
|
773
|
+
const items = await listResource(
|
|
774
|
+
rt.config.proxyFetch,
|
|
775
|
+
wsId,
|
|
776
|
+
resource
|
|
777
|
+
);
|
|
778
|
+
const ids = items.map((i) => i.id).filter((id) => !!id);
|
|
779
|
+
if (ids.length > 0) byWorkspace.set(wsId, new Set(ids));
|
|
780
|
+
}
|
|
781
|
+
} else {
|
|
782
|
+
for (const key of selected) {
|
|
783
|
+
if (key === allSentinel) continue;
|
|
784
|
+
const { wsId, objectId } = parseCompoundKey(key);
|
|
785
|
+
if (!byWorkspace.has(wsId)) byWorkspace.set(wsId, /* @__PURE__ */ new Set());
|
|
786
|
+
byWorkspace.get(wsId).add(objectId);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return byWorkspace;
|
|
790
|
+
}
|
|
791
|
+
const datasetsByWs = await resolveObjects(
|
|
792
|
+
state.selectedDatasets,
|
|
793
|
+
ALL_DATASETS,
|
|
794
|
+
RESOURCE_DATASETS
|
|
795
|
+
);
|
|
796
|
+
const reportsByWs = await resolveObjects(
|
|
797
|
+
state.selectedReports,
|
|
798
|
+
ALL_REPORTS,
|
|
799
|
+
RESOURCE_REPORTS
|
|
800
|
+
);
|
|
801
|
+
const dashboardsByWs = await resolveObjects(
|
|
802
|
+
state.selectedDashboards,
|
|
803
|
+
ALL_DASHBOARDS,
|
|
804
|
+
RESOURCE_DASHBOARDS
|
|
805
|
+
);
|
|
806
|
+
const allWsIds = /* @__PURE__ */ new Set([
|
|
807
|
+
...datasetsByWs.keys(),
|
|
808
|
+
...reportsByWs.keys(),
|
|
809
|
+
...dashboardsByWs.keys()
|
|
810
|
+
]);
|
|
811
|
+
const sections = ["## Power BI", ""];
|
|
812
|
+
if (allWsIds.size === 0) {
|
|
813
|
+
sections.push("_No resources selected._", "");
|
|
814
|
+
return sections.join("\n");
|
|
815
|
+
}
|
|
816
|
+
for (const wsId of wsIds) {
|
|
817
|
+
if (!allWsIds.has(wsId)) continue;
|
|
818
|
+
const name = workspaceName(wsId, groupById, rt.language);
|
|
819
|
+
sections.push(`### Workspace: ${name}`, "");
|
|
820
|
+
if (wsId !== MY_WORKSPACE) {
|
|
821
|
+
sections.push(`- id: \`${wsId}\``);
|
|
822
|
+
}
|
|
823
|
+
const datasetIds = datasetsByWs.get(wsId);
|
|
824
|
+
if (datasetIds?.size) {
|
|
825
|
+
const items = await listResource(
|
|
826
|
+
rt.config.proxyFetch,
|
|
827
|
+
wsId,
|
|
828
|
+
RESOURCE_DATASETS
|
|
829
|
+
);
|
|
830
|
+
const filtered = items.filter(
|
|
831
|
+
(item) => item.id && datasetIds.has(item.id)
|
|
832
|
+
);
|
|
833
|
+
for (const item of filtered.slice(0, RESOURCE_DISPLAY_LIMIT)) {
|
|
834
|
+
sections.push(`#### Dataset: ${resourceLabel(item)}`, "");
|
|
835
|
+
const schema = await fetchDatasetSchema(
|
|
836
|
+
rt.config.proxyFetch,
|
|
837
|
+
wsId,
|
|
838
|
+
item.id
|
|
839
|
+
);
|
|
840
|
+
if (schema.size > 0) {
|
|
841
|
+
for (const [table, columns] of schema) {
|
|
842
|
+
sections.push(`##### Table: ${table}`, "");
|
|
843
|
+
sections.push("| Column |");
|
|
844
|
+
sections.push("|--------|");
|
|
845
|
+
for (const col of columns) {
|
|
846
|
+
sections.push(`| ${col} |`);
|
|
847
|
+
}
|
|
848
|
+
sections.push("");
|
|
849
|
+
}
|
|
850
|
+
} else {
|
|
851
|
+
sections.push("_Schema not available._", "");
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
for (const [resource, selectedByWs, heading] of [
|
|
856
|
+
[RESOURCE_REPORTS, reportsByWs, "Reports"],
|
|
857
|
+
[RESOURCE_DASHBOARDS, dashboardsByWs, "Dashboards"]
|
|
858
|
+
]) {
|
|
859
|
+
const selectedIds = selectedByWs.get(wsId);
|
|
860
|
+
if (!selectedIds?.size) continue;
|
|
861
|
+
const items = await listResource(
|
|
862
|
+
rt.config.proxyFetch,
|
|
863
|
+
wsId,
|
|
864
|
+
resource
|
|
865
|
+
);
|
|
866
|
+
const filtered = items.filter(
|
|
867
|
+
(item) => item.id && selectedIds.has(item.id)
|
|
868
|
+
);
|
|
869
|
+
sections.push(`- ${heading} (${filtered.length}):`);
|
|
870
|
+
for (const item of filtered.slice(0, RESOURCE_DISPLAY_LIMIT)) {
|
|
871
|
+
sections.push(` - ${resourceLabel(item)}`);
|
|
872
|
+
}
|
|
873
|
+
if (filtered.length > RESOURCE_DISPLAY_LIMIT) {
|
|
874
|
+
sections.push(
|
|
875
|
+
` - \u2026and ${filtered.length - RESOURCE_DISPLAY_LIMIT} more`
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
sections.push("");
|
|
880
|
+
}
|
|
881
|
+
return sections.join("\n");
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
|
|
414
885
|
// ../connectors/src/connectors/powerbi-oauth/index.ts
|
|
415
886
|
var tools = { request: requestTool };
|
|
416
887
|
var powerbiOauthConnector = new ConnectorPlugin({
|
|
417
888
|
slug: "powerbi",
|
|
418
889
|
authType: AUTH_TYPES.OAUTH,
|
|
890
|
+
skipConnectionCheckOnCreate: true,
|
|
419
891
|
name: "Power BI",
|
|
420
892
|
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
893
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/2vXQCKGpMJ9kGSaqkZl9IS/cc5669c267fc5d11e7b1f8c01723e461/power-bi-icon.png",
|
|
@@ -546,6 +1018,7 @@ export default async function handler(c: Context) {
|
|
|
546
1018
|
- \`executeQueries\` \u306F\u73FE\u72B6 1 \u30EA\u30AF\u30A8\u30B9\u30C8\u306B\u3064\u304D 1 \u30AF\u30A8\u30EA\u306E\u307F`
|
|
547
1019
|
},
|
|
548
1020
|
tools,
|
|
1021
|
+
setup: (params, ctx, config) => runSetupFlow(powerbiOauthSetupFlow, params, ctx, config),
|
|
549
1022
|
async checkConnection(_params, config) {
|
|
550
1023
|
const { proxyFetch } = config;
|
|
551
1024
|
const url = "https://api.powerbi.com/v1.0/myorg/groups?$top=1";
|
|
@@ -594,6 +1067,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
594
1067
|
import { getContext } from "hono/context-storage";
|
|
595
1068
|
import { getCookie } from "hono/cookie";
|
|
596
1069
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
1070
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
597
1071
|
function normalizeHeaders(input) {
|
|
598
1072
|
const out = {};
|
|
599
1073
|
if (!input) return out;
|
|
@@ -602,6 +1076,11 @@ function normalizeHeaders(input) {
|
|
|
602
1076
|
});
|
|
603
1077
|
return out;
|
|
604
1078
|
}
|
|
1079
|
+
function extractInputUrl(input) {
|
|
1080
|
+
if (typeof input === "string") return input;
|
|
1081
|
+
if (input instanceof URL) return input.href;
|
|
1082
|
+
return input.url;
|
|
1083
|
+
}
|
|
605
1084
|
function createSandboxProxyFetch(connectionId) {
|
|
606
1085
|
return async (input, init) => {
|
|
607
1086
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -611,10 +1090,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
611
1090
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
612
1091
|
);
|
|
613
1092
|
}
|
|
614
|
-
const originalUrl =
|
|
1093
|
+
const originalUrl = extractInputUrl(input);
|
|
1094
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
1095
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1096
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
1097
|
+
return fetch(sessionUrl, {
|
|
1098
|
+
method: "POST",
|
|
1099
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
615
1102
|
const originalMethod = init?.method ?? "GET";
|
|
616
1103
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
617
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
618
1104
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
619
1105
|
return fetch(proxyUrl, {
|
|
620
1106
|
method: "POST",
|
|
@@ -640,10 +1126,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
640
1126
|
}
|
|
641
1127
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
642
1128
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1129
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
643
1130
|
return async (input, init) => {
|
|
644
|
-
const originalUrl =
|
|
645
|
-
const originalMethod = init?.method ?? "GET";
|
|
646
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
1131
|
+
const originalUrl = extractInputUrl(input);
|
|
647
1132
|
const c = getContext();
|
|
648
1133
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
649
1134
|
if (!appSession) {
|
|
@@ -651,6 +1136,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
651
1136
|
"No authentication method available for connection proxy."
|
|
652
1137
|
);
|
|
653
1138
|
}
|
|
1139
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1140
|
+
return fetch(sessionUrl, {
|
|
1141
|
+
method: "POST",
|
|
1142
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
const originalMethod = init?.method ?? "GET";
|
|
1146
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
654
1147
|
return fetch(proxyUrl, {
|
|
655
1148
|
method: "POST",
|
|
656
1149
|
headers: {
|