@malloy-publisher/server 0.0.123 → 0.0.125
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/app/api-doc.yaml +10 -6
- package/dist/app/assets/{HomePage-DXKA9tWd.js → HomePage-D8mD1oZP.js} +1 -1
- package/dist/app/assets/{MainPage-BdYxYmkS.js → MainPage-C-n0YcOq.js} +1 -1
- package/dist/app/assets/{ModelPage-mQttYUXZ.js → ModelPage-0paRB20i.js} +1 -1
- package/dist/app/assets/{PackagePage-8dgNIkwK.js → PackagePage-DQAk5FaK.js} +1 -1
- package/dist/app/assets/{ProjectPage-D3lTBcOF.js → ProjectPage-C3uWnYcG.js} +1 -1
- package/dist/app/assets/{RouteError-B1FrgvdL.js → RouteError-BlMv9NJD.js} +1 -1
- package/dist/app/assets/{WorkbookPage-uhPZOv8J.js → WorkbookPage-BvOveto7.js} +1 -1
- package/dist/app/assets/index-BnoVbLmL.js +1260 -0
- package/dist/app/assets/{index-2wN22fP5.js → index-DCJ3_SfO.js} +1 -1
- package/dist/app/assets/{index-CfR2coZN.js → index-nHOvC22_.js} +5 -5
- package/dist/app/assets/{index.umd-DaPh4mA_.js → index.umd-DmY9YCDr.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/server.js +76 -34
- package/package.json +10 -10
- package/src/controller/connection.controller.ts +2 -3
- package/src/service/connection.ts +32 -43
- package/src/service/db_utils.ts +71 -7
- package/dist/app/assets/index-DiPnMvhX.js +0 -1184
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloy-publisher/server",
|
|
3
3
|
"description": "Malloy Publisher Server",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.125",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"malloy-publisher": "dist/server.js"
|
|
@@ -23,15 +23,15 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@google-cloud/storage": "^7.16.0",
|
|
26
|
-
"@malloydata/db-bigquery": "^0.0.
|
|
27
|
-
"@malloydata/db-duckdb": "^0.0.
|
|
28
|
-
"@malloydata/db-mysql": "^0.0.
|
|
29
|
-
"@malloydata/db-postgres": "^0.0.
|
|
30
|
-
"@malloydata/db-snowflake": "^0.0.
|
|
31
|
-
"@malloydata/db-trino": "^0.0.
|
|
32
|
-
"@malloydata/malloy": "^0.0.
|
|
33
|
-
"@malloydata/malloy-sql": "^0.0.
|
|
34
|
-
"@malloydata/render": "^0.0.
|
|
26
|
+
"@malloydata/db-bigquery": "^0.0.318",
|
|
27
|
+
"@malloydata/db-duckdb": "^0.0.318",
|
|
28
|
+
"@malloydata/db-mysql": "^0.0.318",
|
|
29
|
+
"@malloydata/db-postgres": "^0.0.318",
|
|
30
|
+
"@malloydata/db-snowflake": "^0.0.318",
|
|
31
|
+
"@malloydata/db-trino": "^0.0.318",
|
|
32
|
+
"@malloydata/malloy": "^0.0.318",
|
|
33
|
+
"@malloydata/malloy-sql": "^0.0.318",
|
|
34
|
+
"@malloydata/render": "^0.0.318",
|
|
35
35
|
"@modelcontextprotocol/sdk": "^1.13.2",
|
|
36
36
|
"@opentelemetry/api": "^1.9.0",
|
|
37
37
|
"@opentelemetry/auto-instrumentations-node": "^0.57.0",
|
|
@@ -40,9 +40,7 @@ export class ConnectionController {
|
|
|
40
40
|
if (connection.type === "duckdb") {
|
|
41
41
|
const packages = await project.listPackages();
|
|
42
42
|
if (packages.length === 0) {
|
|
43
|
-
|
|
44
|
-
"No packages found for DuckDB connection",
|
|
45
|
-
);
|
|
43
|
+
return project.getMalloyConnection(connectionName);
|
|
46
44
|
}
|
|
47
45
|
// For now, use the first package's DuckDB connection
|
|
48
46
|
const packageName = packages[0].name;
|
|
@@ -165,6 +163,7 @@ export class ConnectionController {
|
|
|
165
163
|
return {
|
|
166
164
|
resource: tablePath,
|
|
167
165
|
columns: tableSource.columns,
|
|
166
|
+
source: tableSource.source,
|
|
168
167
|
};
|
|
169
168
|
}
|
|
170
169
|
|
|
@@ -301,6 +301,7 @@ async function attachPostgres(
|
|
|
301
301
|
if (config.databaseName) parts.push(`dbname=${config.databaseName}`);
|
|
302
302
|
if (config.userName) parts.push(`user=${config.userName}`);
|
|
303
303
|
if (config.password) parts.push(`password=${config.password}`);
|
|
304
|
+
if (process.env.PGSSLMODE === "no-verify") parts.push(`sslmode=disable`);
|
|
304
305
|
attachString = parts.join(" ");
|
|
305
306
|
}
|
|
306
307
|
|
|
@@ -309,42 +310,6 @@ async function attachPostgres(
|
|
|
309
310
|
logger.info(`Successfully attached PostgreSQL database: ${attachedDb.name}`);
|
|
310
311
|
}
|
|
311
312
|
|
|
312
|
-
async function attachMotherDuck(
|
|
313
|
-
connection: DuckDBConnection,
|
|
314
|
-
attachedDb: AttachedDatabase,
|
|
315
|
-
): Promise<void> {
|
|
316
|
-
if (!attachedDb.motherDuckConnection) {
|
|
317
|
-
throw new Error(
|
|
318
|
-
`MotherDuck connection configuration missing for: ${attachedDb.name}`,
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const config = attachedDb.motherDuckConnection;
|
|
323
|
-
|
|
324
|
-
if (!config.database) {
|
|
325
|
-
throw new Error(
|
|
326
|
-
`MotherDuck database name is required for: ${attachedDb.name}`,
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
await installAndLoadExtension(connection, "motherduck");
|
|
331
|
-
|
|
332
|
-
// Set token if provided
|
|
333
|
-
if (config.accessToken) {
|
|
334
|
-
const escapedToken = escapeSQL(config.accessToken);
|
|
335
|
-
await connection.runSQL(`SET motherduck_token = '${escapedToken}';`);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const connectionString = `md:${config.database}`;
|
|
339
|
-
logger.info(
|
|
340
|
-
`Connecting to MotherDuck database: ${config.database} as ${attachedDb.name}`,
|
|
341
|
-
);
|
|
342
|
-
|
|
343
|
-
const attachCommand = `ATTACH '${connectionString}' AS ${attachedDb.name} (TYPE motherduck, READ_ONLY);`;
|
|
344
|
-
await connection.runSQL(attachCommand);
|
|
345
|
-
logger.info(`Successfully attached MotherDuck database: ${attachedDb.name}`);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
313
|
// Main attachment function
|
|
349
314
|
async function attachDatabasesToDuckDB(
|
|
350
315
|
duckdbConnection: DuckDBConnection,
|
|
@@ -354,7 +319,6 @@ async function attachDatabasesToDuckDB(
|
|
|
354
319
|
bigquery: attachBigQuery,
|
|
355
320
|
snowflake: attachSnowflake,
|
|
356
321
|
postgres: attachPostgres,
|
|
357
|
-
motherduck: attachMotherDuck,
|
|
358
322
|
};
|
|
359
323
|
|
|
360
324
|
for (const attachedDb of attachedDatabases) {
|
|
@@ -595,6 +559,37 @@ export async function createProjectConnections(
|
|
|
595
559
|
break;
|
|
596
560
|
}
|
|
597
561
|
|
|
562
|
+
case "motherduck": {
|
|
563
|
+
if (!connection.motherduckConnection) {
|
|
564
|
+
throw new Error(
|
|
565
|
+
"MotherDuck connection configuration is missing.",
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (!connection.motherduckConnection.accessToken) {
|
|
570
|
+
throw new Error("MotherDuck access token is required.");
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
let databasePath = `md:`;
|
|
574
|
+
// Build the MotherDuck database path
|
|
575
|
+
if (connection.motherduckConnection.database) {
|
|
576
|
+
databasePath = `md:${connection.motherduckConnection.database}?attach_mode=single`;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Create MotherDuck connection using DuckDBConnectionOptions interface
|
|
580
|
+
const motherduckConnection = new DuckDBConnection({
|
|
581
|
+
name: connection.name,
|
|
582
|
+
databasePath: databasePath,
|
|
583
|
+
motherDuckToken: connection.motherduckConnection.accessToken,
|
|
584
|
+
workingDirectory: projectPath,
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
connectionMap.set(connection.name, motherduckConnection);
|
|
588
|
+
connection.attributes =
|
|
589
|
+
getConnectionAttributes(motherduckConnection);
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
|
|
598
593
|
default: {
|
|
599
594
|
throw new Error(`Unsupported connection type: ${connection.type}`);
|
|
600
595
|
}
|
|
@@ -737,12 +732,6 @@ export async function testConnectionConfig(
|
|
|
737
732
|
|
|
738
733
|
// Use createProjectConnections to create the connection, then test it
|
|
739
734
|
// TODO: Test duckdb connections?
|
|
740
|
-
if (connectionConfig.type === "duckdb") {
|
|
741
|
-
return {
|
|
742
|
-
status: "ok",
|
|
743
|
-
errorMessage: "",
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
735
|
|
|
747
736
|
const { malloyConnections } = await createProjectConnections(
|
|
748
737
|
[connectionConfig], // Pass the single connection config
|
package/src/service/db_utils.ts
CHANGED
|
@@ -80,11 +80,20 @@ export async function getSchemasForConnection(
|
|
|
80
80
|
const bigquery = createBigQueryClient(connection);
|
|
81
81
|
const [datasets] = await bigquery.getDatasets();
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
const schemas = await Promise.all(
|
|
84
|
+
datasets.map(async (dataset) => {
|
|
85
|
+
const [metadata] = await dataset.getMetadata();
|
|
86
|
+
return {
|
|
87
|
+
name: dataset.id,
|
|
88
|
+
isHidden: false,
|
|
89
|
+
isDefault: false,
|
|
90
|
+
// Include description from dataset metadata if available
|
|
91
|
+
description: (metadata as { description?: string })
|
|
92
|
+
?.description,
|
|
93
|
+
};
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
return schemas;
|
|
88
97
|
} catch (error) {
|
|
89
98
|
console.error(
|
|
90
99
|
`Error getting schemas for BigQuery connection ${connection.name}:`,
|
|
@@ -251,6 +260,39 @@ export async function getSchemasForConnection(
|
|
|
251
260
|
`Failed to get schemas for DuckDB connection ${connection.name}: ${(error as Error).message}`,
|
|
252
261
|
);
|
|
253
262
|
}
|
|
263
|
+
} else if (connection.type === "motherduck") {
|
|
264
|
+
if (!connection.motherduckConnection) {
|
|
265
|
+
throw new Error("MotherDuck connection is required");
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
// Use MotherDuck's INFORMATION_SCHEMA.SCHEMATA to list schemas
|
|
269
|
+
const result = await malloyConnection.runSQL(
|
|
270
|
+
"SELECT DISTINCT schema_name as row FROM information_schema.schemata ORDER BY schema_name",
|
|
271
|
+
{ rowLimit: 1000 },
|
|
272
|
+
);
|
|
273
|
+
const rows = standardizeRunSQLResult(result);
|
|
274
|
+
console.log(rows);
|
|
275
|
+
return rows.map((row: unknown) => {
|
|
276
|
+
const typedRow = row as { row: string };
|
|
277
|
+
return {
|
|
278
|
+
name: typedRow.row,
|
|
279
|
+
isHidden: [
|
|
280
|
+
"information_schema",
|
|
281
|
+
"performance_schema",
|
|
282
|
+
"",
|
|
283
|
+
].includes(typedRow.row),
|
|
284
|
+
isDefault: false,
|
|
285
|
+
};
|
|
286
|
+
});
|
|
287
|
+
} catch (error) {
|
|
288
|
+
console.error(
|
|
289
|
+
`Error getting schemas for MotherDuck connection ${connection.name}:`,
|
|
290
|
+
error,
|
|
291
|
+
);
|
|
292
|
+
throw new Error(
|
|
293
|
+
`Failed to get schemas for MotherDuck connection ${connection.name}: ${(error as Error).message}`,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
254
296
|
} else {
|
|
255
297
|
throw new Error(`Unsupported connection type: ${connection.type}`);
|
|
256
298
|
}
|
|
@@ -500,8 +542,30 @@ export async function listTablesForSchema(
|
|
|
500
542
|
`Failed to get tables for DuckDB schema ${schemaName} in connection ${connection.name}: ${(error as Error).message}`,
|
|
501
543
|
);
|
|
502
544
|
}
|
|
545
|
+
} else if (connection.type === "motherduck") {
|
|
546
|
+
if (!connection.motherduckConnection) {
|
|
547
|
+
throw new Error("MotherDuck connection is required");
|
|
548
|
+
}
|
|
549
|
+
try {
|
|
550
|
+
const result = await malloyConnection.runSQL(
|
|
551
|
+
`SELECT table_name as row FROM information_schema.tables WHERE table_schema = '${schemaName}' ORDER BY table_name`,
|
|
552
|
+
{ rowLimit: 1000 },
|
|
553
|
+
);
|
|
554
|
+
const rows = standardizeRunSQLResult(result);
|
|
555
|
+
return rows.map((row: unknown) => {
|
|
556
|
+
const typedRow = row as { row: string };
|
|
557
|
+
return typedRow.row;
|
|
558
|
+
});
|
|
559
|
+
} catch (error) {
|
|
560
|
+
logger.error(
|
|
561
|
+
`Error getting tables for MotherDuck schema ${schemaName} in connection ${connection.name}`,
|
|
562
|
+
{ error },
|
|
563
|
+
);
|
|
564
|
+
throw new Error(
|
|
565
|
+
`Failed to get tables for MotherDuck schema ${schemaName} in connection ${connection.name}: ${(error as Error).message}`,
|
|
566
|
+
);
|
|
567
|
+
}
|
|
503
568
|
} else {
|
|
504
|
-
|
|
505
|
-
return [];
|
|
569
|
+
throw new Error(`Unsupported connection type: ${connection.type}`);
|
|
506
570
|
}
|
|
507
571
|
}
|