@squadbase/vite-server 0.1.17-dev.24af54e → 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 +1681 -449
- package/dist/connectors/airtable-oauth.js +28 -3
- package/dist/connectors/airtable.js +28 -3
- package/dist/connectors/amplitude.js +28 -3
- package/dist/connectors/asana.js +28 -3
- package/dist/connectors/attio.js +28 -3
- package/dist/connectors/aws-billing.js +28 -3
- package/dist/connectors/azure-sql.js +31 -6
- package/dist/connectors/backlog-api-key.js +28 -3
- package/dist/connectors/clickup.js +28 -3
- package/dist/connectors/cosmosdb.js +28 -3
- package/dist/connectors/customerio.js +29 -4
- package/dist/connectors/dbt.js +28 -3
- package/dist/connectors/freshdesk.js +28 -3
- package/dist/connectors/freshsales.js +28 -3
- package/dist/connectors/freshservice.js +28 -3
- package/dist/connectors/gamma.js +30 -5
- package/dist/connectors/github.js +28 -3
- package/dist/connectors/gmail-oauth.js +28 -3
- package/dist/connectors/gmail.js +28 -3
- package/dist/connectors/google-ads.js +28 -3
- package/dist/connectors/google-analytics-oauth.js +28 -3
- package/dist/connectors/google-analytics.js +227 -105
- package/dist/connectors/google-audit-log.js +28 -3
- package/dist/connectors/google-calendar-oauth.js +28 -3
- package/dist/connectors/google-calendar.js +28 -3
- package/dist/connectors/google-docs.js +28 -3
- package/dist/connectors/google-drive.js +28 -3
- package/dist/connectors/google-search-console-oauth.js +28 -3
- package/dist/connectors/google-sheets.js +28 -3
- package/dist/connectors/google-slides.js +28 -3
- package/dist/connectors/grafana.js +28 -3
- package/dist/connectors/hubspot-oauth.js +28 -3
- package/dist/connectors/hubspot.js +28 -3
- package/dist/connectors/influxdb.js +28 -3
- package/dist/connectors/intercom-oauth.js +28 -3
- package/dist/connectors/intercom.js +28 -3
- package/dist/connectors/jdbc.js +28 -3
- package/dist/connectors/jira-api-key.js +28 -3
- package/dist/connectors/kintone-api-token.js +28 -3
- package/dist/connectors/kintone.js +28 -3
- package/dist/connectors/linear.js +28 -3
- package/dist/connectors/linkedin-ads.js +28 -3
- package/dist/connectors/mailchimp-oauth.js +28 -3
- package/dist/connectors/mailchimp.js +28 -3
- package/dist/connectors/meta-ads-oauth.js +28 -3
- package/dist/connectors/meta-ads.js +28 -3
- package/dist/connectors/mixpanel.js +28 -3
- package/dist/connectors/monday.js +28 -3
- package/dist/connectors/mongodb.js +28 -3
- package/dist/connectors/notion-oauth.js +28 -3
- package/dist/connectors/notion.js +28 -3
- package/dist/connectors/oracle.js +54 -14
- package/dist/connectors/outlook-oauth.js +28 -3
- package/dist/connectors/powerbi-oauth.js +309 -37
- package/dist/connectors/salesforce.js +28 -3
- package/dist/connectors/semrush.js +366 -46
- package/dist/connectors/sentry.js +28 -3
- package/dist/connectors/shopify-oauth.js +28 -3
- package/dist/connectors/shopify.js +28 -3
- package/dist/connectors/sqlserver.js +31 -6
- package/dist/connectors/stripe-api-key.js +28 -3
- package/dist/connectors/stripe-oauth.js +28 -3
- package/dist/connectors/supabase.js +31 -6
- package/dist/connectors/tableau.js +246 -78
- package/dist/connectors/tiktok-ads.js +28 -3
- package/dist/connectors/wix-store.js +28 -3
- package/dist/connectors/zendesk-oauth.js +28 -3
- package/dist/connectors/zendesk.js +28 -3
- package/dist/index.js +1681 -449
- package/dist/main.js +1681 -449
- package/dist/vite-plugin.js +1681 -449
- package/package.json +1 -1
|
@@ -67,15 +67,6 @@ var parameters = {
|
|
|
67
67
|
type: "base64EncodedJson",
|
|
68
68
|
secret: true,
|
|
69
69
|
required: true
|
|
70
|
-
}),
|
|
71
|
-
propertyId: new ParameterDefinition({
|
|
72
|
-
slug: "property-id",
|
|
73
|
-
name: "Google Analytics Property ID",
|
|
74
|
-
description: "The Google Analytics 4 property ID (e.g., 123456789).",
|
|
75
|
-
envVarBaseKey: "GA_PROPERTY_ID",
|
|
76
|
-
type: "text",
|
|
77
|
-
secret: false,
|
|
78
|
-
required: true
|
|
79
70
|
})
|
|
80
71
|
};
|
|
81
72
|
|
|
@@ -107,15 +98,9 @@ function buildJwt(clientEmail, privateKey, nowSec) {
|
|
|
107
98
|
}
|
|
108
99
|
function createClient(params) {
|
|
109
100
|
const serviceAccountKeyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
|
|
110
|
-
|
|
111
|
-
if (!serviceAccountKeyJsonBase64 || !propertyId) {
|
|
112
|
-
const required = [
|
|
113
|
-
parameters.serviceAccountKeyJsonBase64.slug,
|
|
114
|
-
parameters.propertyId.slug
|
|
115
|
-
];
|
|
116
|
-
const missing = required.filter((s) => !params[s]);
|
|
101
|
+
if (!serviceAccountKeyJsonBase64) {
|
|
117
102
|
throw new Error(
|
|
118
|
-
`google-analytics: missing required parameters: ${
|
|
103
|
+
`google-analytics: missing required parameters: ${parameters.serviceAccountKeyJsonBase64.slug}`
|
|
119
104
|
);
|
|
120
105
|
}
|
|
121
106
|
let serviceAccountKey;
|
|
@@ -169,13 +154,12 @@ function createClient(params) {
|
|
|
169
154
|
return {
|
|
170
155
|
async request(path2, init) {
|
|
171
156
|
const accessToken = await getAccessToken2();
|
|
172
|
-
const
|
|
173
|
-
const url = `${BASE_URL.replace(/\/+$/, "")}/${resolvedPath.replace(/^\/+/, "")}`;
|
|
157
|
+
const url = `${BASE_URL.replace(/\/+$/, "")}/${path2.replace(/^\/+/, "")}`;
|
|
174
158
|
const headers = new Headers(init?.headers);
|
|
175
159
|
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
176
160
|
return fetch(url, { ...init, headers });
|
|
177
161
|
},
|
|
178
|
-
async runReport(request) {
|
|
162
|
+
async runReport(propertyId, request) {
|
|
179
163
|
const response = await this.request(
|
|
180
164
|
`properties/${propertyId}:runReport`,
|
|
181
165
|
{
|
|
@@ -196,7 +180,7 @@ function createClient(params) {
|
|
|
196
180
|
rowCount: data.rowCount ?? 0
|
|
197
181
|
};
|
|
198
182
|
},
|
|
199
|
-
async getMetadata() {
|
|
183
|
+
async getMetadata(propertyId) {
|
|
200
184
|
const response = await this.request(
|
|
201
185
|
`properties/${propertyId}/metadata`,
|
|
202
186
|
{ method: "GET" }
|
|
@@ -209,7 +193,7 @@ function createClient(params) {
|
|
|
209
193
|
}
|
|
210
194
|
return await response.json();
|
|
211
195
|
},
|
|
212
|
-
async runRealtimeReport(request) {
|
|
196
|
+
async runRealtimeReport(propertyId, request) {
|
|
213
197
|
const response = await this.request(
|
|
214
198
|
`properties/${propertyId}:runRealtimeReport`,
|
|
215
199
|
{
|
|
@@ -405,19 +389,34 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
405
389
|
};
|
|
406
390
|
let state = flow.initialState();
|
|
407
391
|
let answerIdx = 0;
|
|
392
|
+
const pendingParameterUpdates = [];
|
|
408
393
|
for (const step of flow.steps) {
|
|
409
394
|
const ans = ctx.answers[answerIdx];
|
|
410
395
|
if (ans && ans.questionSlug === step.slug) {
|
|
411
396
|
state = step.applyAnswer(state, ans.answer);
|
|
397
|
+
if (step.toParameterUpdates) {
|
|
398
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
399
|
+
}
|
|
412
400
|
answerIdx += 1;
|
|
413
401
|
continue;
|
|
414
402
|
}
|
|
403
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
415
404
|
if (step.type === "text") {
|
|
405
|
+
if (step.fetchOptions) {
|
|
406
|
+
const options2 = await step.fetchOptions(state, runtime);
|
|
407
|
+
if (options2.length === 0) {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
416
411
|
return {
|
|
417
412
|
type: "nextQuestion",
|
|
418
413
|
questionSlug: step.slug,
|
|
419
414
|
question: step.question[ctx.language],
|
|
420
|
-
questionType: "text"
|
|
415
|
+
questionType: "text",
|
|
416
|
+
allowFreeText: resolvedAllowFreeText,
|
|
417
|
+
...pendingParameterUpdates.length > 0 && {
|
|
418
|
+
parameterUpdates: pendingParameterUpdates
|
|
419
|
+
}
|
|
421
420
|
};
|
|
422
421
|
}
|
|
423
422
|
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
@@ -429,11 +428,21 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
429
428
|
questionSlug: step.slug,
|
|
430
429
|
question: step.question[ctx.language],
|
|
431
430
|
questionType: step.type,
|
|
432
|
-
options
|
|
431
|
+
options,
|
|
432
|
+
allowFreeText: resolvedAllowFreeText,
|
|
433
|
+
...pendingParameterUpdates.length > 0 && {
|
|
434
|
+
parameterUpdates: pendingParameterUpdates
|
|
435
|
+
}
|
|
433
436
|
};
|
|
434
437
|
}
|
|
435
438
|
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
436
|
-
return {
|
|
439
|
+
return {
|
|
440
|
+
type: "fulfilled",
|
|
441
|
+
dataInvestigationResult,
|
|
442
|
+
...pendingParameterUpdates.length > 0 && {
|
|
443
|
+
parameterUpdates: pendingParameterUpdates
|
|
444
|
+
}
|
|
445
|
+
};
|
|
437
446
|
}
|
|
438
447
|
async function resolveSetupSelection(params) {
|
|
439
448
|
const { selected, allSentinel, fetchAll, limit } = params;
|
|
@@ -465,6 +474,7 @@ var googleAnalyticsOnboarding = new ConnectorOnboarding({
|
|
|
465
474
|
import * as crypto2 from "crypto";
|
|
466
475
|
var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
|
|
467
476
|
var ADMIN_BASE_URL = "https://analyticsadmin.googleapis.com/v1beta";
|
|
477
|
+
var DATA_BASE_URL = "https://analyticsdata.googleapis.com/v1beta";
|
|
468
478
|
var SCOPE2 = "https://www.googleapis.com/auth/analytics.readonly";
|
|
469
479
|
function base64url2(input) {
|
|
470
480
|
const buf = typeof input === "string" ? Buffer.from(input) : input;
|
|
@@ -546,10 +556,25 @@ async function adminFetch(params, path2, init) {
|
|
|
546
556
|
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
547
557
|
return fetch(url, { ...init, headers });
|
|
548
558
|
}
|
|
559
|
+
async function dataFetch(params, path2, init) {
|
|
560
|
+
const keyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
|
|
561
|
+
if (!keyJsonBase64) {
|
|
562
|
+
throw new Error(
|
|
563
|
+
"google-analytics: missing required parameter: service-account-key-json-base64"
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
const serviceAccountKey = decodeServiceAccount(keyJsonBase64);
|
|
567
|
+
const accessToken = await getAccessToken(serviceAccountKey);
|
|
568
|
+
const url = `${DATA_BASE_URL}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
569
|
+
const headers = new Headers(init?.headers);
|
|
570
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
571
|
+
return fetch(url, { ...init, headers });
|
|
572
|
+
}
|
|
549
573
|
|
|
550
574
|
// ../connectors/src/connectors/google-analytics/setup-flow.ts
|
|
551
575
|
var ALL_PROPERTIES = "__ALL_PROPERTIES__";
|
|
552
576
|
var GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES = 20;
|
|
577
|
+
var METADATA_DISPLAY_LIMIT = 30;
|
|
553
578
|
async function listAccountSummaries(params) {
|
|
554
579
|
const summaries = [];
|
|
555
580
|
let pageToken;
|
|
@@ -576,6 +601,48 @@ async function getProperty(params, propertyId) {
|
|
|
576
601
|
if (!res.ok) return null;
|
|
577
602
|
return await res.json();
|
|
578
603
|
}
|
|
604
|
+
async function getMetadata(params, propertyId) {
|
|
605
|
+
try {
|
|
606
|
+
const res = await dataFetch(
|
|
607
|
+
params,
|
|
608
|
+
`/properties/${propertyId}/metadata`
|
|
609
|
+
);
|
|
610
|
+
if (!res.ok) return { dimensions: [], metrics: [] };
|
|
611
|
+
const data = await res.json();
|
|
612
|
+
return {
|
|
613
|
+
dimensions: data.dimensions ?? [],
|
|
614
|
+
metrics: data.metrics ?? []
|
|
615
|
+
};
|
|
616
|
+
} catch {
|
|
617
|
+
return { dimensions: [], metrics: [] };
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
function appendMetadataSection(sections, dimensions, metrics) {
|
|
621
|
+
if (dimensions.length > 0) {
|
|
622
|
+
sections.push(`#### Dimensions (${dimensions.length})`, "");
|
|
623
|
+
for (const d of dimensions.slice(0, METADATA_DISPLAY_LIMIT)) {
|
|
624
|
+
sections.push(`- ${d.apiName ?? d.uiName ?? "(unknown)"}`);
|
|
625
|
+
}
|
|
626
|
+
if (dimensions.length > METADATA_DISPLAY_LIMIT) {
|
|
627
|
+
sections.push(
|
|
628
|
+
`- \u2026and ${dimensions.length - METADATA_DISPLAY_LIMIT} more`
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
sections.push("");
|
|
632
|
+
}
|
|
633
|
+
if (metrics.length > 0) {
|
|
634
|
+
sections.push(`#### Metrics (${metrics.length})`, "");
|
|
635
|
+
for (const m of metrics.slice(0, METADATA_DISPLAY_LIMIT)) {
|
|
636
|
+
sections.push(`- ${m.apiName ?? m.uiName ?? "(unknown)"}`);
|
|
637
|
+
}
|
|
638
|
+
if (metrics.length > METADATA_DISPLAY_LIMIT) {
|
|
639
|
+
sections.push(
|
|
640
|
+
`- \u2026and ${metrics.length - METADATA_DISPLAY_LIMIT} more`
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
sections.push("");
|
|
644
|
+
}
|
|
645
|
+
}
|
|
579
646
|
var googleAnalyticsSetupFlow = {
|
|
580
647
|
initialState: () => ({}),
|
|
581
648
|
steps: [
|
|
@@ -587,15 +654,19 @@ var googleAnalyticsSetupFlow = {
|
|
|
587
654
|
en: "Select a Google Analytics account"
|
|
588
655
|
},
|
|
589
656
|
async fetchOptions(_state, rt) {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
657
|
+
try {
|
|
658
|
+
const summaries = await listAccountSummaries(rt.params);
|
|
659
|
+
return summaries.map((s) => {
|
|
660
|
+
const accountResource = s.account ?? s.name ?? "";
|
|
661
|
+
if (!accountResource) return null;
|
|
662
|
+
return {
|
|
663
|
+
value: accountResource,
|
|
664
|
+
label: s.displayName ?? accountResource
|
|
665
|
+
};
|
|
666
|
+
}).filter((v) => v != null);
|
|
667
|
+
} catch {
|
|
668
|
+
return [];
|
|
669
|
+
}
|
|
599
670
|
},
|
|
600
671
|
applyAnswer: (state, answer) => ({ ...state, account: answer[0] })
|
|
601
672
|
},
|
|
@@ -608,68 +679,117 @@ var googleAnalyticsSetupFlow = {
|
|
|
608
679
|
},
|
|
609
680
|
async fetchOptions(state, rt) {
|
|
610
681
|
if (!state.account) return [];
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
682
|
+
try {
|
|
683
|
+
const summaries = await listAccountSummaries(rt.params);
|
|
684
|
+
const account = summaries.find(
|
|
685
|
+
(s) => (s.account ?? s.name) === state.account
|
|
686
|
+
);
|
|
687
|
+
const props = (account?.propertySummaries ?? []).map((p) => {
|
|
688
|
+
const id = propertyIdFromResource(p.property);
|
|
689
|
+
if (!id) return null;
|
|
690
|
+
return {
|
|
691
|
+
value: id,
|
|
692
|
+
label: p.displayName ? `${p.displayName} (${id})` : id
|
|
693
|
+
};
|
|
694
|
+
}).filter((v) => v != null);
|
|
695
|
+
if (props.length === 0) return [];
|
|
696
|
+
return [
|
|
697
|
+
{
|
|
698
|
+
value: ALL_PROPERTIES,
|
|
699
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D7\u30ED\u30D1\u30C6\u30A3" : "All properties"
|
|
700
|
+
},
|
|
701
|
+
...props
|
|
702
|
+
];
|
|
703
|
+
} catch {
|
|
704
|
+
return [];
|
|
705
|
+
}
|
|
631
706
|
},
|
|
632
707
|
applyAnswer: (state, answer) => ({ ...state, properties: answer })
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
slug: "manualPropertyId",
|
|
711
|
+
type: "text",
|
|
712
|
+
question: {
|
|
713
|
+
ja: "GA4 \u30D7\u30ED\u30D1\u30C6\u30A3 ID \u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: 123456789\uFF09\u3002GA4 \u7BA1\u7406\u753B\u9762 > \u30D7\u30ED\u30D1\u30C6\u30A3\u8A2D\u5B9A\u3067\u78BA\u8A8D\u3067\u304D\u307E\u3059\u3002",
|
|
714
|
+
en: "Enter your GA4 Property ID (e.g., 123456789). Found in GA4 Admin > Property Settings."
|
|
715
|
+
},
|
|
716
|
+
async fetchOptions(state) {
|
|
717
|
+
if (state.properties?.length) return [];
|
|
718
|
+
return [{ value: "_show", label: "" }];
|
|
719
|
+
},
|
|
720
|
+
applyAnswer: (state, answer) => ({
|
|
721
|
+
...state,
|
|
722
|
+
manualPropertyId: answer[0]
|
|
723
|
+
})
|
|
633
724
|
}
|
|
634
725
|
],
|
|
635
726
|
async finalize(state, rt) {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
`### Account: ${accountLabel}`,
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
727
|
+
const sections = ["## Google Analytics", ""];
|
|
728
|
+
if (state.account && state.properties) {
|
|
729
|
+
let summaries = [];
|
|
730
|
+
try {
|
|
731
|
+
summaries = await listAccountSummaries(rt.params);
|
|
732
|
+
} catch {
|
|
733
|
+
}
|
|
734
|
+
const account = summaries.find(
|
|
735
|
+
(s) => (s.account ?? s.name) === state.account
|
|
736
|
+
);
|
|
737
|
+
const accountLabel = account?.displayName ?? state.account.replace(/^accounts\//, "");
|
|
738
|
+
const targetPropertyIds = await resolveSetupSelection({
|
|
739
|
+
selected: state.properties,
|
|
740
|
+
allSentinel: ALL_PROPERTIES,
|
|
741
|
+
fetchAll: async () => (account?.propertySummaries ?? []).map((p) => propertyIdFromResource(p.property)).filter((v) => v),
|
|
742
|
+
limit: GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES
|
|
743
|
+
});
|
|
744
|
+
sections.push(`### Account: ${accountLabel}`, "");
|
|
745
|
+
if (targetPropertyIds.length === 0) {
|
|
746
|
+
sections.push("_No properties selected._", "");
|
|
747
|
+
return sections.join("\n");
|
|
748
|
+
}
|
|
749
|
+
sections.push(
|
|
750
|
+
"| Property ID | Display Name | Time Zone | Currency | Industry |"
|
|
751
|
+
);
|
|
752
|
+
sections.push(
|
|
753
|
+
"|-------------|--------------|-----------|----------|----------|"
|
|
754
|
+
);
|
|
755
|
+
for (const pid of targetPropertyIds) {
|
|
756
|
+
const prop = await getProperty(rt.params, pid).catch(() => null);
|
|
757
|
+
const displayName = (prop?.displayName ?? "-").replace(/\|/g, "\\|");
|
|
758
|
+
const timeZone = prop?.timeZone ?? "-";
|
|
759
|
+
const currency = prop?.currencyCode ?? "-";
|
|
760
|
+
const industry = (prop?.industryCategory ?? "-").replace(
|
|
761
|
+
/\|/g,
|
|
762
|
+
"\\|"
|
|
763
|
+
);
|
|
764
|
+
sections.push(
|
|
765
|
+
`| ${pid} | ${displayName} | ${timeZone} | ${currency} | ${industry} |`
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
sections.push("");
|
|
769
|
+
const firstPropertyId = targetPropertyIds[0];
|
|
770
|
+
if (firstPropertyId) {
|
|
771
|
+
const { dimensions, metrics } = await getMetadata(
|
|
772
|
+
rt.params,
|
|
773
|
+
firstPropertyId
|
|
774
|
+
);
|
|
775
|
+
appendMetadataSection(sections, dimensions, metrics);
|
|
776
|
+
}
|
|
658
777
|
return sections.join("\n");
|
|
659
778
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
const currency = prop?.currencyCode ?? "-";
|
|
667
|
-
const industry = (prop?.industryCategory ?? "-").replace(/\|/g, "\\|");
|
|
668
|
-
sections.push(
|
|
669
|
-
`| ${propertyId} | ${displayName} | ${timeZone} | ${currency} | ${industry} |`
|
|
779
|
+
const propertyId = state.manualPropertyId;
|
|
780
|
+
if (propertyId) {
|
|
781
|
+
sections.push(`### Property: ${propertyId}`, "");
|
|
782
|
+
const { dimensions, metrics } = await getMetadata(
|
|
783
|
+
rt.params,
|
|
784
|
+
propertyId
|
|
670
785
|
);
|
|
786
|
+
appendMetadataSection(sections, dimensions, metrics);
|
|
787
|
+
return sections.join("\n");
|
|
671
788
|
}
|
|
672
|
-
sections.push(
|
|
789
|
+
sections.push(
|
|
790
|
+
"_Could not list GA4 accounts. Please enable the Google Analytics Admin API in your GCP project. Property ID can be specified per request at runtime._",
|
|
791
|
+
""
|
|
792
|
+
);
|
|
673
793
|
return sections.join("\n");
|
|
674
794
|
}
|
|
675
795
|
};
|
|
@@ -681,8 +801,9 @@ var REQUEST_TIMEOUT_MS = 6e4;
|
|
|
681
801
|
var inputSchema = z.object({
|
|
682
802
|
toolUseIntent: z.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
|
|
683
803
|
connectionId: z.string().describe("ID of the Google Analytics connection to use"),
|
|
804
|
+
propertyId: z.string().describe("GA4 property ID (e.g., '123456789')"),
|
|
684
805
|
method: z.enum(["GET", "POST"]).describe("HTTP method"),
|
|
685
|
-
path: z.string().describe("API path (e.g., 'properties/{propertyId}:runReport'). {propertyId} is
|
|
806
|
+
path: z.string().describe("API path (e.g., 'properties/{propertyId}:runReport'). {propertyId} is replaced with the propertyId parameter."),
|
|
686
807
|
body: z.record(z.string(), z.unknown()).optional().describe("POST request body (JSON)")
|
|
687
808
|
});
|
|
688
809
|
var outputSchema = z.discriminatedUnion("success", [
|
|
@@ -700,10 +821,10 @@ var requestTool = new ConnectorTool({
|
|
|
700
821
|
name: "request",
|
|
701
822
|
description: `Send authenticated requests to the Google Analytics Data API.
|
|
702
823
|
Authentication is handled automatically using a service account.
|
|
703
|
-
{propertyId} in the path is automatically replaced with the
|
|
824
|
+
{propertyId} in the path is automatically replaced with the propertyId parameter.`,
|
|
704
825
|
inputSchema,
|
|
705
826
|
outputSchema,
|
|
706
|
-
async execute({ connectionId, method, path: path2, body }, connections) {
|
|
827
|
+
async execute({ connectionId, propertyId, method, path: path2, body }, connections) {
|
|
707
828
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
708
829
|
if (!connection2) {
|
|
709
830
|
return { success: false, error: `Connection ${connectionId} not found` };
|
|
@@ -712,7 +833,6 @@ Authentication is handled automatically using a service account.
|
|
|
712
833
|
try {
|
|
713
834
|
const { GoogleAuth } = await import("google-auth-library");
|
|
714
835
|
const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
|
|
715
|
-
const propertyId = parameters.propertyId.getValue(connection2);
|
|
716
836
|
const credentials = JSON.parse(
|
|
717
837
|
Buffer.from(keyJsonBase64, "base64").toString("utf-8")
|
|
718
838
|
);
|
|
@@ -772,16 +892,16 @@ var googleAnalyticsConnector = new ConnectorPlugin({
|
|
|
772
892
|
systemPrompt: {
|
|
773
893
|
en: `### Tools
|
|
774
894
|
|
|
775
|
-
- \`google-analytics-service-account_request\`: The only way to call the GA4 Data API. Use it to fetch metadata, run reports, or run realtime reports. See the GA4 Data API Reference below for available endpoints and request bodies.
|
|
895
|
+
- \`google-analytics-service-account_request\`: The only way to call the GA4 Data API. Use it to fetch metadata, run reports, or run realtime reports. Requires a \`propertyId\` parameter. See the GA4 Data API Reference below for available endpoints and request bodies.
|
|
776
896
|
|
|
777
897
|
### Business Logic
|
|
778
898
|
|
|
779
899
|
The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
|
|
780
900
|
|
|
781
901
|
SDK methods (client created via \`connection(connectionId)\`):
|
|
782
|
-
- \`client.runReport(request)\` \u2014 run a GA4 report
|
|
783
|
-
- \`client.runRealtimeReport(request)\` \u2014 run a realtime report
|
|
784
|
-
- \`client.getMetadata()\` \u2014 fetch available dimensions/metrics
|
|
902
|
+
- \`client.runReport(propertyId, request)\` \u2014 run a GA4 report
|
|
903
|
+
- \`client.runRealtimeReport(propertyId, request)\` \u2014 run a realtime report
|
|
904
|
+
- \`client.getMetadata(propertyId)\` \u2014 fetch available dimensions/metrics
|
|
785
905
|
- \`client.request(path, init?)\` \u2014 low-level authenticated fetch
|
|
786
906
|
|
|
787
907
|
\`\`\`ts
|
|
@@ -791,12 +911,13 @@ import { connection } from "@squadbase/vite-server/connectors/google-analytics";
|
|
|
791
911
|
const ga = connection("<connectionId>");
|
|
792
912
|
|
|
793
913
|
export default async function handler(c: Context) {
|
|
794
|
-
const { startDate = "7daysAgo", endDate = "today" } = await c.req.json<{
|
|
914
|
+
const { propertyId, startDate = "7daysAgo", endDate = "today" } = await c.req.json<{
|
|
915
|
+
propertyId: string;
|
|
795
916
|
startDate?: string;
|
|
796
917
|
endDate?: string;
|
|
797
918
|
}>();
|
|
798
919
|
|
|
799
|
-
const { rows } = await ga.runReport({
|
|
920
|
+
const { rows } = await ga.runReport(propertyId, {
|
|
800
921
|
dateRanges: [{ startDate, endDate }],
|
|
801
922
|
dimensions: [{ name: "date" }],
|
|
802
923
|
metrics: [{ name: "activeUsers" }, { name: "sessions" }],
|
|
@@ -840,16 +961,16 @@ activeUsers, sessions, screenPageViews, bounceRate, averageSessionDuration, conv
|
|
|
840
961
|
- Relative: \`"today"\`, \`"yesterday"\`, \`"7daysAgo"\`, \`"30daysAgo"\``,
|
|
841
962
|
ja: `### \u30C4\u30FC\u30EB
|
|
842
963
|
|
|
843
|
-
- \`google-analytics-service-account_request\`: GA4 Data API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30E1\u30BF\u30C7\u30FC\u30BF\u306E\u53D6\u5F97\u3001\u30EC\u30DD\u30FC\u30C8\u306E\u5B9F\u884C\u3001\u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u30EC\u30DD\u30FC\u30C8\u306E\u5B9F\u884C\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3068\u30EA\u30AF\u30A8\u30B9\u30C8\u30DC\u30C7\u30A3\u306F\u4E0B\u90E8\u306E\u300CGA4 Data API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
964
|
+
- \`google-analytics-service-account_request\`: GA4 Data API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30E1\u30BF\u30C7\u30FC\u30BF\u306E\u53D6\u5F97\u3001\u30EC\u30DD\u30FC\u30C8\u306E\u5B9F\u884C\u3001\u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u30EC\u30DD\u30FC\u30C8\u306E\u5B9F\u884C\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\`propertyId\` \u30D1\u30E9\u30E1\u30FC\u30BF\u30FC\u304C\u5FC5\u8981\u3067\u3059\u3002\u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3068\u30EA\u30AF\u30A8\u30B9\u30C8\u30DC\u30C7\u30A3\u306F\u4E0B\u90E8\u306E\u300CGA4 Data API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
844
965
|
|
|
845
966
|
### Business Logic
|
|
846
967
|
|
|
847
968
|
\u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u8A8D\u8A3C\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
|
|
848
969
|
|
|
849
970
|
SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
|
|
850
|
-
- \`client.runReport(request)\` \u2014 GA4\u30EC\u30DD\u30FC\u30C8\u3092\u5B9F\u884C
|
|
851
|
-
- \`client.runRealtimeReport(request)\` \u2014 \u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u30EC\u30DD\u30FC\u30C8\u3092\u5B9F\u884C
|
|
852
|
-
- \`client.getMetadata()\` \u2014 \u5229\u7528\u53EF\u80FD\u306A\u30C7\u30A3\u30E1\u30F3\u30B7\u30E7\u30F3/\u30E1\u30C8\u30EA\u30AF\u30B9\u3092\u53D6\u5F97
|
|
971
|
+
- \`client.runReport(propertyId, request)\` \u2014 GA4\u30EC\u30DD\u30FC\u30C8\u3092\u5B9F\u884C
|
|
972
|
+
- \`client.runRealtimeReport(propertyId, request)\` \u2014 \u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u30EC\u30DD\u30FC\u30C8\u3092\u5B9F\u884C
|
|
973
|
+
- \`client.getMetadata(propertyId)\` \u2014 \u5229\u7528\u53EF\u80FD\u306A\u30C7\u30A3\u30E1\u30F3\u30B7\u30E7\u30F3/\u30E1\u30C8\u30EA\u30AF\u30B9\u3092\u53D6\u5F97
|
|
853
974
|
- \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304Dfetch
|
|
854
975
|
|
|
855
976
|
\`\`\`ts
|
|
@@ -859,12 +980,13 @@ import { connection } from "@squadbase/vite-server/connectors/google-analytics";
|
|
|
859
980
|
const ga = connection("<connectionId>");
|
|
860
981
|
|
|
861
982
|
export default async function handler(c: Context) {
|
|
862
|
-
const { startDate = "7daysAgo", endDate = "today" } = await c.req.json<{
|
|
983
|
+
const { propertyId, startDate = "7daysAgo", endDate = "today" } = await c.req.json<{
|
|
984
|
+
propertyId: string;
|
|
863
985
|
startDate?: string;
|
|
864
986
|
endDate?: string;
|
|
865
987
|
}>();
|
|
866
988
|
|
|
867
|
-
const { rows } = await ga.runReport({
|
|
989
|
+
const { rows } = await ga.runReport(propertyId, {
|
|
868
990
|
dateRanges: [{ startDate, endDate }],
|
|
869
991
|
dimensions: [{ name: "date" }],
|
|
870
992
|
metrics: [{ name: "activeUsers" }, { name: "sessions" }],
|
|
@@ -918,12 +1040,12 @@ activeUsers, sessions, screenPageViews, bounceRate, averageSessionDuration, conv
|
|
|
918
1040
|
};
|
|
919
1041
|
}
|
|
920
1042
|
try {
|
|
921
|
-
const res = await
|
|
1043
|
+
const res = await dataFetch(params, `/metadata`);
|
|
922
1044
|
if (!res.ok) {
|
|
923
1045
|
const body = await res.text().catch(() => res.statusText);
|
|
924
1046
|
return {
|
|
925
1047
|
success: false,
|
|
926
|
-
error: `
|
|
1048
|
+
error: `Google Analytics API failed: HTTP ${res.status} ${body}`
|
|
927
1049
|
};
|
|
928
1050
|
}
|
|
929
1051
|
return { success: true };
|
|
@@ -290,19 +290,34 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
290
290
|
};
|
|
291
291
|
let state = flow.initialState();
|
|
292
292
|
let answerIdx = 0;
|
|
293
|
+
const pendingParameterUpdates = [];
|
|
293
294
|
for (const step of flow.steps) {
|
|
294
295
|
const ans = ctx.answers[answerIdx];
|
|
295
296
|
if (ans && ans.questionSlug === step.slug) {
|
|
296
297
|
state = step.applyAnswer(state, ans.answer);
|
|
298
|
+
if (step.toParameterUpdates) {
|
|
299
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
300
|
+
}
|
|
297
301
|
answerIdx += 1;
|
|
298
302
|
continue;
|
|
299
303
|
}
|
|
304
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
300
305
|
if (step.type === "text") {
|
|
306
|
+
if (step.fetchOptions) {
|
|
307
|
+
const options2 = await step.fetchOptions(state, runtime);
|
|
308
|
+
if (options2.length === 0) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
301
312
|
return {
|
|
302
313
|
type: "nextQuestion",
|
|
303
314
|
questionSlug: step.slug,
|
|
304
315
|
question: step.question[ctx.language],
|
|
305
|
-
questionType: "text"
|
|
316
|
+
questionType: "text",
|
|
317
|
+
allowFreeText: resolvedAllowFreeText,
|
|
318
|
+
...pendingParameterUpdates.length > 0 && {
|
|
319
|
+
parameterUpdates: pendingParameterUpdates
|
|
320
|
+
}
|
|
306
321
|
};
|
|
307
322
|
}
|
|
308
323
|
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
@@ -314,11 +329,21 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
314
329
|
questionSlug: step.slug,
|
|
315
330
|
question: step.question[ctx.language],
|
|
316
331
|
questionType: step.type,
|
|
317
|
-
options
|
|
332
|
+
options,
|
|
333
|
+
allowFreeText: resolvedAllowFreeText,
|
|
334
|
+
...pendingParameterUpdates.length > 0 && {
|
|
335
|
+
parameterUpdates: pendingParameterUpdates
|
|
336
|
+
}
|
|
318
337
|
};
|
|
319
338
|
}
|
|
320
339
|
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
321
|
-
return {
|
|
340
|
+
return {
|
|
341
|
+
type: "fulfilled",
|
|
342
|
+
dataInvestigationResult,
|
|
343
|
+
...pendingParameterUpdates.length > 0 && {
|
|
344
|
+
parameterUpdates: pendingParameterUpdates
|
|
345
|
+
}
|
|
346
|
+
};
|
|
322
347
|
}
|
|
323
348
|
async function resolveSetupSelection(params) {
|
|
324
349
|
const { selected, allSentinel, fetchAll, limit } = params;
|
|
@@ -306,19 +306,34 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
306
306
|
};
|
|
307
307
|
let state = flow.initialState();
|
|
308
308
|
let answerIdx = 0;
|
|
309
|
+
const pendingParameterUpdates = [];
|
|
309
310
|
for (const step of flow.steps) {
|
|
310
311
|
const ans = ctx.answers[answerIdx];
|
|
311
312
|
if (ans && ans.questionSlug === step.slug) {
|
|
312
313
|
state = step.applyAnswer(state, ans.answer);
|
|
314
|
+
if (step.toParameterUpdates) {
|
|
315
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
316
|
+
}
|
|
313
317
|
answerIdx += 1;
|
|
314
318
|
continue;
|
|
315
319
|
}
|
|
320
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
316
321
|
if (step.type === "text") {
|
|
322
|
+
if (step.fetchOptions) {
|
|
323
|
+
const options2 = await step.fetchOptions(state, runtime);
|
|
324
|
+
if (options2.length === 0) {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
317
328
|
return {
|
|
318
329
|
type: "nextQuestion",
|
|
319
330
|
questionSlug: step.slug,
|
|
320
331
|
question: step.question[ctx.language],
|
|
321
|
-
questionType: "text"
|
|
332
|
+
questionType: "text",
|
|
333
|
+
allowFreeText: resolvedAllowFreeText,
|
|
334
|
+
...pendingParameterUpdates.length > 0 && {
|
|
335
|
+
parameterUpdates: pendingParameterUpdates
|
|
336
|
+
}
|
|
322
337
|
};
|
|
323
338
|
}
|
|
324
339
|
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
@@ -330,11 +345,21 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
330
345
|
questionSlug: step.slug,
|
|
331
346
|
question: step.question[ctx.language],
|
|
332
347
|
questionType: step.type,
|
|
333
|
-
options
|
|
348
|
+
options,
|
|
349
|
+
allowFreeText: resolvedAllowFreeText,
|
|
350
|
+
...pendingParameterUpdates.length > 0 && {
|
|
351
|
+
parameterUpdates: pendingParameterUpdates
|
|
352
|
+
}
|
|
334
353
|
};
|
|
335
354
|
}
|
|
336
355
|
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
337
|
-
return {
|
|
356
|
+
return {
|
|
357
|
+
type: "fulfilled",
|
|
358
|
+
dataInvestigationResult,
|
|
359
|
+
...pendingParameterUpdates.length > 0 && {
|
|
360
|
+
parameterUpdates: pendingParameterUpdates
|
|
361
|
+
}
|
|
362
|
+
};
|
|
338
363
|
}
|
|
339
364
|
async function resolveSetupSelection(params) {
|
|
340
365
|
const { selected, allSentinel, fetchAll, limit } = params;
|