@squadbase/vite-server 0.1.17-dev.a9ddcfa → 0.1.17-dev.e4768d1
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 +8941 -3700
- package/dist/connectors/airtable-oauth.js +87 -23
- package/dist/connectors/airtable.js +130 -23
- package/dist/connectors/amplitude.js +47 -23
- package/dist/connectors/anthropic.js +41 -17
- package/dist/connectors/asana.js +76 -25
- package/dist/connectors/attio.js +69 -28
- package/dist/connectors/aws-billing.js +47 -23
- package/dist/connectors/azure-sql.js +322 -25
- package/dist/connectors/backlog-api-key.js +79 -30
- package/dist/connectors/clickup.js +89 -25
- package/dist/connectors/cosmosdb.js +51 -27
- package/dist/connectors/customerio.js +47 -23
- package/dist/connectors/dbt.js +725 -40
- package/dist/connectors/freshdesk.js +121 -23
- package/dist/connectors/freshsales.js +47 -23
- package/dist/connectors/freshservice.js +47 -23
- package/dist/connectors/gamma.js +54 -30
- package/dist/connectors/gemini.js +41 -17
- package/dist/connectors/github.js +51 -27
- package/dist/connectors/gmail-oauth.js +42 -23
- package/dist/connectors/gmail.js +43 -19
- package/dist/connectors/google-ads.js +47 -23
- package/dist/connectors/google-analytics-oauth.js +191 -40
- package/dist/connectors/google-analytics.js +529 -111
- package/dist/connectors/google-audit-log.js +43 -19
- package/dist/connectors/google-calendar-oauth.js +100 -30
- package/dist/connectors/google-calendar.js +100 -26
- package/dist/connectors/google-docs.js +42 -23
- package/dist/connectors/google-drive.js +64 -23
- package/dist/connectors/google-search-console-oauth.js +165 -32
- package/dist/connectors/google-sheets.js +142 -199
- package/dist/connectors/google-slides.js +42 -23
- package/dist/connectors/grafana.js +84 -25
- package/dist/connectors/hackernews.d.ts +5 -0
- package/dist/connectors/hackernews.js +909 -0
- package/dist/connectors/hubspot-oauth.js +73 -22
- package/dist/connectors/hubspot.js +64 -24
- package/dist/connectors/influxdb.js +47 -23
- package/dist/connectors/intercom-oauth.js +104 -25
- package/dist/connectors/intercom.js +51 -27
- package/dist/connectors/jdbc.js +76 -25
- package/dist/connectors/jira-api-key.js +107 -26
- package/dist/connectors/kintone-api-token.js +105 -33
- package/dist/connectors/kintone.js +182 -26
- package/dist/connectors/linear.js +93 -27
- package/dist/connectors/linkedin-ads.js +80 -29
- package/dist/connectors/mailchimp-oauth.js +45 -21
- package/dist/connectors/mailchimp.js +45 -21
- package/dist/connectors/meta-ads-oauth.js +72 -29
- package/dist/connectors/meta-ads.js +74 -29
- package/dist/connectors/mixpanel.js +47 -23
- package/dist/connectors/monday.js +48 -24
- package/dist/connectors/mongodb.js +47 -23
- package/dist/connectors/notion-oauth.js +92 -24
- package/dist/connectors/notion.js +144 -26
- package/dist/connectors/openai.js +41 -17
- package/dist/connectors/oracle.js +316 -26
- package/dist/connectors/outlook-oauth.js +53 -34
- package/dist/connectors/powerbi-oauth.js +45 -26
- package/dist/connectors/salesforce.js +150 -24
- package/dist/connectors/semrush.js +45 -21
- package/dist/connectors/sentry.js +75 -25
- package/dist/connectors/shopify-oauth.js +75 -23
- package/dist/connectors/shopify.js +47 -23
- package/dist/connectors/sqlserver.js +322 -25
- package/dist/connectors/stripe-api-key.js +105 -30
- package/dist/connectors/stripe-oauth.js +102 -32
- package/dist/connectors/supabase.js +315 -21
- package/dist/connectors/tableau.js +54 -30
- package/dist/connectors/tiktok-ads.js +76 -31
- package/dist/connectors/wix-store.js +47 -23
- package/dist/connectors/x.d.ts +5 -0
- package/dist/connectors/x.js +951 -0
- package/dist/connectors/zendesk-oauth.js +87 -25
- package/dist/connectors/zendesk.js +51 -27
- package/dist/index.js +9005 -3709
- package/dist/main.js +9005 -3709
- package/dist/vite-plugin.js +8939 -3698
- package/package.json +12 -4
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
-
var __esm = (fn, res) => function __init() {
|
|
3
|
-
|
|
2
|
+
var __esm = (fn, res, err) => function __init() {
|
|
3
|
+
if (err) throw err[0];
|
|
4
|
+
try {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
} catch (e) {
|
|
7
|
+
throw err = [e], e;
|
|
8
|
+
}
|
|
4
9
|
};
|
|
5
10
|
|
|
6
11
|
// ../connectors/src/parameter-definition.ts
|
|
@@ -169,6 +174,15 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
169
174
|
* `runSetupFlow` from `setup-flow.ts`.
|
|
170
175
|
*/
|
|
171
176
|
setup;
|
|
177
|
+
/**
|
|
178
|
+
* 統一 introspection。SQL DB の `information_schema` / `SHOW` / `DESCRIBE` や SaaS の
|
|
179
|
+
* メタデータ API といった取得経路の差を吸収し、tables / columns / PK / FK を共通型
|
|
180
|
+
* {@link IntrospectedSchema} へ正規化して返す。取得経路は {@link IntrospectContext} で
|
|
181
|
+
* 抽象化され、SQL DB は `ctx.query`、SaaS は `ctx.proxyFetch` + `ctx.params` を使う。
|
|
182
|
+
* `ctx.profile === true` のときは L2 プロファイル(行数・NULL 率等)も best-effort で埋める。
|
|
183
|
+
* coding-agent の `DataContextIndex` がデータ地図を構築する際に使う。
|
|
184
|
+
*/
|
|
185
|
+
introspect;
|
|
172
186
|
/**
|
|
173
187
|
* Opt-out of the default "verify before save" behavior on connection
|
|
174
188
|
* creation. The backend invokes `checkConnection` synchronously while
|
|
@@ -183,6 +197,14 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
183
197
|
* the default verify-on-create behavior without opt-in.
|
|
184
198
|
*/
|
|
185
199
|
skipConnectionCheckOnCreate;
|
|
200
|
+
/**
|
|
201
|
+
* 決定論的な「立ち上げ survey」レシピ(ルールベース prewarm 基盤)。実装した connector は、LLM を使わずに
|
|
202
|
+
* read-only で自分を survey する probe 列(テーブル/列メタデータ → 小サンプル)を宣言する。coding-agent の
|
|
203
|
+
* recon driver がこれを読み、connector ツールの `execute` を直接呼んで結果を seed にする。未実装の connector は
|
|
204
|
+
* driver 側で safely skip される(その connection は listConnections のみ seed)。標準 `information_schema` を
|
|
205
|
+
* 持つ SQL connector は `makeStandardSqlSurveyProbes` を使える。**probe は必ず read-only**。
|
|
206
|
+
*/
|
|
207
|
+
surveyProbes;
|
|
186
208
|
constructor(config) {
|
|
187
209
|
this.slug = config.slug;
|
|
188
210
|
this.authType = config.authType;
|
|
@@ -200,7 +222,9 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
200
222
|
this.query = config.query;
|
|
201
223
|
this.checkConnection = config.checkConnection;
|
|
202
224
|
this.setup = config.setup;
|
|
225
|
+
this.introspect = config.introspect;
|
|
203
226
|
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
227
|
+
this.surveyProbes = config.surveyProbes;
|
|
204
228
|
}
|
|
205
229
|
get connectorKey() {
|
|
206
230
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -208,7 +232,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
208
232
|
/**
|
|
209
233
|
* Create tools for connections that belong to this connector.
|
|
210
234
|
* Filters connections by connectorKey internally.
|
|
211
|
-
* Returns tools keyed as
|
|
235
|
+
* Returns tools keyed as `connector_${connectorKey}_${toolName}`.
|
|
212
236
|
*/
|
|
213
237
|
createTools(connections, config, opts) {
|
|
214
238
|
const myConnections = connections.filter(
|
|
@@ -218,7 +242,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
218
242
|
for (const t of Object.values(this.tools)) {
|
|
219
243
|
const tool = t.createTool(myConnections, config);
|
|
220
244
|
const originalToModelOutput = tool.toModelOutput;
|
|
221
|
-
result[
|
|
245
|
+
result[`connector_${this.connectorKey}_${t.name}`] = {
|
|
222
246
|
...tool,
|
|
223
247
|
toModelOutput: async (options) => {
|
|
224
248
|
if (!originalToModelOutput) {
|
|
@@ -238,25 +262,25 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
238
262
|
if (authType) return `${slug}-${authType}`;
|
|
239
263
|
const LEGACY_NULL_AUTH_TYPE_MAP = {
|
|
240
264
|
// user-password
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
265
|
+
postgresql: "user-password",
|
|
266
|
+
mysql: "user-password",
|
|
267
|
+
clickhouse: "user-password",
|
|
268
|
+
kintone: "user-password",
|
|
245
269
|
"squadbase-db": "user-password",
|
|
246
270
|
// service-account
|
|
247
|
-
|
|
248
|
-
|
|
271
|
+
snowflake: "service-account",
|
|
272
|
+
bigquery: "service-account",
|
|
249
273
|
"google-analytics": "service-account",
|
|
250
274
|
"google-calendar": "service-account",
|
|
251
275
|
"aws-athena": "service-account",
|
|
252
|
-
|
|
276
|
+
redshift: "service-account",
|
|
253
277
|
// api-key
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
278
|
+
databricks: "api-key",
|
|
279
|
+
dbt: "api-key",
|
|
280
|
+
airtable: "api-key",
|
|
281
|
+
openai: "api-key",
|
|
282
|
+
gemini: "api-key",
|
|
283
|
+
anthropic: "api-key",
|
|
260
284
|
"wix-store": "api-key"
|
|
261
285
|
};
|
|
262
286
|
const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
|
|
@@ -265,6 +289,84 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
265
289
|
}
|
|
266
290
|
};
|
|
267
291
|
|
|
292
|
+
// ../connectors/src/survey-probe.ts
|
|
293
|
+
function rowsOf(output) {
|
|
294
|
+
if (!output || typeof output !== "object") return [];
|
|
295
|
+
const o = output;
|
|
296
|
+
if (o.success === false) return [];
|
|
297
|
+
return Array.isArray(o.rows) ? o.rows : [];
|
|
298
|
+
}
|
|
299
|
+
function pickString(row, candidates) {
|
|
300
|
+
for (const key of candidates) {
|
|
301
|
+
const v = row[key] ?? row[key.toUpperCase()] ?? row[key.toLowerCase()];
|
|
302
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
303
|
+
}
|
|
304
|
+
return void 0;
|
|
305
|
+
}
|
|
306
|
+
var TABLE_NAME_FIELDS = [
|
|
307
|
+
"table_name",
|
|
308
|
+
"name",
|
|
309
|
+
"tableName",
|
|
310
|
+
"tab_name",
|
|
311
|
+
"Table",
|
|
312
|
+
"table"
|
|
313
|
+
];
|
|
314
|
+
function makeQuoteIdent(opts) {
|
|
315
|
+
if (opts.bracket) {
|
|
316
|
+
return (name) => `[${name.replaceAll("]", "]]")}]`;
|
|
317
|
+
}
|
|
318
|
+
const q = opts.quote ?? '"';
|
|
319
|
+
return (name) => `${q}${name.replaceAll(q, q + q)}${q}`;
|
|
320
|
+
}
|
|
321
|
+
function sampleSql(ref, limit) {
|
|
322
|
+
return `SELECT * FROM (SELECT * FROM ${ref}) AS _sq LIMIT ${limit}`;
|
|
323
|
+
}
|
|
324
|
+
function distinctTables(prior, max) {
|
|
325
|
+
const names = [];
|
|
326
|
+
const seen = /* @__PURE__ */ new Set();
|
|
327
|
+
for (const row of rowsOf(prior[0]?.output)) {
|
|
328
|
+
const name = pickString(row, TABLE_NAME_FIELDS);
|
|
329
|
+
if (!name || seen.has(name)) continue;
|
|
330
|
+
seen.add(name);
|
|
331
|
+
names.push(name);
|
|
332
|
+
if (names.length >= max) break;
|
|
333
|
+
}
|
|
334
|
+
return names;
|
|
335
|
+
}
|
|
336
|
+
function makeStandardSqlSurveyProbes(opts) {
|
|
337
|
+
const queryTool = opts.queryTool ?? "executeQuery";
|
|
338
|
+
const sampleLimit = opts.sampleLimit ?? 5;
|
|
339
|
+
const maxSampleTables = opts.maxSampleTables ?? 4;
|
|
340
|
+
const quoteIdent = makeQuoteIdent(opts);
|
|
341
|
+
const whereSchema = opts.schemaLiteral ? ` WHERE table_schema = '${opts.schemaLiteral.replaceAll("'", "''")}'` : opts.schemaExpr ? ` WHERE table_schema = ${opts.schemaExpr}` : "";
|
|
342
|
+
return (_connectionId, prior) => {
|
|
343
|
+
if (prior.length === 0) {
|
|
344
|
+
return [
|
|
345
|
+
{
|
|
346
|
+
tool: queryTool,
|
|
347
|
+
input: {
|
|
348
|
+
toolUseIntent: "Survey table/column metadata",
|
|
349
|
+
sql: `SELECT table_name, column_name, data_type FROM information_schema.columns${whereSchema} ORDER BY table_name, ordinal_position LIMIT 1000`
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
];
|
|
353
|
+
}
|
|
354
|
+
if (prior.length === 1) {
|
|
355
|
+
return distinctTables(prior, maxSampleTables).map((name) => {
|
|
356
|
+
const ref = opts.schemaLiteral ? `${quoteIdent(opts.schemaLiteral)}.${quoteIdent(name)}` : quoteIdent(name);
|
|
357
|
+
return {
|
|
358
|
+
tool: queryTool,
|
|
359
|
+
input: {
|
|
360
|
+
toolUseIntent: `Sample ${name}`,
|
|
361
|
+
sql: sampleSql(ref, sampleLimit)
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
return [];
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
268
370
|
// ../connectors/src/setup-flow.ts
|
|
269
371
|
async function runSetupFlow(flow, params, ctx, config) {
|
|
270
372
|
const runtime = {
|
|
@@ -335,6 +437,58 @@ async function resolveSetupSelection(params) {
|
|
|
335
437
|
return resolved.slice(0, limit);
|
|
336
438
|
}
|
|
337
439
|
|
|
440
|
+
// ../connectors/src/introspect-concurrency.ts
|
|
441
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
442
|
+
const results = new Array(items.length);
|
|
443
|
+
let next = 0;
|
|
444
|
+
const workers = Array.from(
|
|
445
|
+
{ length: Math.min(Math.max(limit, 1), items.length) },
|
|
446
|
+
async () => {
|
|
447
|
+
for (; ; ) {
|
|
448
|
+
const i = next++;
|
|
449
|
+
if (i >= items.length) return;
|
|
450
|
+
results[i] = await fn(items[i]);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
);
|
|
454
|
+
await Promise.all(workers);
|
|
455
|
+
return results;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// ../connectors/src/introspect-profile.ts
|
|
459
|
+
var DEFAULT_MAX_TABLES = 200;
|
|
460
|
+
var DEFAULT_CONCURRENCY = 5;
|
|
461
|
+
function dq(ident) {
|
|
462
|
+
return `"${ident.replace(/"/g, '""')}"`;
|
|
463
|
+
}
|
|
464
|
+
var quoteAnsi = (schema, name) => [...schema.split("."), name].map(dq).join(".");
|
|
465
|
+
async function withRowCounts(ctx, schemaPromise, quoteRef, opts = {}) {
|
|
466
|
+
const schema = await schemaPromise;
|
|
467
|
+
if (!ctx.profile || !ctx.query) return schema;
|
|
468
|
+
return attachRowCounts(ctx.query, schema, quoteRef, opts);
|
|
469
|
+
}
|
|
470
|
+
async function attachRowCounts(run, schema, quoteRef, opts = {}) {
|
|
471
|
+
const targets = schema.tables.slice(0, opts.maxTables ?? DEFAULT_MAX_TABLES);
|
|
472
|
+
await mapWithConcurrency(
|
|
473
|
+
targets,
|
|
474
|
+
opts.concurrency ?? DEFAULT_CONCURRENCY,
|
|
475
|
+
async (table) => {
|
|
476
|
+
try {
|
|
477
|
+
const { rows } = await run(
|
|
478
|
+
`SELECT COUNT(*) AS row_count FROM ${quoteRef(table.schema, table.name)}`
|
|
479
|
+
);
|
|
480
|
+
const first = rows[0] ? Object.values(rows[0])[0] : void 0;
|
|
481
|
+
const rowCount = Number(first);
|
|
482
|
+
if (Number.isFinite(rowCount)) {
|
|
483
|
+
table.profile = { ...table.profile ?? {}, rowCount };
|
|
484
|
+
}
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
);
|
|
489
|
+
return schema;
|
|
490
|
+
}
|
|
491
|
+
|
|
338
492
|
// ../connectors/src/auth-types.ts
|
|
339
493
|
var AUTH_TYPES = {
|
|
340
494
|
OAUTH: "oauth",
|
|
@@ -360,6 +514,116 @@ function buildPositionalParams(sql, namedParams) {
|
|
|
360
514
|
return { text, values };
|
|
361
515
|
}
|
|
362
516
|
|
|
517
|
+
// ../connectors/src/introspect-information-schema.ts
|
|
518
|
+
function pickCI(row, key) {
|
|
519
|
+
if (key in row) return String(row[key] ?? "");
|
|
520
|
+
const lower = key.toLowerCase();
|
|
521
|
+
for (const k of Object.keys(row)) {
|
|
522
|
+
if (k.toLowerCase() === lower) return String(row[k] ?? "");
|
|
523
|
+
}
|
|
524
|
+
return "";
|
|
525
|
+
}
|
|
526
|
+
function assembleIntrospectedSchema(input) {
|
|
527
|
+
const { colRows, pkRows = [], fkRows = [] } = input;
|
|
528
|
+
const pkSet = /* @__PURE__ */ new Set();
|
|
529
|
+
for (const r of pkRows) {
|
|
530
|
+
pkSet.add(
|
|
531
|
+
`${pickCI(r, "table_schema")}.${pickCI(r, "table_name")}.${pickCI(r, "column_name")}`
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
const fkMap = /* @__PURE__ */ new Map();
|
|
535
|
+
for (const r of fkRows) {
|
|
536
|
+
fkMap.set(
|
|
537
|
+
`${pickCI(r, "table_schema")}.${pickCI(r, "table_name")}.${pickCI(r, "column_name")}`,
|
|
538
|
+
{
|
|
539
|
+
schema: pickCI(r, "foreign_table_schema"),
|
|
540
|
+
table: pickCI(r, "foreign_table_name"),
|
|
541
|
+
column: pickCI(r, "foreign_column_name")
|
|
542
|
+
}
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
const tableMap = /* @__PURE__ */ new Map();
|
|
546
|
+
for (const r of colRows) {
|
|
547
|
+
const schema = pickCI(r, "table_schema");
|
|
548
|
+
const name = pickCI(r, "table_name");
|
|
549
|
+
const key = `${schema}.${name}`;
|
|
550
|
+
let table = tableMap.get(key);
|
|
551
|
+
if (!table) {
|
|
552
|
+
table = {
|
|
553
|
+
schema,
|
|
554
|
+
name,
|
|
555
|
+
type: pickCI(r, "table_type").toUpperCase() === "VIEW" ? "view" : "table",
|
|
556
|
+
columns: []
|
|
557
|
+
};
|
|
558
|
+
tableMap.set(key, table);
|
|
559
|
+
}
|
|
560
|
+
const colKey = `${schema}.${name}.${pickCI(r, "column_name")}`;
|
|
561
|
+
const column = {
|
|
562
|
+
name: pickCI(r, "column_name"),
|
|
563
|
+
dataType: pickCI(r, "data_type"),
|
|
564
|
+
nullable: pickCI(r, "is_nullable").toUpperCase() === "YES"
|
|
565
|
+
};
|
|
566
|
+
if (pkSet.has(colKey)) column.isPrimaryKey = true;
|
|
567
|
+
const fk = fkMap.get(colKey);
|
|
568
|
+
if (fk) column.foreignKey = fk;
|
|
569
|
+
table.columns.push(column);
|
|
570
|
+
}
|
|
571
|
+
return { tables: [...tableMap.values()] };
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// ../connectors/src/connectors/postgresql/introspect.ts
|
|
575
|
+
async function introspectPostgresql(run) {
|
|
576
|
+
const SCHEMA_FILTER = `c.table_schema NOT IN ('pg_catalog', 'information_schema') AND c.table_schema NOT LIKE 'pg\\_%'`;
|
|
577
|
+
const [{ rows: colRows }, { rows: pkRows }, { rows: fkRows }] = await Promise.all([
|
|
578
|
+
// 1) 全カラム(型 / nullable)+ テーブル種別(BASE TABLE / VIEW)
|
|
579
|
+
run(
|
|
580
|
+
`SELECT c.table_schema, c.table_name, c.column_name, c.data_type,
|
|
581
|
+
c.is_nullable, c.ordinal_position, t.table_type
|
|
582
|
+
FROM information_schema.columns c
|
|
583
|
+
JOIN information_schema.tables t
|
|
584
|
+
ON t.table_schema = c.table_schema AND t.table_name = c.table_name
|
|
585
|
+
WHERE ${SCHEMA_FILTER}
|
|
586
|
+
ORDER BY c.table_schema, c.table_name, c.ordinal_position`
|
|
587
|
+
),
|
|
588
|
+
// 2) 主キー(schema.table.column)
|
|
589
|
+
run(
|
|
590
|
+
`SELECT tc.table_schema, tc.table_name, kcu.column_name
|
|
591
|
+
FROM information_schema.table_constraints tc
|
|
592
|
+
JOIN information_schema.key_column_usage kcu
|
|
593
|
+
ON kcu.constraint_name = tc.constraint_name
|
|
594
|
+
AND kcu.table_schema = tc.table_schema
|
|
595
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
596
|
+
AND tc.table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
597
|
+
AND tc.table_schema NOT LIKE 'pg\\_%'`
|
|
598
|
+
),
|
|
599
|
+
// 3) 外部キー(参照元 → 参照先)。
|
|
600
|
+
// 複合 FK を正しく対応付けるため、`constraint_column_usage`(順序非保証 + constraint_name のみの
|
|
601
|
+
// join で複合キーが直積爆発する)を使わず、`referential_constraints` 経由で参照先 unique 制約へ辿り、
|
|
602
|
+
// FK 列の `position_in_unique_constraint` を参照先列の `ordinal_position` に突き合わせて 1:1 に組む。
|
|
603
|
+
run(
|
|
604
|
+
`SELECT tc.table_schema, tc.table_name, kcu.column_name,
|
|
605
|
+
fkcu.table_schema AS foreign_table_schema,
|
|
606
|
+
fkcu.table_name AS foreign_table_name,
|
|
607
|
+
fkcu.column_name AS foreign_column_name
|
|
608
|
+
FROM information_schema.table_constraints tc
|
|
609
|
+
JOIN information_schema.key_column_usage kcu
|
|
610
|
+
ON kcu.constraint_name = tc.constraint_name
|
|
611
|
+
AND kcu.constraint_schema = tc.constraint_schema
|
|
612
|
+
JOIN information_schema.referential_constraints rc
|
|
613
|
+
ON rc.constraint_name = tc.constraint_name
|
|
614
|
+
AND rc.constraint_schema = tc.constraint_schema
|
|
615
|
+
JOIN information_schema.key_column_usage fkcu
|
|
616
|
+
ON fkcu.constraint_name = rc.unique_constraint_name
|
|
617
|
+
AND fkcu.constraint_schema = rc.unique_constraint_schema
|
|
618
|
+
AND fkcu.ordinal_position = kcu.position_in_unique_constraint
|
|
619
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
620
|
+
AND tc.table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
621
|
+
AND tc.table_schema NOT LIKE 'pg\\_%'`
|
|
622
|
+
)
|
|
623
|
+
]);
|
|
624
|
+
return assembleIntrospectedSchema({ colRows, pkRows, fkRows });
|
|
625
|
+
}
|
|
626
|
+
|
|
363
627
|
// ../connectors/src/connectors/supabase/setup.ts
|
|
364
628
|
var supabaseOnboarding = new ConnectorOnboarding({
|
|
365
629
|
dataOverviewInstructions: {
|
|
@@ -469,6 +733,21 @@ var supabaseSetupFlow = {
|
|
|
469
733
|
fetchAll: () => fetchTableNames(rt.params, schema),
|
|
470
734
|
limit: SUPABASE_SETUP_MAX_TABLES
|
|
471
735
|
});
|
|
736
|
+
let rowCounts = /* @__PURE__ */ new Map();
|
|
737
|
+
try {
|
|
738
|
+
const countRows = await runQuery(
|
|
739
|
+
rt.params,
|
|
740
|
+
`SELECT relname, n_live_tup FROM pg_stat_user_tables WHERE schemaname = $1`,
|
|
741
|
+
[schema]
|
|
742
|
+
);
|
|
743
|
+
rowCounts = new Map(
|
|
744
|
+
countRows.map((r) => [
|
|
745
|
+
String(r["relname"] ?? ""),
|
|
746
|
+
Number(r["n_live_tup"] ?? 0)
|
|
747
|
+
])
|
|
748
|
+
);
|
|
749
|
+
} catch {
|
|
750
|
+
}
|
|
472
751
|
const sections = [
|
|
473
752
|
"## Supabase",
|
|
474
753
|
"",
|
|
@@ -484,7 +763,9 @@ var supabaseSetupFlow = {
|
|
|
484
763
|
ORDER BY ordinal_position`,
|
|
485
764
|
[schema, table]
|
|
486
765
|
);
|
|
487
|
-
|
|
766
|
+
const rowCount = rowCounts.get(table);
|
|
767
|
+
const heading = typeof rowCount === "number" ? `#### Table: ${table} (~${rowCount.toLocaleString()} rows)` : `#### Table: ${table}`;
|
|
768
|
+
sections.push(heading, "");
|
|
488
769
|
sections.push("| Column | Type | Nullable | Default |");
|
|
489
770
|
sections.push("|--------|------|----------|---------|");
|
|
490
771
|
for (const c of cols) {
|
|
@@ -520,6 +801,9 @@ var outputSchema = z.discriminatedUnion("success", [
|
|
|
520
801
|
z.object({
|
|
521
802
|
success: z.literal(true),
|
|
522
803
|
rowCount: z.number(),
|
|
804
|
+
rowsAffected: z.number().optional().describe(
|
|
805
|
+
"Affected rows for INSERT/UPDATE/DELETE; undefined for SELECT"
|
|
806
|
+
),
|
|
523
807
|
truncated: z.boolean(),
|
|
524
808
|
rows: z.array(z.record(z.string(), z.unknown()))
|
|
525
809
|
}),
|
|
@@ -533,7 +817,8 @@ var executeQueryTool = new ConnectorTool({
|
|
|
533
817
|
description: `Execute SQL against a Supabase Postgres database. Returns up to ${MAX_ROWS} rows.
|
|
534
818
|
Use for: schema exploration (information_schema), data sampling, and analytical queries against Supabase tables.
|
|
535
819
|
User tables live in the \`public\` schema by default; Supabase-managed metadata lives in \`auth\` and \`storage\`.
|
|
536
|
-
Avoid loading large amounts of data; always include LIMIT in queries
|
|
820
|
+
Avoid loading large amounts of data; always include LIMIT in queries.
|
|
821
|
+
For INSERT/UPDATE/DELETE, \`rowCount\` is 0 and \`rows\` is empty (rowCount counts returned rows, not affected rows) \u2014 this is normal, not a failure; check \`rowsAffected\` for the number of affected rows. Use RETURNING to get written rows back in \`rows\`.`,
|
|
537
822
|
inputSchema,
|
|
538
823
|
outputSchema,
|
|
539
824
|
async execute({ connectionId, sql }, connections) {
|
|
@@ -561,9 +846,13 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
|
561
846
|
const result = await pool.query(sql);
|
|
562
847
|
const rows = result.rows;
|
|
563
848
|
const truncated = rows.length > MAX_ROWS;
|
|
849
|
+
const isDml = ["INSERT", "UPDATE", "DELETE", "MERGE"].includes(
|
|
850
|
+
result.command
|
|
851
|
+
);
|
|
564
852
|
return {
|
|
565
853
|
success: true,
|
|
566
854
|
rowCount: Math.min(rows.length, MAX_ROWS),
|
|
855
|
+
rowsAffected: isDml ? result.rowCount ?? 0 : void 0,
|
|
567
856
|
truncated,
|
|
568
857
|
rows: rows.slice(0, MAX_ROWS)
|
|
569
858
|
};
|
|
@@ -595,7 +884,7 @@ var supabaseConnector = new ConnectorPlugin({
|
|
|
595
884
|
systemPrompt: {
|
|
596
885
|
en: `### Tools
|
|
597
886
|
|
|
598
|
-
- \`
|
|
887
|
+
- \`connector_supabase_executeQuery\`: Executes a PostgreSQL query against the Supabase project's database and returns rows. Use this for schema exploration via \`information_schema.tables\`/\`columns\` and for sampling data. See the SQL Reference below for syntax notes and Supabase-specific schemas.
|
|
599
888
|
|
|
600
889
|
### Business Logic
|
|
601
890
|
|
|
@@ -612,7 +901,7 @@ The business logic type for this connector is "sql".
|
|
|
612
901
|
- Always include LIMIT in queries`,
|
|
613
902
|
ja: `### \u30C4\u30FC\u30EB
|
|
614
903
|
|
|
615
|
-
- \`
|
|
904
|
+
- \`connector_supabase_executeQuery\`: Supabase\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E Postgres \u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306B\u5BFE\u3057\u3066\u30AF\u30A8\u30EA\u3092\u5B9F\u884C\u3057\u3001\u884C\u30C7\u30FC\u30BF\u3092\u8FD4\u3057\u307E\u3059\u3002\`information_schema.tables\`/\`columns\` \u3092\u4F7F\u3063\u305F\u30B9\u30AD\u30FC\u30DE\u63A2\u7D22\u3084\u30C7\u30FC\u30BF\u306E\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u306B\u4F7F\u3044\u307E\u3059\u3002\u69CB\u6587\u306E\u6CE8\u610F\u70B9\u3068 Supabase \u56FA\u6709\u306E\u30B9\u30AD\u30FC\u30DE\u306F\u4E0B\u90E8\u306E\u300CSQL \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
616
905
|
|
|
617
906
|
### Business Logic
|
|
618
907
|
|
|
@@ -629,6 +918,11 @@ The business logic type for this connector is "sql".
|
|
|
629
918
|
- \u30AF\u30A8\u30EA\u306B\u306F\u5FC5\u305A LIMIT \u3092\u542B\u3081\u3066\u304F\u3060\u3055\u3044`
|
|
630
919
|
},
|
|
631
920
|
tools,
|
|
921
|
+
// 決定論的な立ち上げ survey(ルールベース prewarm)。Supabase は Postgres で公開スキーマは `public`。
|
|
922
|
+
surveyProbes: makeStandardSqlSurveyProbes({ schemaLiteral: "public" }),
|
|
923
|
+
// Supabase は PostgreSQL なので postgresql の introspect をそのまま流用する
|
|
924
|
+
// (exploreData / DataContextIndex の対象に含めるため)。
|
|
925
|
+
introspect: (ctx) => withRowCounts(ctx, introspectPostgresql(ctx.query), quoteAnsi),
|
|
632
926
|
setup: (params, ctx, config) => runSetupFlow(supabaseSetupFlow, params, ctx, config),
|
|
633
927
|
async checkConnection(params, _config) {
|
|
634
928
|
const { Pool } = await import("pg");
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
-
var __esm = (fn, res) => function __init() {
|
|
3
|
-
|
|
2
|
+
var __esm = (fn, res, err) => function __init() {
|
|
3
|
+
if (err) throw err[0];
|
|
4
|
+
try {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
} catch (e) {
|
|
7
|
+
throw err = [e], e;
|
|
8
|
+
}
|
|
4
9
|
};
|
|
5
10
|
|
|
6
11
|
// ../connectors/src/parameter-definition.ts
|
|
@@ -266,6 +271,15 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
266
271
|
* `runSetupFlow` from `setup-flow.ts`.
|
|
267
272
|
*/
|
|
268
273
|
setup;
|
|
274
|
+
/**
|
|
275
|
+
* 統一 introspection。SQL DB の `information_schema` / `SHOW` / `DESCRIBE` や SaaS の
|
|
276
|
+
* メタデータ API といった取得経路の差を吸収し、tables / columns / PK / FK を共通型
|
|
277
|
+
* {@link IntrospectedSchema} へ正規化して返す。取得経路は {@link IntrospectContext} で
|
|
278
|
+
* 抽象化され、SQL DB は `ctx.query`、SaaS は `ctx.proxyFetch` + `ctx.params` を使う。
|
|
279
|
+
* `ctx.profile === true` のときは L2 プロファイル(行数・NULL 率等)も best-effort で埋める。
|
|
280
|
+
* coding-agent の `DataContextIndex` がデータ地図を構築する際に使う。
|
|
281
|
+
*/
|
|
282
|
+
introspect;
|
|
269
283
|
/**
|
|
270
284
|
* Opt-out of the default "verify before save" behavior on connection
|
|
271
285
|
* creation. The backend invokes `checkConnection` synchronously while
|
|
@@ -280,6 +294,14 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
280
294
|
* the default verify-on-create behavior without opt-in.
|
|
281
295
|
*/
|
|
282
296
|
skipConnectionCheckOnCreate;
|
|
297
|
+
/**
|
|
298
|
+
* 決定論的な「立ち上げ survey」レシピ(ルールベース prewarm 基盤)。実装した connector は、LLM を使わずに
|
|
299
|
+
* read-only で自分を survey する probe 列(テーブル/列メタデータ → 小サンプル)を宣言する。coding-agent の
|
|
300
|
+
* recon driver がこれを読み、connector ツールの `execute` を直接呼んで結果を seed にする。未実装の connector は
|
|
301
|
+
* driver 側で safely skip される(その connection は listConnections のみ seed)。標準 `information_schema` を
|
|
302
|
+
* 持つ SQL connector は `makeStandardSqlSurveyProbes` を使える。**probe は必ず read-only**。
|
|
303
|
+
*/
|
|
304
|
+
surveyProbes;
|
|
283
305
|
constructor(config) {
|
|
284
306
|
this.slug = config.slug;
|
|
285
307
|
this.authType = config.authType;
|
|
@@ -297,7 +319,9 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
297
319
|
this.query = config.query;
|
|
298
320
|
this.checkConnection = config.checkConnection;
|
|
299
321
|
this.setup = config.setup;
|
|
322
|
+
this.introspect = config.introspect;
|
|
300
323
|
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
324
|
+
this.surveyProbes = config.surveyProbes;
|
|
301
325
|
}
|
|
302
326
|
get connectorKey() {
|
|
303
327
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -305,7 +329,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
305
329
|
/**
|
|
306
330
|
* Create tools for connections that belong to this connector.
|
|
307
331
|
* Filters connections by connectorKey internally.
|
|
308
|
-
* Returns tools keyed as
|
|
332
|
+
* Returns tools keyed as `connector_${connectorKey}_${toolName}`.
|
|
309
333
|
*/
|
|
310
334
|
createTools(connections, config, opts) {
|
|
311
335
|
const myConnections = connections.filter(
|
|
@@ -315,7 +339,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
315
339
|
for (const t of Object.values(this.tools)) {
|
|
316
340
|
const tool = t.createTool(myConnections, config);
|
|
317
341
|
const originalToModelOutput = tool.toModelOutput;
|
|
318
|
-
result[
|
|
342
|
+
result[`connector_${this.connectorKey}_${t.name}`] = {
|
|
319
343
|
...tool,
|
|
320
344
|
toModelOutput: async (options) => {
|
|
321
345
|
if (!originalToModelOutput) {
|
|
@@ -335,25 +359,25 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
335
359
|
if (authType) return `${slug}-${authType}`;
|
|
336
360
|
const LEGACY_NULL_AUTH_TYPE_MAP = {
|
|
337
361
|
// user-password
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
362
|
+
postgresql: "user-password",
|
|
363
|
+
mysql: "user-password",
|
|
364
|
+
clickhouse: "user-password",
|
|
365
|
+
kintone: "user-password",
|
|
342
366
|
"squadbase-db": "user-password",
|
|
343
367
|
// service-account
|
|
344
|
-
|
|
345
|
-
|
|
368
|
+
snowflake: "service-account",
|
|
369
|
+
bigquery: "service-account",
|
|
346
370
|
"google-analytics": "service-account",
|
|
347
371
|
"google-calendar": "service-account",
|
|
348
372
|
"aws-athena": "service-account",
|
|
349
|
-
|
|
373
|
+
redshift: "service-account",
|
|
350
374
|
// api-key
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
375
|
+
databricks: "api-key",
|
|
376
|
+
dbt: "api-key",
|
|
377
|
+
airtable: "api-key",
|
|
378
|
+
openai: "api-key",
|
|
379
|
+
gemini: "api-key",
|
|
380
|
+
anthropic: "api-key",
|
|
357
381
|
"wix-store": "api-key"
|
|
358
382
|
};
|
|
359
383
|
const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
|
|
@@ -447,7 +471,7 @@ var tableauOnboarding = new ConnectorOnboarding({
|
|
|
447
471
|
connectionSetupInstructions: {
|
|
448
472
|
en: `Follow these steps to verify the Tableau PAT connection.
|
|
449
473
|
|
|
450
|
-
1. Call \`
|
|
474
|
+
1. Call \`connector_tableau_request\` with \`method: "GET"\` and \`path: "/sites/{siteId}/projects?pageSize=5"\` to list a few projects on the signed-in site
|
|
451
475
|
2. If the call fails with HTTP 401, ask the user to confirm the PAT name/secret and that the Site Content URL matches the site where the PAT was issued (Tableau Cloud sites are case-sensitive)
|
|
452
476
|
|
|
453
477
|
#### Constraints
|
|
@@ -455,7 +479,7 @@ var tableauOnboarding = new ConnectorOnboarding({
|
|
|
455
479
|
- The literal placeholder \`{siteId}\` in the path is automatically replaced with the session's site ID \u2014 do NOT hardcode the ID`,
|
|
456
480
|
ja: `Tableau PAT \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u306F\u4EE5\u4E0B\u306E\u624B\u9806\u3067\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
457
481
|
|
|
458
|
-
1. \`
|
|
482
|
+
1. \`connector_tableau_request\` \u3092 \`method: "GET"\`\u3001\`path: "/sites/{siteId}/projects?pageSize=5"\` \u3067\u547C\u3073\u51FA\u3057\u3001\u30B5\u30A4\u30F3\u30A4\u30F3\u5148\u30B5\u30A4\u30C8\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u3044\u304F\u3064\u304B\u53D6\u5F97\u3059\u308B
|
|
459
483
|
2. HTTP 401 \u3067\u5931\u6557\u3057\u305F\u5834\u5408\u306F\u3001PAT \u540D/\u30B7\u30FC\u30AF\u30EC\u30C3\u30C8\u304A\u3088\u3073 Site Content URL \u304C PAT \u767A\u884C\u30B5\u30A4\u30C8\u3068\u4E00\u81F4\u3057\u3066\u3044\u308B\u304B\u3092\u30E6\u30FC\u30B6\u30FC\u306B\u78BA\u8A8D\u3059\u308B\u3088\u3046\u4F1D\u3048\u308B\uFF08Tableau Cloud \u306E\u30B5\u30A4\u30C8\u540D\u306F\u5927\u6587\u5B57\u5C0F\u6587\u5B57\u3092\u533A\u5225\uFF09
|
|
460
484
|
|
|
461
485
|
#### \u5236\u7D04
|
|
@@ -463,14 +487,14 @@ var tableauOnboarding = new ConnectorOnboarding({
|
|
|
463
487
|
- \u30D1\u30B9\u5185\u306E\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC \`{siteId}\` \u306F\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u30B5\u30A4\u30C8 ID \u3067\u81EA\u52D5\u7F6E\u63DB\u3055\u308C\u307E\u3059 \u2014 ID \u3092\u30CF\u30FC\u30C9\u30B3\u30FC\u30C9\u3057\u306A\u3044\u3053\u3068`
|
|
464
488
|
},
|
|
465
489
|
dataOverviewInstructions: {
|
|
466
|
-
en: `1. Call
|
|
467
|
-
2. Call
|
|
468
|
-
3. For a target workbook, call
|
|
469
|
-
4. Call
|
|
470
|
-
ja: `1.
|
|
471
|
-
2.
|
|
472
|
-
3. \u5BFE\u8C61\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u306B\u3064\u3044\u3066
|
|
473
|
-
4.
|
|
490
|
+
en: `1. Call connector_tableau_request with GET /sites/{siteId}/projects to list projects
|
|
491
|
+
2. Call connector_tableau_request with GET /sites/{siteId}/workbooks?pageSize=20 to list workbooks
|
|
492
|
+
3. For a target workbook, call connector_tableau_request with GET /sites/{siteId}/workbooks/{workbookId}/views to list its views
|
|
493
|
+
4. Call connector_tableau_request with GET /sites/{siteId}/datasources?pageSize=20 to list published data sources`,
|
|
494
|
+
ja: `1. connector_tableau_request \u3067 GET /sites/{siteId}/projects \u3092\u547C\u3073\u51FA\u3057\u3001\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97
|
|
495
|
+
2. connector_tableau_request \u3067 GET /sites/{siteId}/workbooks?pageSize=20 \u3092\u547C\u3073\u51FA\u3057\u3001\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u4E00\u89A7\u3092\u53D6\u5F97
|
|
496
|
+
3. \u5BFE\u8C61\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u306B\u3064\u3044\u3066 connector_tableau_request \u3067 GET /sites/{siteId}/workbooks/{workbookId}/views \u3092\u547C\u3073\u51FA\u3057\u3001\u30D3\u30E5\u30FC\u4E00\u89A7\u3092\u53D6\u5F97
|
|
497
|
+
4. connector_tableau_request \u3067 GET /sites/{siteId}/datasources?pageSize=20 \u3092\u547C\u3073\u51FA\u3057\u3001\u516C\u958B\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u4E00\u89A7\u3092\u53D6\u5F97`
|
|
474
498
|
}
|
|
475
499
|
});
|
|
476
500
|
|
|
@@ -917,13 +941,13 @@ var tableauConnector = new ConnectorPlugin({
|
|
|
917
941
|
description: "Connect to Tableau Cloud or Tableau Server via a Personal Access Token (PAT). Use it to enumerate projects, workbooks, views, and data sources, and to pull view data.",
|
|
918
942
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/3ejrZvfw7zCDa3FPbUNQx9/d810e117b3a86c45dd96205453bf67a0/tableau-icon.svg",
|
|
919
943
|
parameters,
|
|
920
|
-
releaseFlag: { dev1: true, dev2:
|
|
944
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
921
945
|
categories: ["bi"],
|
|
922
946
|
onboarding: tableauOnboarding,
|
|
923
947
|
systemPrompt: {
|
|
924
948
|
en: `### Tools
|
|
925
949
|
|
|
926
|
-
- \`
|
|
950
|
+
- \`connector_tableau_request\`: The only way to call the Tableau REST API. Use it for projects, workbooks, views, data sources, users, and permissions. The \`X-Tableau-Auth\` token is issued and managed centrally by the Squadbase backend so concurrent processes (chat & deployed app) share a single PAT sign-in. Use the literal \`{siteId}\` placeholder in the path \u2014 it is replaced with the session's site ID automatically.
|
|
927
951
|
|
|
928
952
|
### Business Logic
|
|
929
953
|
|
|
@@ -980,7 +1004,7 @@ export default async function handler(c: Context) {
|
|
|
980
1004
|
- Combine: \`filter=ownerName:eq:alice,tags:in:[finance]\``,
|
|
981
1005
|
ja: `### \u30C4\u30FC\u30EB
|
|
982
1006
|
|
|
983
|
-
- \`
|
|
1007
|
+
- \`connector_tableau_request\`: Tableau REST API \u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3001\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u3001\u30D3\u30E5\u30FC\u3001\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u3001\u30E6\u30FC\u30B6\u30FC\u3001\u6A29\u9650\u306A\u3069\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\`X-Tableau-Auth\` \u30C8\u30FC\u30AF\u30F3\u306F Squadbase \u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u3067\u4E00\u5143\u7BA1\u7406\u3055\u308C\u3001\u8907\u6570\u30D7\u30ED\u30BB\u30B9 (\u30C1\u30E3\u30C3\u30C8\u30FB\u30C7\u30D7\u30ED\u30A4\u6E08\u307F\u30A2\u30D7\u30EA) \u3067\u540C\u3058 PAT \u30B5\u30A4\u30F3\u30A4\u30F3\u3092\u5171\u6709\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306B\u306F \`{siteId}\` \u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044 \u2014 \u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u30B5\u30A4\u30C8 ID \u3067\u81EA\u52D5\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002
|
|
984
1008
|
|
|
985
1009
|
### Business Logic
|
|
986
1010
|
|