@squadbase/vite-server 0.1.12-dev.93b8799 → 0.1.17-dev.24af54e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +12128 -934
- package/dist/connectors/airtable-oauth.js +248 -46
- package/dist/connectors/airtable.js +285 -51
- package/dist/connectors/amplitude.js +288 -47
- package/dist/connectors/anthropic.js +126 -47
- package/dist/connectors/asana.js +293 -49
- package/dist/connectors/attio.js +268 -49
- package/dist/connectors/aws-billing.js +253 -46
- package/dist/connectors/azure-sql.js +387 -102
- package/dist/connectors/backlog-api-key.js +283 -47
- package/dist/connectors/clickup.js +304 -49
- package/dist/connectors/cosmosdb.js +271 -50
- package/dist/connectors/customerio.js +285 -47
- package/dist/connectors/dbt.js +306 -47
- package/dist/connectors/freshdesk.js +308 -53
- package/dist/connectors/freshsales.js +299 -52
- package/dist/connectors/freshservice.js +327 -53
- package/dist/connectors/gamma.js +293 -52
- package/dist/connectors/gemini.js +125 -47
- package/dist/connectors/github.js +352 -49
- package/dist/connectors/gmail-oauth.js +170 -7
- package/dist/connectors/gmail.js +316 -47
- package/dist/connectors/google-ads.js +254 -46
- package/dist/connectors/google-analytics-oauth.js +276 -46
- package/dist/connectors/google-analytics.js +378 -49
- package/dist/connectors/google-audit-log.js +404 -47
- package/dist/connectors/google-calendar-oauth.js +225 -46
- package/dist/connectors/google-calendar.js +325 -47
- package/dist/connectors/google-docs.js +186 -6
- package/dist/connectors/google-drive.js +228 -5
- package/dist/connectors/google-search-console-oauth.js +222 -46
- package/dist/connectors/google-sheets.js +238 -47
- package/dist/connectors/google-slides.js +171 -6
- package/dist/connectors/grafana.js +298 -49
- package/dist/connectors/hubspot-oauth.js +174 -5
- package/dist/connectors/hubspot.js +272 -49
- package/dist/connectors/influxdb.js +382 -51
- package/dist/connectors/intercom-oauth.js +176 -5
- package/dist/connectors/intercom.js +268 -49
- package/dist/connectors/jdbc.js +728 -110
- package/dist/connectors/jira-api-key.js +292 -47
- package/dist/connectors/kintone-api-token.js +247 -47
- package/dist/connectors/kintone.js +294 -47
- package/dist/connectors/linear.js +296 -49
- package/dist/connectors/linkedin-ads.js +234 -50
- package/dist/connectors/mailchimp-oauth.js +234 -46
- package/dist/connectors/mailchimp.js +286 -49
- package/dist/connectors/meta-ads-oauth.js +239 -48
- package/dist/connectors/meta-ads.js +251 -50
- package/dist/connectors/mixpanel.js +304 -47
- package/dist/connectors/monday.js +326 -49
- package/dist/connectors/mongodb.js +285 -57
- package/dist/connectors/notion-oauth.js +197 -5
- package/dist/connectors/notion.js +289 -51
- package/dist/connectors/openai.js +125 -47
- package/dist/connectors/oracle.js +405 -103
- package/dist/connectors/outlook-oauth.js +170 -5
- package/dist/connectors/powerbi-oauth.js +217 -5
- package/dist/connectors/salesforce.js +350 -49
- package/dist/connectors/semrush.js +280 -49
- package/dist/connectors/sentry.js +255 -50
- package/dist/connectors/shopify-oauth.js +153 -5
- package/dist/connectors/shopify.js +323 -47
- package/dist/connectors/sqlserver.js +381 -102
- package/dist/connectors/stripe-api-key.js +235 -46
- package/dist/connectors/stripe-oauth.js +168 -5
- package/dist/connectors/supabase.js +269 -48
- package/dist/connectors/tableau.js +337 -206
- package/dist/connectors/tiktok-ads.js +245 -48
- package/dist/connectors/wix-store.js +286 -49
- package/dist/connectors/zendesk-oauth.js +205 -5
- package/dist/connectors/zendesk.js +324 -47
- package/dist/index.d.ts +149 -1
- package/dist/index.js +18297 -6886
- package/dist/main.js +12785 -1382
- package/dist/vite-plugin.js +12140 -936
- package/package.json +1 -1
|
@@ -1,51 +1,63 @@
|
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __esm = (fn, res) => function __init() {
|
|
3
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
4
|
+
};
|
|
5
|
+
|
|
1
6
|
// ../connectors/src/parameter-definition.ts
|
|
2
|
-
var ParameterDefinition
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
7
|
+
var ParameterDefinition;
|
|
8
|
+
var init_parameter_definition = __esm({
|
|
9
|
+
"../connectors/src/parameter-definition.ts"() {
|
|
10
|
+
"use strict";
|
|
11
|
+
ParameterDefinition = class {
|
|
12
|
+
slug;
|
|
13
|
+
name;
|
|
14
|
+
description;
|
|
15
|
+
envVarBaseKey;
|
|
16
|
+
type;
|
|
17
|
+
secret;
|
|
18
|
+
required;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.slug = config.slug;
|
|
21
|
+
this.name = config.name;
|
|
22
|
+
this.description = config.description;
|
|
23
|
+
this.envVarBaseKey = config.envVarBaseKey;
|
|
24
|
+
this.type = config.type;
|
|
25
|
+
this.secret = config.secret;
|
|
26
|
+
this.required = config.required;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the parameter value from a ConnectorConnectionObject.
|
|
30
|
+
*/
|
|
31
|
+
getValue(connection2) {
|
|
32
|
+
const param = connection2.parameters.find(
|
|
33
|
+
(p) => p.parameterSlug === this.slug
|
|
34
|
+
);
|
|
35
|
+
if (!param || param.value == null) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return param.value;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Try to get the parameter value. Returns undefined if not found (for optional params).
|
|
44
|
+
*/
|
|
45
|
+
tryGetValue(connection2) {
|
|
46
|
+
const param = connection2.parameters.find(
|
|
47
|
+
(p) => p.parameterSlug === this.slug
|
|
48
|
+
);
|
|
49
|
+
if (!param || param.value == null) return void 0;
|
|
50
|
+
return param.value;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
42
53
|
}
|
|
43
|
-
};
|
|
54
|
+
});
|
|
44
55
|
|
|
45
56
|
// ../connectors/src/connectors/google-analytics/sdk/index.ts
|
|
46
57
|
import * as crypto from "crypto";
|
|
47
58
|
|
|
48
59
|
// ../connectors/src/connectors/google-analytics/parameters.ts
|
|
60
|
+
init_parameter_definition();
|
|
49
61
|
var parameters = {
|
|
50
62
|
serviceAccountKeyJsonBase64: new ParameterDefinition({
|
|
51
63
|
slug: "service-account-key-json-base64",
|
|
@@ -125,7 +137,7 @@ function createClient(params) {
|
|
|
125
137
|
}
|
|
126
138
|
let cachedToken = null;
|
|
127
139
|
let tokenExpiresAt = 0;
|
|
128
|
-
async function
|
|
140
|
+
async function getAccessToken2() {
|
|
129
141
|
const nowSec = Math.floor(Date.now() / 1e3);
|
|
130
142
|
if (cachedToken && nowSec < tokenExpiresAt - 60) {
|
|
131
143
|
return cachedToken;
|
|
@@ -156,7 +168,7 @@ function createClient(params) {
|
|
|
156
168
|
}
|
|
157
169
|
return {
|
|
158
170
|
async request(path2, init) {
|
|
159
|
-
const accessToken = await
|
|
171
|
+
const accessToken = await getAccessToken2();
|
|
160
172
|
const resolvedPath = path2.replace(/\{propertyId\}/g, propertyId);
|
|
161
173
|
const url = `${BASE_URL.replace(/\/+$/, "")}/${resolvedPath.replace(/^\/+/, "")}`;
|
|
162
174
|
const headers = new Headers(init?.headers);
|
|
@@ -288,6 +300,20 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
288
300
|
* `runSetupFlow` from `setup-flow.ts`.
|
|
289
301
|
*/
|
|
290
302
|
setup;
|
|
303
|
+
/**
|
|
304
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
305
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
306
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
307
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
308
|
+
*
|
|
309
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
310
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
311
|
+
* connectionId, which doesn't exist until the row is saved
|
|
312
|
+
*
|
|
313
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
314
|
+
* the default verify-on-create behavior without opt-in.
|
|
315
|
+
*/
|
|
316
|
+
skipConnectionCheckOnCreate;
|
|
291
317
|
constructor(config) {
|
|
292
318
|
this.slug = config.slug;
|
|
293
319
|
this.authType = config.authType;
|
|
@@ -305,6 +331,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
305
331
|
this.query = config.query;
|
|
306
332
|
this.checkConnection = config.checkConnection;
|
|
307
333
|
this.setup = config.setup;
|
|
334
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
308
335
|
}
|
|
309
336
|
get connectorKey() {
|
|
310
337
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -369,6 +396,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
369
396
|
}
|
|
370
397
|
};
|
|
371
398
|
|
|
399
|
+
// ../connectors/src/setup-flow.ts
|
|
400
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
401
|
+
const runtime = {
|
|
402
|
+
params,
|
|
403
|
+
language: ctx.language,
|
|
404
|
+
config
|
|
405
|
+
};
|
|
406
|
+
let state = flow.initialState();
|
|
407
|
+
let answerIdx = 0;
|
|
408
|
+
for (const step of flow.steps) {
|
|
409
|
+
const ans = ctx.answers[answerIdx];
|
|
410
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
411
|
+
state = step.applyAnswer(state, ans.answer);
|
|
412
|
+
answerIdx += 1;
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
if (step.type === "text") {
|
|
416
|
+
return {
|
|
417
|
+
type: "nextQuestion",
|
|
418
|
+
questionSlug: step.slug,
|
|
419
|
+
question: step.question[ctx.language],
|
|
420
|
+
questionType: "text"
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
424
|
+
if (options.length === 0) {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
type: "nextQuestion",
|
|
429
|
+
questionSlug: step.slug,
|
|
430
|
+
question: step.question[ctx.language],
|
|
431
|
+
questionType: step.type,
|
|
432
|
+
options
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
436
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
437
|
+
}
|
|
438
|
+
async function resolveSetupSelection(params) {
|
|
439
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
440
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
441
|
+
return resolved.slice(0, limit);
|
|
442
|
+
}
|
|
443
|
+
|
|
372
444
|
// ../connectors/src/auth-types.ts
|
|
373
445
|
var AUTH_TYPES = {
|
|
374
446
|
OAUTH: "oauth",
|
|
@@ -389,6 +461,219 @@ var googleAnalyticsOnboarding = new ConnectorOnboarding({
|
|
|
389
461
|
}
|
|
390
462
|
});
|
|
391
463
|
|
|
464
|
+
// ../connectors/src/connectors/google-analytics/utils.ts
|
|
465
|
+
import * as crypto2 from "crypto";
|
|
466
|
+
var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
|
|
467
|
+
var ADMIN_BASE_URL = "https://analyticsadmin.googleapis.com/v1beta";
|
|
468
|
+
var SCOPE2 = "https://www.googleapis.com/auth/analytics.readonly";
|
|
469
|
+
function base64url2(input) {
|
|
470
|
+
const buf = typeof input === "string" ? Buffer.from(input) : input;
|
|
471
|
+
return buf.toString("base64url");
|
|
472
|
+
}
|
|
473
|
+
function buildJwt2(clientEmail, privateKey, nowSec) {
|
|
474
|
+
const header = base64url2(JSON.stringify({ alg: "RS256", typ: "JWT" }));
|
|
475
|
+
const payload = base64url2(
|
|
476
|
+
JSON.stringify({
|
|
477
|
+
iss: clientEmail,
|
|
478
|
+
scope: SCOPE2,
|
|
479
|
+
aud: TOKEN_URL2,
|
|
480
|
+
iat: nowSec,
|
|
481
|
+
exp: nowSec + 3600
|
|
482
|
+
})
|
|
483
|
+
);
|
|
484
|
+
const signingInput = `${header}.${payload}`;
|
|
485
|
+
const sign = crypto2.createSign("RSA-SHA256");
|
|
486
|
+
sign.update(signingInput);
|
|
487
|
+
sign.end();
|
|
488
|
+
const signature = base64url2(sign.sign(privateKey));
|
|
489
|
+
return `${signingInput}.${signature}`;
|
|
490
|
+
}
|
|
491
|
+
function decodeServiceAccount(serviceAccountKeyJsonBase64) {
|
|
492
|
+
let serviceAccountKey;
|
|
493
|
+
try {
|
|
494
|
+
const decoded = Buffer.from(
|
|
495
|
+
serviceAccountKeyJsonBase64,
|
|
496
|
+
"base64"
|
|
497
|
+
).toString("utf-8");
|
|
498
|
+
serviceAccountKey = JSON.parse(decoded);
|
|
499
|
+
} catch {
|
|
500
|
+
throw new Error(
|
|
501
|
+
"google-analytics: failed to decode service account key JSON from base64"
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
if (!serviceAccountKey.client_email || !serviceAccountKey.private_key) {
|
|
505
|
+
throw new Error(
|
|
506
|
+
"google-analytics: service account key JSON must contain client_email and private_key"
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
return serviceAccountKey;
|
|
510
|
+
}
|
|
511
|
+
async function getAccessToken(serviceAccountKey) {
|
|
512
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
513
|
+
const jwt = buildJwt2(
|
|
514
|
+
serviceAccountKey.client_email,
|
|
515
|
+
serviceAccountKey.private_key,
|
|
516
|
+
nowSec
|
|
517
|
+
);
|
|
518
|
+
const response = await fetch(TOKEN_URL2, {
|
|
519
|
+
method: "POST",
|
|
520
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
521
|
+
body: new URLSearchParams({
|
|
522
|
+
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
523
|
+
assertion: jwt
|
|
524
|
+
})
|
|
525
|
+
});
|
|
526
|
+
if (!response.ok) {
|
|
527
|
+
const text = await response.text();
|
|
528
|
+
throw new Error(
|
|
529
|
+
`google-analytics: token exchange failed (${response.status}): ${text}`
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
const data = await response.json();
|
|
533
|
+
return data.access_token;
|
|
534
|
+
}
|
|
535
|
+
async function adminFetch(params, path2, init) {
|
|
536
|
+
const keyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
|
|
537
|
+
if (!keyJsonBase64) {
|
|
538
|
+
throw new Error(
|
|
539
|
+
"google-analytics: missing required parameter: service-account-key-json-base64"
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
const serviceAccountKey = decodeServiceAccount(keyJsonBase64);
|
|
543
|
+
const accessToken = await getAccessToken(serviceAccountKey);
|
|
544
|
+
const url = `${ADMIN_BASE_URL}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
545
|
+
const headers = new Headers(init?.headers);
|
|
546
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
547
|
+
return fetch(url, { ...init, headers });
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// ../connectors/src/connectors/google-analytics/setup-flow.ts
|
|
551
|
+
var ALL_PROPERTIES = "__ALL_PROPERTIES__";
|
|
552
|
+
var GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES = 20;
|
|
553
|
+
async function listAccountSummaries(params) {
|
|
554
|
+
const summaries = [];
|
|
555
|
+
let pageToken;
|
|
556
|
+
do {
|
|
557
|
+
const path2 = pageToken ? `/accountSummaries?pageToken=${encodeURIComponent(pageToken)}` : `/accountSummaries`;
|
|
558
|
+
const res = await adminFetch(params, path2);
|
|
559
|
+
if (!res.ok) {
|
|
560
|
+
const body = await res.text().catch(() => res.statusText);
|
|
561
|
+
throw new Error(
|
|
562
|
+
`google-analytics: accountSummaries failed (${res.status}): ${body}`
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
const data = await res.json();
|
|
566
|
+
summaries.push(...data.accountSummaries ?? []);
|
|
567
|
+
pageToken = data.nextPageToken;
|
|
568
|
+
} while (pageToken);
|
|
569
|
+
return summaries;
|
|
570
|
+
}
|
|
571
|
+
function propertyIdFromResource(resource) {
|
|
572
|
+
return (resource ?? "").replace(/^properties\//, "");
|
|
573
|
+
}
|
|
574
|
+
async function getProperty(params, propertyId) {
|
|
575
|
+
const res = await adminFetch(params, `/properties/${propertyId}`);
|
|
576
|
+
if (!res.ok) return null;
|
|
577
|
+
return await res.json();
|
|
578
|
+
}
|
|
579
|
+
var googleAnalyticsSetupFlow = {
|
|
580
|
+
initialState: () => ({}),
|
|
581
|
+
steps: [
|
|
582
|
+
{
|
|
583
|
+
slug: "account",
|
|
584
|
+
type: "select",
|
|
585
|
+
question: {
|
|
586
|
+
ja: "Google Analytics \u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044",
|
|
587
|
+
en: "Select a Google Analytics account"
|
|
588
|
+
},
|
|
589
|
+
async fetchOptions(_state, rt) {
|
|
590
|
+
const summaries = await listAccountSummaries(rt.params);
|
|
591
|
+
return summaries.map((s) => {
|
|
592
|
+
const accountResource = s.account ?? s.name ?? "";
|
|
593
|
+
if (!accountResource) return null;
|
|
594
|
+
return {
|
|
595
|
+
value: accountResource,
|
|
596
|
+
label: s.displayName ?? accountResource
|
|
597
|
+
};
|
|
598
|
+
}).filter((v) => v != null);
|
|
599
|
+
},
|
|
600
|
+
applyAnswer: (state, answer) => ({ ...state, account: answer[0] })
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
slug: "properties",
|
|
604
|
+
type: "multiSelect",
|
|
605
|
+
question: {
|
|
606
|
+
ja: "\u5BFE\u8C61\u306E GA4 \u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
607
|
+
en: "Select target GA4 properties (multi-select allowed)"
|
|
608
|
+
},
|
|
609
|
+
async fetchOptions(state, rt) {
|
|
610
|
+
if (!state.account) return [];
|
|
611
|
+
const summaries = await listAccountSummaries(rt.params);
|
|
612
|
+
const account = summaries.find(
|
|
613
|
+
(s) => (s.account ?? s.name) === state.account
|
|
614
|
+
);
|
|
615
|
+
const props = (account?.propertySummaries ?? []).map((p) => {
|
|
616
|
+
const id = propertyIdFromResource(p.property);
|
|
617
|
+
if (!id) return null;
|
|
618
|
+
return {
|
|
619
|
+
value: id,
|
|
620
|
+
label: p.displayName ? `${p.displayName} (${id})` : id
|
|
621
|
+
};
|
|
622
|
+
}).filter((v) => v != null);
|
|
623
|
+
if (props.length === 0) return [];
|
|
624
|
+
return [
|
|
625
|
+
{
|
|
626
|
+
value: ALL_PROPERTIES,
|
|
627
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D7\u30ED\u30D1\u30C6\u30A3" : "All properties"
|
|
628
|
+
},
|
|
629
|
+
...props
|
|
630
|
+
];
|
|
631
|
+
},
|
|
632
|
+
applyAnswer: (state, answer) => ({ ...state, properties: answer })
|
|
633
|
+
}
|
|
634
|
+
],
|
|
635
|
+
async finalize(state, rt) {
|
|
636
|
+
if (!state.account || !state.properties) {
|
|
637
|
+
throw new Error("Google Analytics setup: incomplete state on finalize");
|
|
638
|
+
}
|
|
639
|
+
const summaries = await listAccountSummaries(rt.params);
|
|
640
|
+
const account = summaries.find(
|
|
641
|
+
(s) => (s.account ?? s.name) === state.account
|
|
642
|
+
);
|
|
643
|
+
const accountLabel = account?.displayName ?? state.account.replace(/^accounts\//, "");
|
|
644
|
+
const targetPropertyIds = await resolveSetupSelection({
|
|
645
|
+
selected: state.properties,
|
|
646
|
+
allSentinel: ALL_PROPERTIES,
|
|
647
|
+
fetchAll: async () => (account?.propertySummaries ?? []).map((p) => propertyIdFromResource(p.property)).filter((v) => v),
|
|
648
|
+
limit: GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES
|
|
649
|
+
});
|
|
650
|
+
const sections = [
|
|
651
|
+
"## Google Analytics",
|
|
652
|
+
"",
|
|
653
|
+
`### Account: ${accountLabel}`,
|
|
654
|
+
""
|
|
655
|
+
];
|
|
656
|
+
if (targetPropertyIds.length === 0) {
|
|
657
|
+
sections.push("_No properties selected._", "");
|
|
658
|
+
return sections.join("\n");
|
|
659
|
+
}
|
|
660
|
+
sections.push("| Property ID | Display Name | Time Zone | Currency | Industry |");
|
|
661
|
+
sections.push("|-------------|--------------|-----------|----------|----------|");
|
|
662
|
+
for (const propertyId of targetPropertyIds) {
|
|
663
|
+
const prop = await getProperty(rt.params, propertyId);
|
|
664
|
+
const displayName = (prop?.displayName ?? "-").replace(/\|/g, "\\|");
|
|
665
|
+
const timeZone = prop?.timeZone ?? "-";
|
|
666
|
+
const currency = prop?.currencyCode ?? "-";
|
|
667
|
+
const industry = (prop?.industryCategory ?? "-").replace(/\|/g, "\\|");
|
|
668
|
+
sections.push(
|
|
669
|
+
`| ${propertyId} | ${displayName} | ${timeZone} | ${currency} | ${industry} |`
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
sections.push("");
|
|
673
|
+
return sections.join("\n");
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
|
|
392
677
|
// ../connectors/src/connectors/google-analytics/tools/request.ts
|
|
393
678
|
import { z } from "zod";
|
|
394
679
|
var BASE_URL2 = "https://analyticsdata.googleapis.com/v1beta/";
|
|
@@ -622,7 +907,31 @@ activeUsers, sessions, screenPageViews, bounceRate, averageSessionDuration, conv
|
|
|
622
907
|
- \u7D76\u5BFE\u5024: \`"2024-01-01"\`
|
|
623
908
|
- \u76F8\u5BFE\u5024: \`"today"\`, \`"yesterday"\`, \`"7daysAgo"\`, \`"30daysAgo"\``
|
|
624
909
|
},
|
|
625
|
-
tools
|
|
910
|
+
tools,
|
|
911
|
+
setup: (params, ctx, config) => runSetupFlow(googleAnalyticsSetupFlow, params, ctx, config),
|
|
912
|
+
async checkConnection(params, _config) {
|
|
913
|
+
const keyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
|
|
914
|
+
if (!keyJsonBase64) {
|
|
915
|
+
return {
|
|
916
|
+
success: false,
|
|
917
|
+
error: "google-analytics: missing service account key"
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
try {
|
|
921
|
+
const res = await adminFetch(params, "/accountSummaries?pageSize=1");
|
|
922
|
+
if (!res.ok) {
|
|
923
|
+
const body = await res.text().catch(() => res.statusText);
|
|
924
|
+
return {
|
|
925
|
+
success: false,
|
|
926
|
+
error: `google-analytics: accountSummaries failed (${res.status}): ${body}`
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
return { success: true };
|
|
930
|
+
} catch (err) {
|
|
931
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
932
|
+
return { success: false, error: msg };
|
|
933
|
+
}
|
|
934
|
+
}
|
|
626
935
|
});
|
|
627
936
|
|
|
628
937
|
// src/connectors/create-connector-sdk.ts
|
|
@@ -651,6 +960,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
651
960
|
import { getContext } from "hono/context-storage";
|
|
652
961
|
import { getCookie } from "hono/cookie";
|
|
653
962
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
963
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
654
964
|
function normalizeHeaders(input) {
|
|
655
965
|
const out = {};
|
|
656
966
|
if (!input) return out;
|
|
@@ -659,6 +969,11 @@ function normalizeHeaders(input) {
|
|
|
659
969
|
});
|
|
660
970
|
return out;
|
|
661
971
|
}
|
|
972
|
+
function extractInputUrl(input) {
|
|
973
|
+
if (typeof input === "string") return input;
|
|
974
|
+
if (input instanceof URL) return input.href;
|
|
975
|
+
return input.url;
|
|
976
|
+
}
|
|
662
977
|
function createSandboxProxyFetch(connectionId) {
|
|
663
978
|
return async (input, init) => {
|
|
664
979
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -668,10 +983,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
668
983
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
669
984
|
);
|
|
670
985
|
}
|
|
671
|
-
const originalUrl =
|
|
986
|
+
const originalUrl = extractInputUrl(input);
|
|
987
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
988
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
989
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
990
|
+
return fetch(sessionUrl, {
|
|
991
|
+
method: "POST",
|
|
992
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
993
|
+
});
|
|
994
|
+
}
|
|
672
995
|
const originalMethod = init?.method ?? "GET";
|
|
673
996
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
674
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
675
997
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
676
998
|
return fetch(proxyUrl, {
|
|
677
999
|
method: "POST",
|
|
@@ -697,10 +1019,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
697
1019
|
}
|
|
698
1020
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
699
1021
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1022
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
700
1023
|
return async (input, init) => {
|
|
701
|
-
const originalUrl =
|
|
702
|
-
const originalMethod = init?.method ?? "GET";
|
|
703
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
1024
|
+
const originalUrl = extractInputUrl(input);
|
|
704
1025
|
const c = getContext();
|
|
705
1026
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
706
1027
|
if (!appSession) {
|
|
@@ -708,6 +1029,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
708
1029
|
"No authentication method available for connection proxy."
|
|
709
1030
|
);
|
|
710
1031
|
}
|
|
1032
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1033
|
+
return fetch(sessionUrl, {
|
|
1034
|
+
method: "POST",
|
|
1035
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
const originalMethod = init?.method ?? "GET";
|
|
1039
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
711
1040
|
return fetch(proxyUrl, {
|
|
712
1041
|
method: "POST",
|
|
713
1042
|
headers: {
|