@squadbase/vite-server 0.1.17-dev.24af54e → 0.1.17-dev.7408ec4
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 +1682 -425
- package/dist/connectors/airtable-oauth.js +22 -3
- package/dist/connectors/airtable.js +22 -3
- package/dist/connectors/amplitude.js +22 -3
- package/dist/connectors/asana.js +22 -3
- package/dist/connectors/attio.js +22 -3
- package/dist/connectors/aws-billing.js +22 -3
- package/dist/connectors/azure-sql.js +25 -6
- package/dist/connectors/backlog-api-key.js +22 -3
- package/dist/connectors/clickup.js +22 -3
- package/dist/connectors/cosmosdb.js +22 -3
- package/dist/connectors/customerio.js +23 -4
- package/dist/connectors/dbt.js +22 -3
- package/dist/connectors/freshdesk.js +22 -3
- package/dist/connectors/freshsales.js +22 -3
- package/dist/connectors/freshservice.js +22 -3
- package/dist/connectors/gamma.js +24 -5
- package/dist/connectors/github.js +22 -3
- package/dist/connectors/gmail-oauth.js +22 -3
- package/dist/connectors/gmail.js +22 -3
- package/dist/connectors/google-ads.js +22 -3
- package/dist/connectors/google-analytics-oauth.js +22 -3
- package/dist/connectors/google-analytics.js +222 -68
- package/dist/connectors/google-audit-log.js +22 -3
- package/dist/connectors/google-calendar-oauth.js +22 -3
- package/dist/connectors/google-calendar.js +22 -3
- package/dist/connectors/google-docs.js +22 -3
- package/dist/connectors/google-drive.js +22 -3
- package/dist/connectors/google-search-console-oauth.js +22 -3
- package/dist/connectors/google-sheets.js +22 -3
- package/dist/connectors/google-slides.js +22 -3
- package/dist/connectors/grafana.js +22 -3
- package/dist/connectors/hubspot-oauth.js +22 -3
- package/dist/connectors/hubspot.js +22 -3
- package/dist/connectors/influxdb.js +22 -3
- package/dist/connectors/intercom-oauth.js +22 -3
- package/dist/connectors/intercom.js +22 -3
- package/dist/connectors/jdbc.js +22 -3
- package/dist/connectors/jira-api-key.js +22 -3
- package/dist/connectors/kintone-api-token.js +22 -3
- package/dist/connectors/kintone.js +22 -3
- package/dist/connectors/linear.js +22 -3
- package/dist/connectors/linkedin-ads.js +22 -3
- package/dist/connectors/mailchimp-oauth.js +22 -3
- package/dist/connectors/mailchimp.js +22 -3
- package/dist/connectors/meta-ads-oauth.js +22 -3
- package/dist/connectors/meta-ads.js +22 -3
- package/dist/connectors/mixpanel.js +22 -3
- package/dist/connectors/monday.js +22 -3
- package/dist/connectors/mongodb.js +22 -3
- package/dist/connectors/notion-oauth.js +22 -3
- package/dist/connectors/notion.js +22 -3
- package/dist/connectors/oracle.js +48 -14
- package/dist/connectors/outlook-oauth.js +22 -3
- package/dist/connectors/powerbi-oauth.js +303 -37
- package/dist/connectors/salesforce.js +22 -3
- package/dist/connectors/semrush.js +360 -46
- package/dist/connectors/sentry.js +22 -3
- package/dist/connectors/shopify-oauth.js +22 -3
- package/dist/connectors/shopify.js +22 -3
- package/dist/connectors/sqlserver.js +25 -6
- package/dist/connectors/stripe-api-key.js +22 -3
- package/dist/connectors/stripe-oauth.js +22 -3
- package/dist/connectors/supabase.js +25 -6
- package/dist/connectors/tableau.js +240 -78
- package/dist/connectors/tiktok-ads.js +22 -3
- package/dist/connectors/wix-store.js +22 -3
- package/dist/connectors/zendesk-oauth.js +22 -3
- package/dist/connectors/zendesk.js +22 -3
- package/dist/index.js +1682 -425
- package/dist/main.js +1682 -425
- package/dist/vite-plugin.js +1682 -425
- package/package.json +1 -1
|
@@ -369,19 +369,28 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
369
369
|
};
|
|
370
370
|
let state = flow.initialState();
|
|
371
371
|
let answerIdx = 0;
|
|
372
|
+
const pendingParameterUpdates = [];
|
|
372
373
|
for (const step of flow.steps) {
|
|
373
374
|
const ans = ctx.answers[answerIdx];
|
|
374
375
|
if (ans && ans.questionSlug === step.slug) {
|
|
375
376
|
state = step.applyAnswer(state, ans.answer);
|
|
377
|
+
if (step.toParameterUpdates) {
|
|
378
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
379
|
+
}
|
|
376
380
|
answerIdx += 1;
|
|
377
381
|
continue;
|
|
378
382
|
}
|
|
383
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
379
384
|
if (step.type === "text") {
|
|
380
385
|
return {
|
|
381
386
|
type: "nextQuestion",
|
|
382
387
|
questionSlug: step.slug,
|
|
383
388
|
question: step.question[ctx.language],
|
|
384
|
-
questionType: "text"
|
|
389
|
+
questionType: "text",
|
|
390
|
+
allowFreeText: resolvedAllowFreeText,
|
|
391
|
+
...pendingParameterUpdates.length > 0 && {
|
|
392
|
+
parameterUpdates: pendingParameterUpdates
|
|
393
|
+
}
|
|
385
394
|
};
|
|
386
395
|
}
|
|
387
396
|
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
@@ -393,11 +402,21 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
393
402
|
questionSlug: step.slug,
|
|
394
403
|
question: step.question[ctx.language],
|
|
395
404
|
questionType: step.type,
|
|
396
|
-
options
|
|
405
|
+
options,
|
|
406
|
+
allowFreeText: resolvedAllowFreeText,
|
|
407
|
+
...pendingParameterUpdates.length > 0 && {
|
|
408
|
+
parameterUpdates: pendingParameterUpdates
|
|
409
|
+
}
|
|
397
410
|
};
|
|
398
411
|
}
|
|
399
412
|
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
400
|
-
return {
|
|
413
|
+
return {
|
|
414
|
+
type: "fulfilled",
|
|
415
|
+
dataInvestigationResult,
|
|
416
|
+
...pendingParameterUpdates.length > 0 && {
|
|
417
|
+
parameterUpdates: pendingParameterUpdates
|
|
418
|
+
}
|
|
419
|
+
};
|
|
401
420
|
}
|
|
402
421
|
async function resolveSetupSelection(params) {
|
|
403
422
|
const { selected, allSentinel, fetchAll, limit } = params;
|
|
@@ -467,7 +486,11 @@ async function tableauProxyApiFetch(proxyFetch, params, path2, init) {
|
|
|
467
486
|
|
|
468
487
|
// ../connectors/src/connectors/tableau/setup-flow.ts
|
|
469
488
|
var ALL_PROJECTS = "__ALL_PROJECTS__";
|
|
489
|
+
var ALL_WORKBOOKS = "__ALL_WORKBOOKS__";
|
|
470
490
|
var TABLEAU_SETUP_MAX_PROJECTS = 10;
|
|
491
|
+
var MAX_WORKBOOKS_DETAIL = 10;
|
|
492
|
+
var MAX_VIEWS_PER_WORKBOOK = 5;
|
|
493
|
+
var MAX_SAMPLE_ROWS = 5;
|
|
471
494
|
var PAGE_SIZE = 100;
|
|
472
495
|
async function listAllProjects(rt) {
|
|
473
496
|
const all = [];
|
|
@@ -516,6 +539,57 @@ async function listProjectResources(rt, resource) {
|
|
|
516
539
|
}
|
|
517
540
|
return all;
|
|
518
541
|
}
|
|
542
|
+
async function listViewsForWorkbook(rt, workbookId) {
|
|
543
|
+
const res = await tableauProxyApiFetch(
|
|
544
|
+
rt.config.proxyFetch,
|
|
545
|
+
rt.params,
|
|
546
|
+
`/sites/{siteId}/workbooks/${encodeURIComponent(workbookId)}/views`
|
|
547
|
+
);
|
|
548
|
+
if (!res.ok) return [];
|
|
549
|
+
const data = await res.json();
|
|
550
|
+
return data.views?.view ?? [];
|
|
551
|
+
}
|
|
552
|
+
async function fetchViewDataSample(rt, viewId) {
|
|
553
|
+
try {
|
|
554
|
+
const res = await tableauProxyApiFetch(
|
|
555
|
+
rt.config.proxyFetch,
|
|
556
|
+
rt.params,
|
|
557
|
+
`/sites/{siteId}/views/${encodeURIComponent(viewId)}/data`
|
|
558
|
+
);
|
|
559
|
+
if (!res.ok) return null;
|
|
560
|
+
const csv = await res.text();
|
|
561
|
+
const lines = csv.trim().split("\n").filter(Boolean);
|
|
562
|
+
if (lines.length === 0) return null;
|
|
563
|
+
const columns = parseCsvLine(lines[0]);
|
|
564
|
+
const rows = lines.slice(1, 1 + MAX_SAMPLE_ROWS).map(parseCsvLine);
|
|
565
|
+
return { columns, rows };
|
|
566
|
+
} catch {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function parseCsvLine(line) {
|
|
571
|
+
const result = [];
|
|
572
|
+
let current = "";
|
|
573
|
+
let inQuotes = false;
|
|
574
|
+
for (let i = 0; i < line.length; i++) {
|
|
575
|
+
const ch = line[i];
|
|
576
|
+
if (ch === '"') {
|
|
577
|
+
if (inQuotes && line[i + 1] === '"') {
|
|
578
|
+
current += '"';
|
|
579
|
+
i++;
|
|
580
|
+
} else {
|
|
581
|
+
inQuotes = !inQuotes;
|
|
582
|
+
}
|
|
583
|
+
} else if (ch === "," && !inQuotes) {
|
|
584
|
+
result.push(current.trim());
|
|
585
|
+
current = "";
|
|
586
|
+
} else {
|
|
587
|
+
current += ch;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
result.push(current.trim());
|
|
591
|
+
return result;
|
|
592
|
+
}
|
|
519
593
|
var tableauSetupFlow = {
|
|
520
594
|
initialState: () => ({}),
|
|
521
595
|
steps: [
|
|
@@ -538,6 +612,39 @@ var tableauSetupFlow = {
|
|
|
538
612
|
];
|
|
539
613
|
},
|
|
540
614
|
applyAnswer: (state, answer) => ({ ...state, projects: answer })
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
slug: "workbooks",
|
|
618
|
+
type: "multiSelect",
|
|
619
|
+
question: {
|
|
620
|
+
ja: "\u5206\u6790\u5BFE\u8C61\u306E\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
621
|
+
en: "Select the workbooks to analyze (multi-select allowed)"
|
|
622
|
+
},
|
|
623
|
+
async fetchOptions(state, rt) {
|
|
624
|
+
if (!state.projects?.length) return [];
|
|
625
|
+
const allProjects = await listAllProjects(rt);
|
|
626
|
+
const targetIds = await resolveSetupSelection({
|
|
627
|
+
selected: state.projects,
|
|
628
|
+
allSentinel: ALL_PROJECTS,
|
|
629
|
+
fetchAll: async () => allProjects.map((p) => p.id).filter((id) => id),
|
|
630
|
+
limit: TABLEAU_SETUP_MAX_PROJECTS
|
|
631
|
+
});
|
|
632
|
+
const targetSet = new Set(targetIds);
|
|
633
|
+
const workbooks = await listProjectResources(rt, "workbooks");
|
|
634
|
+
const options = workbooks.filter((wb) => wb.id && wb.project?.id && targetSet.has(wb.project.id)).map((wb) => ({
|
|
635
|
+
value: wb.id,
|
|
636
|
+
label: wb.name ?? wb.id ?? "(unknown)"
|
|
637
|
+
}));
|
|
638
|
+
if (options.length === 0) return [];
|
|
639
|
+
return [
|
|
640
|
+
{
|
|
641
|
+
value: ALL_WORKBOOKS,
|
|
642
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF" : "All workbooks"
|
|
643
|
+
},
|
|
644
|
+
...options
|
|
645
|
+
];
|
|
646
|
+
},
|
|
647
|
+
applyAnswer: (state, answer) => ({ ...state, workbooks: answer })
|
|
541
648
|
}
|
|
542
649
|
],
|
|
543
650
|
async finalize(state, rt) {
|
|
@@ -546,25 +653,33 @@ var tableauSetupFlow = {
|
|
|
546
653
|
}
|
|
547
654
|
const allProjects = await listAllProjects(rt);
|
|
548
655
|
const projectById = new Map(allProjects.map((p) => [p.id, p]));
|
|
549
|
-
const
|
|
656
|
+
const targetProjectIds = await resolveSetupSelection({
|
|
550
657
|
selected: state.projects,
|
|
551
658
|
allSentinel: ALL_PROJECTS,
|
|
552
659
|
fetchAll: async () => allProjects.map((p) => p.id).filter((id) => id),
|
|
553
660
|
limit: TABLEAU_SETUP_MAX_PROJECTS
|
|
554
661
|
});
|
|
555
662
|
const sections = ["## Tableau", ""];
|
|
556
|
-
if (!
|
|
663
|
+
if (!targetProjectIds.length) {
|
|
557
664
|
sections.push("_No projects selected._", "");
|
|
558
665
|
return sections.join("\n");
|
|
559
666
|
}
|
|
560
|
-
const [
|
|
667
|
+
const [allWorkbooks, datasources] = await Promise.all([
|
|
561
668
|
listProjectResources(rt, "workbooks"),
|
|
562
669
|
listProjectResources(rt, "datasources")
|
|
563
670
|
]);
|
|
671
|
+
const projectIdSet = new Set(targetProjectIds);
|
|
672
|
+
const targetWorkbooks = await resolveSetupSelection({
|
|
673
|
+
selected: state.workbooks ?? [],
|
|
674
|
+
allSentinel: ALL_WORKBOOKS,
|
|
675
|
+
fetchAll: async () => allWorkbooks.filter((wb) => wb.id && wb.project?.id && projectIdSet.has(wb.project.id)).map((wb) => wb.id).filter(Boolean),
|
|
676
|
+
limit: MAX_WORKBOOKS_DETAIL
|
|
677
|
+
});
|
|
678
|
+
const targetWorkbookSet = new Set(targetWorkbooks);
|
|
564
679
|
const workbooksByProject = /* @__PURE__ */ new Map();
|
|
565
|
-
for (const wb of
|
|
680
|
+
for (const wb of allWorkbooks) {
|
|
566
681
|
const pid = wb.project?.id;
|
|
567
|
-
if (!pid) continue;
|
|
682
|
+
if (!pid || !projectIdSet.has(pid)) continue;
|
|
568
683
|
const bucket = workbooksByProject.get(pid) ?? [];
|
|
569
684
|
bucket.push(wb);
|
|
570
685
|
workbooksByProject.set(pid, bucket);
|
|
@@ -572,32 +687,78 @@ var tableauSetupFlow = {
|
|
|
572
687
|
const datasourcesByProject = /* @__PURE__ */ new Map();
|
|
573
688
|
for (const ds of datasources) {
|
|
574
689
|
const pid = ds.project?.id;
|
|
575
|
-
if (!pid) continue;
|
|
690
|
+
if (!pid || !projectIdSet.has(pid)) continue;
|
|
576
691
|
const bucket = datasourcesByProject.get(pid) ?? [];
|
|
577
692
|
bucket.push(ds);
|
|
578
693
|
datasourcesByProject.set(pid, bucket);
|
|
579
694
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
const
|
|
583
|
-
sections.push(`### Project: ${name}`, ""
|
|
584
|
-
const projectWorkbooks = workbooksByProject.get(
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
695
|
+
const allDiscoveredColumns = [];
|
|
696
|
+
for (const pid of targetProjectIds) {
|
|
697
|
+
const project = projectById.get(pid);
|
|
698
|
+
sections.push(`### Project: ${project?.name ?? pid}`, "");
|
|
699
|
+
const projectWorkbooks = workbooksByProject.get(pid) ?? [];
|
|
700
|
+
for (const wb of projectWorkbooks) {
|
|
701
|
+
if (!wb.id) continue;
|
|
702
|
+
const isDetailed = targetWorkbookSet.has(wb.id);
|
|
703
|
+
sections.push(`#### Workbook: ${wb.name ?? wb.id}`, "");
|
|
704
|
+
if (!isDetailed) continue;
|
|
705
|
+
const views = await listViewsForWorkbook(rt, wb.id);
|
|
706
|
+
if (views.length === 0) {
|
|
707
|
+
sections.push("_No views found._", "");
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
for (const view of views.slice(0, MAX_VIEWS_PER_WORKBOOK)) {
|
|
711
|
+
if (!view.id) continue;
|
|
712
|
+
sections.push(`##### View: ${view.name ?? view.id}`, "");
|
|
713
|
+
const sample = await fetchViewDataSample(rt, view.id);
|
|
714
|
+
if (!sample || sample.columns.length === 0) {
|
|
715
|
+
sections.push("_Data not available._", "");
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
allDiscoveredColumns.push(...sample.columns);
|
|
719
|
+
sections.push(`| ${sample.columns.join(" | ")} |`);
|
|
720
|
+
sections.push(`|${sample.columns.map(() => "---").join("|")}|`);
|
|
721
|
+
for (const row of sample.rows) {
|
|
722
|
+
const cells = row.map((c) => c.replace(/\|/g, "\\|"));
|
|
723
|
+
sections.push(`| ${cells.join(" | ")} |`);
|
|
724
|
+
}
|
|
725
|
+
sections.push("");
|
|
726
|
+
}
|
|
591
727
|
}
|
|
592
|
-
const projectDatasources = datasourcesByProject.get(
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
728
|
+
const projectDatasources = datasourcesByProject.get(pid) ?? [];
|
|
729
|
+
if (projectDatasources.length > 0) {
|
|
730
|
+
sections.push(`#### Datasources (${projectDatasources.length})`, "");
|
|
731
|
+
for (const ds of projectDatasources.slice(0, 25)) {
|
|
732
|
+
sections.push(`- ${ds.name ?? ds.id ?? "(unknown)"}`);
|
|
733
|
+
}
|
|
734
|
+
sections.push("");
|
|
596
735
|
}
|
|
597
|
-
|
|
598
|
-
|
|
736
|
+
}
|
|
737
|
+
if (allDiscoveredColumns.length > 0) {
|
|
738
|
+
const unique = [...new Set(allDiscoveredColumns.map((c) => c.toLowerCase()))];
|
|
739
|
+
const themes = [];
|
|
740
|
+
const hasPattern = (keywords) => unique.some((c) => keywords.some((k) => c.includes(k)));
|
|
741
|
+
if (hasPattern(["revenue", "sales", "amount", "price", "cost", "profit", "\u58F2\u4E0A", "\u91D1\u984D", "\u5229\u76CA"]))
|
|
742
|
+
themes.push("Revenue & profitability analysis (trends, breakdown by segment)");
|
|
743
|
+
if (hasPattern(["date", "month", "year", "quarter", "day", "\u65E5\u4ED8", "\u6708", "\u5E74", "\u671F"]))
|
|
744
|
+
themes.push("Time-series trend analysis (YoY, MoM comparisons)");
|
|
745
|
+
if (hasPattern(["region", "country", "city", "state", "\u5730\u57DF", "\u56FD", "\u90FD\u5E02", "\u5E02"]))
|
|
746
|
+
themes.push("Geographic/regional performance comparison");
|
|
747
|
+
if (hasPattern(["product", "category", "item", "sku", "\u5546\u54C1", "\u30AB\u30C6\u30B4\u30EA"]))
|
|
748
|
+
themes.push("Product/category mix analysis");
|
|
749
|
+
if (hasPattern(["customer", "user", "account", "\u9867\u5BA2", "\u30E6\u30FC\u30B6\u30FC"]))
|
|
750
|
+
themes.push("Customer segmentation and behavior analysis");
|
|
751
|
+
if (hasPattern(["quantity", "count", "volume", "\u6570\u91CF", "\u4EF6\u6570"]))
|
|
752
|
+
themes.push("Volume and demand pattern analysis");
|
|
753
|
+
if (hasPattern(["rate", "ratio", "percentage", "%", "\u7387"]))
|
|
754
|
+
themes.push("KPI ratio tracking and benchmarking");
|
|
755
|
+
if (themes.length > 0) {
|
|
756
|
+
sections.push("### Suggested Analysis Themes", "");
|
|
757
|
+
for (const theme of themes) {
|
|
758
|
+
sections.push(`- ${theme}`);
|
|
759
|
+
}
|
|
760
|
+
sections.push("");
|
|
599
761
|
}
|
|
600
|
-
sections.push("");
|
|
601
762
|
}
|
|
602
763
|
return sections.join("\n");
|
|
603
764
|
}
|
|
@@ -607,27 +768,36 @@ var tableauSetupFlow = {
|
|
|
607
768
|
import { z } from "zod";
|
|
608
769
|
var DEFAULT_API_VERSION3 = "3.28";
|
|
609
770
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
throw new Error(
|
|
615
|
-
"Tableau session manager is not configured. Missing INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL or INTERNAL_SQUADBASE_SANDBOX_ID."
|
|
616
|
-
);
|
|
771
|
+
var cachedToken = null;
|
|
772
|
+
async function getProxyToken(config) {
|
|
773
|
+
if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
|
|
774
|
+
return cachedToken.token;
|
|
617
775
|
}
|
|
618
|
-
const
|
|
619
|
-
const url = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
776
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
620
777
|
const res = await fetch(url, {
|
|
621
778
|
method: "POST",
|
|
622
|
-
headers: {
|
|
779
|
+
headers: {
|
|
780
|
+
"Content-Type": "application/json",
|
|
781
|
+
"x-api-key": config.appApiKey,
|
|
782
|
+
"project-id": config.projectId
|
|
783
|
+
},
|
|
784
|
+
body: JSON.stringify({
|
|
785
|
+
sandboxId: config.sandboxId,
|
|
786
|
+
issuedBy: "coding-agent"
|
|
787
|
+
})
|
|
623
788
|
});
|
|
624
789
|
if (!res.ok) {
|
|
625
|
-
const
|
|
790
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
626
791
|
throw new Error(
|
|
627
|
-
`Failed to
|
|
792
|
+
`Failed to get proxy token: HTTP ${res.status} ${errorText}`
|
|
628
793
|
);
|
|
629
794
|
}
|
|
630
|
-
|
|
795
|
+
const data = await res.json();
|
|
796
|
+
cachedToken = {
|
|
797
|
+
token: data.token,
|
|
798
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
799
|
+
};
|
|
800
|
+
return data.token;
|
|
631
801
|
}
|
|
632
802
|
function buildBaseUrl(serverUrl, apiVersion) {
|
|
633
803
|
return `${serverUrl.replace(/\/$/, "")}/api/${apiVersion}`;
|
|
@@ -669,7 +839,7 @@ All paths are relative to {serverUrl}/api/{apiVersion}. Use the literal placehol
|
|
|
669
839
|
Accept and Content-Type headers default to application/json so list responses come back as JSON instead of Tableau's default XML.`,
|
|
670
840
|
inputSchema,
|
|
671
841
|
outputSchema,
|
|
672
|
-
async execute({ connectionId, method, path: path2, queryParams, body }, connections) {
|
|
842
|
+
async execute({ connectionId, method, path: path2, queryParams, body }, connections, config) {
|
|
673
843
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
674
844
|
if (!connection2) {
|
|
675
845
|
return {
|
|
@@ -684,50 +854,42 @@ Accept and Content-Type headers default to application/json so list responses co
|
|
|
684
854
|
const serverUrl = parameters.serverUrl.getValue(connection2);
|
|
685
855
|
const apiVersion = parameters.apiVersion.tryGetValue(connection2) || DEFAULT_API_VERSION3;
|
|
686
856
|
const trimmedPath = path2.trim().replace(/^([^/])/, "/$1");
|
|
687
|
-
const queryString = queryParams ? `?${new URLSearchParams(queryParams).toString()}` : "";
|
|
688
857
|
const baseUrl = buildBaseUrl(serverUrl, apiVersion);
|
|
858
|
+
let url = `${baseUrl}${trimmedPath}`;
|
|
859
|
+
if (queryParams) {
|
|
860
|
+
url += `?${new URLSearchParams(queryParams).toString()}`;
|
|
861
|
+
}
|
|
862
|
+
const token = await getProxyToken(config.oauthProxy);
|
|
863
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
689
864
|
const controller = new AbortController();
|
|
690
865
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
691
866
|
try {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
const init = {
|
|
867
|
+
const response = await fetch(proxyUrl, {
|
|
868
|
+
method: "POST",
|
|
869
|
+
headers: {
|
|
870
|
+
"Content-Type": "application/json",
|
|
871
|
+
Authorization: `Bearer ${token}`
|
|
872
|
+
},
|
|
873
|
+
body: JSON.stringify({
|
|
874
|
+
url,
|
|
701
875
|
method,
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
const response = await fetch(url, init);
|
|
713
|
-
const text = await response.text();
|
|
714
|
-
const data = text ? (() => {
|
|
715
|
-
try {
|
|
716
|
-
return JSON.parse(text);
|
|
717
|
-
} catch {
|
|
718
|
-
return text;
|
|
719
|
-
}
|
|
720
|
-
})() : null;
|
|
721
|
-
if (response.status === 401 && attempt === 0) {
|
|
722
|
-
attempt++;
|
|
723
|
-
continue;
|
|
724
|
-
}
|
|
725
|
-
if (!response.ok) {
|
|
726
|
-
const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
|
|
727
|
-
return { success: false, error: errorMessage };
|
|
876
|
+
...body !== void 0 ? { body } : {}
|
|
877
|
+
}),
|
|
878
|
+
signal: controller.signal
|
|
879
|
+
});
|
|
880
|
+
const text = await response.text();
|
|
881
|
+
const data = text ? (() => {
|
|
882
|
+
try {
|
|
883
|
+
return JSON.parse(text);
|
|
884
|
+
} catch {
|
|
885
|
+
return text;
|
|
728
886
|
}
|
|
729
|
-
|
|
887
|
+
})() : null;
|
|
888
|
+
if (!response.ok) {
|
|
889
|
+
const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
|
|
890
|
+
return { success: false, error: errorMessage };
|
|
730
891
|
}
|
|
892
|
+
return { success: true, status: response.status, data };
|
|
731
893
|
} finally {
|
|
732
894
|
clearTimeout(timeout);
|
|
733
895
|
}
|
|
@@ -235,19 +235,28 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
235
235
|
};
|
|
236
236
|
let state = flow.initialState();
|
|
237
237
|
let answerIdx = 0;
|
|
238
|
+
const pendingParameterUpdates = [];
|
|
238
239
|
for (const step of flow.steps) {
|
|
239
240
|
const ans = ctx.answers[answerIdx];
|
|
240
241
|
if (ans && ans.questionSlug === step.slug) {
|
|
241
242
|
state = step.applyAnswer(state, ans.answer);
|
|
243
|
+
if (step.toParameterUpdates) {
|
|
244
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
245
|
+
}
|
|
242
246
|
answerIdx += 1;
|
|
243
247
|
continue;
|
|
244
248
|
}
|
|
249
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
245
250
|
if (step.type === "text") {
|
|
246
251
|
return {
|
|
247
252
|
type: "nextQuestion",
|
|
248
253
|
questionSlug: step.slug,
|
|
249
254
|
question: step.question[ctx.language],
|
|
250
|
-
questionType: "text"
|
|
255
|
+
questionType: "text",
|
|
256
|
+
allowFreeText: resolvedAllowFreeText,
|
|
257
|
+
...pendingParameterUpdates.length > 0 && {
|
|
258
|
+
parameterUpdates: pendingParameterUpdates
|
|
259
|
+
}
|
|
251
260
|
};
|
|
252
261
|
}
|
|
253
262
|
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
@@ -259,11 +268,21 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
259
268
|
questionSlug: step.slug,
|
|
260
269
|
question: step.question[ctx.language],
|
|
261
270
|
questionType: step.type,
|
|
262
|
-
options
|
|
271
|
+
options,
|
|
272
|
+
allowFreeText: resolvedAllowFreeText,
|
|
273
|
+
...pendingParameterUpdates.length > 0 && {
|
|
274
|
+
parameterUpdates: pendingParameterUpdates
|
|
275
|
+
}
|
|
263
276
|
};
|
|
264
277
|
}
|
|
265
278
|
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
266
|
-
return {
|
|
279
|
+
return {
|
|
280
|
+
type: "fulfilled",
|
|
281
|
+
dataInvestigationResult,
|
|
282
|
+
...pendingParameterUpdates.length > 0 && {
|
|
283
|
+
parameterUpdates: pendingParameterUpdates
|
|
284
|
+
}
|
|
285
|
+
};
|
|
267
286
|
}
|
|
268
287
|
async function resolveSetupSelection(params) {
|
|
269
288
|
const { selected, allSentinel, fetchAll, limit } = params;
|
|
@@ -383,19 +383,28 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
383
383
|
};
|
|
384
384
|
let state = flow.initialState();
|
|
385
385
|
let answerIdx = 0;
|
|
386
|
+
const pendingParameterUpdates = [];
|
|
386
387
|
for (const step of flow.steps) {
|
|
387
388
|
const ans = ctx.answers[answerIdx];
|
|
388
389
|
if (ans && ans.questionSlug === step.slug) {
|
|
389
390
|
state = step.applyAnswer(state, ans.answer);
|
|
391
|
+
if (step.toParameterUpdates) {
|
|
392
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
393
|
+
}
|
|
390
394
|
answerIdx += 1;
|
|
391
395
|
continue;
|
|
392
396
|
}
|
|
397
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
393
398
|
if (step.type === "text") {
|
|
394
399
|
return {
|
|
395
400
|
type: "nextQuestion",
|
|
396
401
|
questionSlug: step.slug,
|
|
397
402
|
question: step.question[ctx.language],
|
|
398
|
-
questionType: "text"
|
|
403
|
+
questionType: "text",
|
|
404
|
+
allowFreeText: resolvedAllowFreeText,
|
|
405
|
+
...pendingParameterUpdates.length > 0 && {
|
|
406
|
+
parameterUpdates: pendingParameterUpdates
|
|
407
|
+
}
|
|
399
408
|
};
|
|
400
409
|
}
|
|
401
410
|
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
@@ -407,11 +416,21 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
407
416
|
questionSlug: step.slug,
|
|
408
417
|
question: step.question[ctx.language],
|
|
409
418
|
questionType: step.type,
|
|
410
|
-
options
|
|
419
|
+
options,
|
|
420
|
+
allowFreeText: resolvedAllowFreeText,
|
|
421
|
+
...pendingParameterUpdates.length > 0 && {
|
|
422
|
+
parameterUpdates: pendingParameterUpdates
|
|
423
|
+
}
|
|
411
424
|
};
|
|
412
425
|
}
|
|
413
426
|
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
414
|
-
return {
|
|
427
|
+
return {
|
|
428
|
+
type: "fulfilled",
|
|
429
|
+
dataInvestigationResult,
|
|
430
|
+
...pendingParameterUpdates.length > 0 && {
|
|
431
|
+
parameterUpdates: pendingParameterUpdates
|
|
432
|
+
}
|
|
433
|
+
};
|
|
415
434
|
}
|
|
416
435
|
|
|
417
436
|
// ../connectors/src/auth-types.ts
|
|
@@ -178,19 +178,28 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
178
178
|
};
|
|
179
179
|
let state = flow.initialState();
|
|
180
180
|
let answerIdx = 0;
|
|
181
|
+
const pendingParameterUpdates = [];
|
|
181
182
|
for (const step of flow.steps) {
|
|
182
183
|
const ans = ctx.answers[answerIdx];
|
|
183
184
|
if (ans && ans.questionSlug === step.slug) {
|
|
184
185
|
state = step.applyAnswer(state, ans.answer);
|
|
186
|
+
if (step.toParameterUpdates) {
|
|
187
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
188
|
+
}
|
|
185
189
|
answerIdx += 1;
|
|
186
190
|
continue;
|
|
187
191
|
}
|
|
192
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
188
193
|
if (step.type === "text") {
|
|
189
194
|
return {
|
|
190
195
|
type: "nextQuestion",
|
|
191
196
|
questionSlug: step.slug,
|
|
192
197
|
question: step.question[ctx.language],
|
|
193
|
-
questionType: "text"
|
|
198
|
+
questionType: "text",
|
|
199
|
+
allowFreeText: resolvedAllowFreeText,
|
|
200
|
+
...pendingParameterUpdates.length > 0 && {
|
|
201
|
+
parameterUpdates: pendingParameterUpdates
|
|
202
|
+
}
|
|
194
203
|
};
|
|
195
204
|
}
|
|
196
205
|
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
@@ -202,11 +211,21 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
202
211
|
questionSlug: step.slug,
|
|
203
212
|
question: step.question[ctx.language],
|
|
204
213
|
questionType: step.type,
|
|
205
|
-
options
|
|
214
|
+
options,
|
|
215
|
+
allowFreeText: resolvedAllowFreeText,
|
|
216
|
+
...pendingParameterUpdates.length > 0 && {
|
|
217
|
+
parameterUpdates: pendingParameterUpdates
|
|
218
|
+
}
|
|
206
219
|
};
|
|
207
220
|
}
|
|
208
221
|
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
209
|
-
return {
|
|
222
|
+
return {
|
|
223
|
+
type: "fulfilled",
|
|
224
|
+
dataInvestigationResult,
|
|
225
|
+
...pendingParameterUpdates.length > 0 && {
|
|
226
|
+
parameterUpdates: pendingParameterUpdates
|
|
227
|
+
}
|
|
228
|
+
};
|
|
210
229
|
}
|
|
211
230
|
async function resolveSetupSelection(params) {
|
|
212
231
|
const { selected, allSentinel, fetchAll, limit } = params;
|
|
@@ -365,19 +365,28 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
365
365
|
};
|
|
366
366
|
let state = flow.initialState();
|
|
367
367
|
let answerIdx = 0;
|
|
368
|
+
const pendingParameterUpdates = [];
|
|
368
369
|
for (const step of flow.steps) {
|
|
369
370
|
const ans = ctx.answers[answerIdx];
|
|
370
371
|
if (ans && ans.questionSlug === step.slug) {
|
|
371
372
|
state = step.applyAnswer(state, ans.answer);
|
|
373
|
+
if (step.toParameterUpdates) {
|
|
374
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
375
|
+
}
|
|
372
376
|
answerIdx += 1;
|
|
373
377
|
continue;
|
|
374
378
|
}
|
|
379
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
375
380
|
if (step.type === "text") {
|
|
376
381
|
return {
|
|
377
382
|
type: "nextQuestion",
|
|
378
383
|
questionSlug: step.slug,
|
|
379
384
|
question: step.question[ctx.language],
|
|
380
|
-
questionType: "text"
|
|
385
|
+
questionType: "text",
|
|
386
|
+
allowFreeText: resolvedAllowFreeText,
|
|
387
|
+
...pendingParameterUpdates.length > 0 && {
|
|
388
|
+
parameterUpdates: pendingParameterUpdates
|
|
389
|
+
}
|
|
381
390
|
};
|
|
382
391
|
}
|
|
383
392
|
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
@@ -389,11 +398,21 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
389
398
|
questionSlug: step.slug,
|
|
390
399
|
question: step.question[ctx.language],
|
|
391
400
|
questionType: step.type,
|
|
392
|
-
options
|
|
401
|
+
options,
|
|
402
|
+
allowFreeText: resolvedAllowFreeText,
|
|
403
|
+
...pendingParameterUpdates.length > 0 && {
|
|
404
|
+
parameterUpdates: pendingParameterUpdates
|
|
405
|
+
}
|
|
393
406
|
};
|
|
394
407
|
}
|
|
395
408
|
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
396
|
-
return {
|
|
409
|
+
return {
|
|
410
|
+
type: "fulfilled",
|
|
411
|
+
dataInvestigationResult,
|
|
412
|
+
...pendingParameterUpdates.length > 0 && {
|
|
413
|
+
parameterUpdates: pendingParameterUpdates
|
|
414
|
+
}
|
|
415
|
+
};
|
|
397
416
|
}
|
|
398
417
|
async function resolveSetupSelection(params) {
|
|
399
418
|
const { selected, allSentinel, fetchAll, limit } = params;
|