@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
|
@@ -1,48 +1,60 @@
|
|
|
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/salesforce/parameters.ts
|
|
57
|
+
init_parameter_definition();
|
|
46
58
|
var parameters = {
|
|
47
59
|
instanceUrl: new ParameterDefinition({
|
|
48
60
|
slug: "instance-url",
|
|
@@ -272,6 +284,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
272
284
|
tools;
|
|
273
285
|
query;
|
|
274
286
|
checkConnection;
|
|
287
|
+
/**
|
|
288
|
+
* SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
|
|
289
|
+
* implement this expose a step-by-step exploration flow (database/schema/
|
|
290
|
+
* table/etc. discovery) that the dashboard backend drives via the
|
|
291
|
+
* `/connections/:connectionId/setup` endpoint. Implement by delegating to
|
|
292
|
+
* `runSetupFlow` from `setup-flow.ts`.
|
|
293
|
+
*/
|
|
294
|
+
setup;
|
|
295
|
+
/**
|
|
296
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
297
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
298
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
299
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
300
|
+
*
|
|
301
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
302
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
303
|
+
* connectionId, which doesn't exist until the row is saved
|
|
304
|
+
*
|
|
305
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
306
|
+
* the default verify-on-create behavior without opt-in.
|
|
307
|
+
*/
|
|
308
|
+
skipConnectionCheckOnCreate;
|
|
275
309
|
constructor(config) {
|
|
276
310
|
this.slug = config.slug;
|
|
277
311
|
this.authType = config.authType;
|
|
@@ -288,6 +322,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
288
322
|
this.tools = config.tools;
|
|
289
323
|
this.query = config.query;
|
|
290
324
|
this.checkConnection = config.checkConnection;
|
|
325
|
+
this.setup = config.setup;
|
|
326
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
291
327
|
}
|
|
292
328
|
get connectorKey() {
|
|
293
329
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -352,6 +388,76 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
352
388
|
}
|
|
353
389
|
};
|
|
354
390
|
|
|
391
|
+
// ../connectors/src/setup-flow.ts
|
|
392
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
393
|
+
const runtime = {
|
|
394
|
+
params,
|
|
395
|
+
language: ctx.language,
|
|
396
|
+
config
|
|
397
|
+
};
|
|
398
|
+
let state = flow.initialState();
|
|
399
|
+
let answerIdx = 0;
|
|
400
|
+
const pendingParameterUpdates = [];
|
|
401
|
+
for (const step of flow.steps) {
|
|
402
|
+
const ans = ctx.answers[answerIdx];
|
|
403
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
404
|
+
state = step.applyAnswer(state, ans.answer);
|
|
405
|
+
if (step.toParameterUpdates) {
|
|
406
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
407
|
+
}
|
|
408
|
+
answerIdx += 1;
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
412
|
+
if (step.type === "text") {
|
|
413
|
+
if (step.fetchOptions) {
|
|
414
|
+
const options2 = await step.fetchOptions(state, runtime);
|
|
415
|
+
if (options2.length === 0) {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return {
|
|
420
|
+
type: "nextQuestion",
|
|
421
|
+
questionSlug: step.slug,
|
|
422
|
+
question: step.question[ctx.language],
|
|
423
|
+
questionType: "text",
|
|
424
|
+
allowFreeText: resolvedAllowFreeText,
|
|
425
|
+
...pendingParameterUpdates.length > 0 && {
|
|
426
|
+
parameterUpdates: pendingParameterUpdates
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
431
|
+
if (options.length === 0) {
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
type: "nextQuestion",
|
|
436
|
+
questionSlug: step.slug,
|
|
437
|
+
question: step.question[ctx.language],
|
|
438
|
+
questionType: step.type,
|
|
439
|
+
options,
|
|
440
|
+
allowFreeText: resolvedAllowFreeText,
|
|
441
|
+
...pendingParameterUpdates.length > 0 && {
|
|
442
|
+
parameterUpdates: pendingParameterUpdates
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
447
|
+
return {
|
|
448
|
+
type: "fulfilled",
|
|
449
|
+
dataInvestigationResult,
|
|
450
|
+
...pendingParameterUpdates.length > 0 && {
|
|
451
|
+
parameterUpdates: pendingParameterUpdates
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
async function resolveSetupSelection(params) {
|
|
456
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
457
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
458
|
+
return resolved.slice(0, limit);
|
|
459
|
+
}
|
|
460
|
+
|
|
355
461
|
// ../connectors/src/auth-types.ts
|
|
356
462
|
var AUTH_TYPES = {
|
|
357
463
|
OAUTH: "oauth",
|
|
@@ -374,6 +480,151 @@ var salesforceOnboarding = new ConnectorOnboarding({
|
|
|
374
480
|
}
|
|
375
481
|
});
|
|
376
482
|
|
|
483
|
+
// ../connectors/src/connectors/salesforce/utils.ts
|
|
484
|
+
var API_VERSION = "v60.0";
|
|
485
|
+
function normalizeInstanceUrl2(raw) {
|
|
486
|
+
const trimmed = raw.trim().replace(/\/+$/, "");
|
|
487
|
+
if (!/^https?:\/\//i.test(trimmed)) {
|
|
488
|
+
return `https://${trimmed}`;
|
|
489
|
+
}
|
|
490
|
+
return trimmed;
|
|
491
|
+
}
|
|
492
|
+
async function getAccessToken(params) {
|
|
493
|
+
const instanceUrlParam = params[parameters.instanceUrl.slug];
|
|
494
|
+
const clientId = params[parameters.clientId.slug];
|
|
495
|
+
const clientSecret = params[parameters.clientSecret.slug];
|
|
496
|
+
if (!instanceUrlParam || !clientId || !clientSecret) {
|
|
497
|
+
throw new Error(
|
|
498
|
+
"salesforce: missing required parameter(s) (instance-url, client-id, client-secret)"
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
const instanceUrl = normalizeInstanceUrl2(instanceUrlParam);
|
|
502
|
+
const tokenBody = new URLSearchParams({
|
|
503
|
+
grant_type: "client_credentials",
|
|
504
|
+
client_id: clientId,
|
|
505
|
+
client_secret: clientSecret
|
|
506
|
+
});
|
|
507
|
+
const tokenRes = await fetch(`${instanceUrl}/services/oauth2/token`, {
|
|
508
|
+
method: "POST",
|
|
509
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
510
|
+
body: tokenBody.toString()
|
|
511
|
+
});
|
|
512
|
+
if (!tokenRes.ok) {
|
|
513
|
+
const errText = await tokenRes.text().catch(() => tokenRes.statusText);
|
|
514
|
+
throw new Error(
|
|
515
|
+
`salesforce: token request failed (${tokenRes.status}): ${errText}`
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
const tokenJson = await tokenRes.json();
|
|
519
|
+
if (!tokenJson.access_token) {
|
|
520
|
+
throw new Error("salesforce: access_token missing in token response");
|
|
521
|
+
}
|
|
522
|
+
return {
|
|
523
|
+
accessToken: tokenJson.access_token,
|
|
524
|
+
instanceUrl: tokenJson.instance_url ?? instanceUrl
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
async function apiFetch(params, path2, init) {
|
|
528
|
+
const token = await getAccessToken(params);
|
|
529
|
+
const url = `${token.instanceUrl}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
530
|
+
const headers = new Headers(init?.headers);
|
|
531
|
+
headers.set("Authorization", `Bearer ${token.accessToken}`);
|
|
532
|
+
headers.set("Accept", "application/json");
|
|
533
|
+
return fetch(url, { ...init, headers });
|
|
534
|
+
}
|
|
535
|
+
var SALESFORCE_API_VERSION = API_VERSION;
|
|
536
|
+
|
|
537
|
+
// ../connectors/src/connectors/salesforce/setup-flow.ts
|
|
538
|
+
var SALESFORCE_SETUP_MAX_OBJECTS = 10;
|
|
539
|
+
var SALESFORCE_SETUP_MAX_FIELDS = 50;
|
|
540
|
+
async function listSObjects(params) {
|
|
541
|
+
const res = await apiFetch(
|
|
542
|
+
params,
|
|
543
|
+
`/services/data/${SALESFORCE_API_VERSION}/sobjects/`
|
|
544
|
+
);
|
|
545
|
+
if (!res.ok) {
|
|
546
|
+
const body = await res.text().catch(() => res.statusText);
|
|
547
|
+
throw new Error(
|
|
548
|
+
`salesforce: listSObjects failed (${res.status}): ${body}`
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
const data = await res.json();
|
|
552
|
+
return data.sobjects ?? [];
|
|
553
|
+
}
|
|
554
|
+
async function describeSObject(params, name) {
|
|
555
|
+
const res = await apiFetch(
|
|
556
|
+
params,
|
|
557
|
+
`/services/data/${SALESFORCE_API_VERSION}/sobjects/${name}/describe`
|
|
558
|
+
);
|
|
559
|
+
if (!res.ok) {
|
|
560
|
+
const body = await res.text().catch(() => res.statusText);
|
|
561
|
+
throw new Error(
|
|
562
|
+
`salesforce: describeSObject(${name}) failed (${res.status}): ${body}`
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
const data = await res.json();
|
|
566
|
+
return data.fields ?? [];
|
|
567
|
+
}
|
|
568
|
+
var salesforceSetupFlow = {
|
|
569
|
+
initialState: () => ({}),
|
|
570
|
+
steps: [
|
|
571
|
+
{
|
|
572
|
+
slug: "objects",
|
|
573
|
+
type: "multiSelect",
|
|
574
|
+
question: {
|
|
575
|
+
ja: "\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3067\u6982\u89B3\u3057\u305F\u3044Salesforce\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
576
|
+
en: "Select the Salesforce sObjects to include in setup (multi-select allowed)"
|
|
577
|
+
},
|
|
578
|
+
async fetchOptions(_state, rt) {
|
|
579
|
+
const all = await listSObjects(rt.params);
|
|
580
|
+
return all.filter((s) => s.queryable !== false && s.name).map((s) => ({
|
|
581
|
+
value: String(s.name),
|
|
582
|
+
label: s.label ? `${s.label} (${s.name})` : String(s.name)
|
|
583
|
+
}));
|
|
584
|
+
},
|
|
585
|
+
applyAnswer: (state, answer) => ({ ...state, objects: answer })
|
|
586
|
+
}
|
|
587
|
+
],
|
|
588
|
+
async finalize(state, rt) {
|
|
589
|
+
if (!state.objects) {
|
|
590
|
+
throw new Error("Salesforce setup: incomplete state on finalize");
|
|
591
|
+
}
|
|
592
|
+
const targetObjects = await resolveSetupSelection({
|
|
593
|
+
selected: state.objects,
|
|
594
|
+
allSentinel: "__ALL__",
|
|
595
|
+
fetchAll: async () => {
|
|
596
|
+
const all = await listSObjects(rt.params);
|
|
597
|
+
return all.filter((s) => s.queryable !== false && s.name).map((s) => String(s.name));
|
|
598
|
+
},
|
|
599
|
+
limit: SALESFORCE_SETUP_MAX_OBJECTS
|
|
600
|
+
});
|
|
601
|
+
const sections = ["## Salesforce", ""];
|
|
602
|
+
for (const objectName of targetObjects) {
|
|
603
|
+
sections.push(`### sObject: ${objectName}`, "");
|
|
604
|
+
const fields = await describeSObject(rt.params, objectName);
|
|
605
|
+
const limited = fields.slice(0, SALESFORCE_SETUP_MAX_FIELDS);
|
|
606
|
+
sections.push("| Field | Type | Label | Custom |");
|
|
607
|
+
sections.push("|-------|------|-------|--------|");
|
|
608
|
+
for (const f of limited) {
|
|
609
|
+
const name = (f.name ?? "").replace(/\|/g, "\\|");
|
|
610
|
+
const type = (f.type ?? "").replace(/\|/g, "\\|");
|
|
611
|
+
const label = (f.label ?? "").replace(/\|/g, "\\|");
|
|
612
|
+
sections.push(
|
|
613
|
+
`| ${name} | ${type} | ${label || "-"} | ${f.custom ? "yes" : "no"} |`
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
if (fields.length > SALESFORCE_SETUP_MAX_FIELDS) {
|
|
617
|
+
sections.push(
|
|
618
|
+
"",
|
|
619
|
+
`_Showing first ${SALESFORCE_SETUP_MAX_FIELDS} of ${fields.length} fields._`
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
sections.push("");
|
|
623
|
+
}
|
|
624
|
+
return sections.join("\n");
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
|
|
377
628
|
// ../connectors/src/connectors/salesforce/tools/request.ts
|
|
378
629
|
import { z } from "zod";
|
|
379
630
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
@@ -401,7 +652,7 @@ var outputSchema = z.discriminatedUnion("success", [
|
|
|
401
652
|
error: z.string()
|
|
402
653
|
})
|
|
403
654
|
]);
|
|
404
|
-
function
|
|
655
|
+
function normalizeInstanceUrl3(raw) {
|
|
405
656
|
const trimmed = raw.trim().replace(/\/+$/, "");
|
|
406
657
|
if (!/^https?:\/\//i.test(trimmed)) {
|
|
407
658
|
return `https://${trimmed}`;
|
|
@@ -431,7 +682,7 @@ Prefer SOQL via the /query endpoint for filtered, joined, or aggregated reads ra
|
|
|
431
682
|
const instanceUrlParam = parameters.instanceUrl.getValue(connection2);
|
|
432
683
|
const clientId = parameters.clientId.getValue(connection2);
|
|
433
684
|
const clientSecret = parameters.clientSecret.getValue(connection2);
|
|
434
|
-
const instanceUrl =
|
|
685
|
+
const instanceUrl = normalizeInstanceUrl3(instanceUrlParam);
|
|
435
686
|
const tokenBody = new URLSearchParams({
|
|
436
687
|
grant_type: "client_credentials",
|
|
437
688
|
client_id: clientId,
|
|
@@ -660,7 +911,71 @@ export default async function handler(c: Context) {
|
|
|
660
911
|
- \u96C6\u8A08: \`GROUP BY\`, \`HAVING\`, \`COUNT()\`, \`SUM()\`, \`AVG()\`, \`MIN()\`, \`MAX()\`
|
|
661
912
|
- \u89AA\u2192\u5B50\u30B5\u30D6\u30AF\u30A8\u30EA: \`SELECT Id, Name, (SELECT Id, Email FROM Contacts) FROM Account\``
|
|
662
913
|
},
|
|
663
|
-
tools
|
|
914
|
+
tools,
|
|
915
|
+
setup: (params, ctx, config) => runSetupFlow(salesforceSetupFlow, params, ctx, config),
|
|
916
|
+
async checkConnection(params, _config) {
|
|
917
|
+
const instanceUrlParam = params[parameters.instanceUrl.slug];
|
|
918
|
+
const clientId = params[parameters.clientId.slug];
|
|
919
|
+
const clientSecret = params[parameters.clientSecret.slug];
|
|
920
|
+
if (!instanceUrlParam || !clientId || !clientSecret) {
|
|
921
|
+
return {
|
|
922
|
+
success: false,
|
|
923
|
+
error: `Missing required parameter(s): ${parameters.instanceUrl.slug}, ${parameters.clientId.slug}, ${parameters.clientSecret.slug}`
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
const trimmed = instanceUrlParam.trim().replace(/\/+$/, "");
|
|
927
|
+
const instanceUrl = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
928
|
+
try {
|
|
929
|
+
const tokenBody = new URLSearchParams({
|
|
930
|
+
grant_type: "client_credentials",
|
|
931
|
+
client_id: clientId,
|
|
932
|
+
client_secret: clientSecret
|
|
933
|
+
});
|
|
934
|
+
const tokenRes = await fetch(`${instanceUrl}/services/oauth2/token`, {
|
|
935
|
+
method: "POST",
|
|
936
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
937
|
+
body: tokenBody.toString()
|
|
938
|
+
});
|
|
939
|
+
if (!tokenRes.ok) {
|
|
940
|
+
const errText = await tokenRes.text().catch(() => tokenRes.statusText);
|
|
941
|
+
return {
|
|
942
|
+
success: false,
|
|
943
|
+
error: `Salesforce token request failed: HTTP ${tokenRes.status} ${errText}`
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
const tokenJson = await tokenRes.json();
|
|
947
|
+
if (!tokenJson.access_token) {
|
|
948
|
+
return {
|
|
949
|
+
success: false,
|
|
950
|
+
error: "Salesforce: access_token missing in token response"
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
const resolvedInstanceUrl = tokenJson.instance_url ?? instanceUrl;
|
|
954
|
+
const res = await fetch(
|
|
955
|
+
`${resolvedInstanceUrl}/services/data/v60.0/limits`,
|
|
956
|
+
{
|
|
957
|
+
method: "GET",
|
|
958
|
+
headers: {
|
|
959
|
+
Authorization: `Bearer ${tokenJson.access_token}`,
|
|
960
|
+
Accept: "application/json"
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
);
|
|
964
|
+
if (!res.ok) {
|
|
965
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
966
|
+
return {
|
|
967
|
+
success: false,
|
|
968
|
+
error: `Salesforce API failed: HTTP ${res.status} ${errText}`
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
return { success: true };
|
|
972
|
+
} catch (error) {
|
|
973
|
+
return {
|
|
974
|
+
success: false,
|
|
975
|
+
error: error instanceof Error ? error.message : String(error)
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
}
|
|
664
979
|
});
|
|
665
980
|
|
|
666
981
|
// src/connectors/create-connector-sdk.ts
|
|
@@ -689,6 +1004,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
689
1004
|
import { getContext } from "hono/context-storage";
|
|
690
1005
|
import { getCookie } from "hono/cookie";
|
|
691
1006
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
1007
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
692
1008
|
function normalizeHeaders(input) {
|
|
693
1009
|
const out = {};
|
|
694
1010
|
if (!input) return out;
|
|
@@ -697,6 +1013,11 @@ function normalizeHeaders(input) {
|
|
|
697
1013
|
});
|
|
698
1014
|
return out;
|
|
699
1015
|
}
|
|
1016
|
+
function extractInputUrl(input) {
|
|
1017
|
+
if (typeof input === "string") return input;
|
|
1018
|
+
if (input instanceof URL) return input.href;
|
|
1019
|
+
return input.url;
|
|
1020
|
+
}
|
|
700
1021
|
function createSandboxProxyFetch(connectionId) {
|
|
701
1022
|
return async (input, init) => {
|
|
702
1023
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -706,10 +1027,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
706
1027
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
707
1028
|
);
|
|
708
1029
|
}
|
|
709
|
-
const originalUrl =
|
|
1030
|
+
const originalUrl = extractInputUrl(input);
|
|
1031
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
1032
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1033
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
1034
|
+
return fetch(sessionUrl, {
|
|
1035
|
+
method: "POST",
|
|
1036
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
710
1039
|
const originalMethod = init?.method ?? "GET";
|
|
711
1040
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
712
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
713
1041
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
714
1042
|
return fetch(proxyUrl, {
|
|
715
1043
|
method: "POST",
|
|
@@ -735,10 +1063,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
735
1063
|
}
|
|
736
1064
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
737
1065
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1066
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
738
1067
|
return async (input, init) => {
|
|
739
|
-
const originalUrl =
|
|
740
|
-
const originalMethod = init?.method ?? "GET";
|
|
741
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
1068
|
+
const originalUrl = extractInputUrl(input);
|
|
742
1069
|
const c = getContext();
|
|
743
1070
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
744
1071
|
if (!appSession) {
|
|
@@ -746,6 +1073,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
746
1073
|
"No authentication method available for connection proxy."
|
|
747
1074
|
);
|
|
748
1075
|
}
|
|
1076
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1077
|
+
return fetch(sessionUrl, {
|
|
1078
|
+
method: "POST",
|
|
1079
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
const originalMethod = init?.method ?? "GET";
|
|
1083
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
749
1084
|
return fetch(proxyUrl, {
|
|
750
1085
|
method: "POST",
|
|
751
1086
|
headers: {
|