@malloy-publisher/server 0.0.173 → 0.0.175

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.
@@ -12,7 +12,7 @@
12
12
  href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"
13
13
  />
14
14
  <title>Malloy Publisher</title>
15
- <script type="module" crossorigin src="/assets/index-CGKwxuak.js"></script>
15
+ <script type="module" crossorigin src="/assets/index-ZpoRvcIi.js"></script>
16
16
  <link rel="stylesheet" crossorigin href="/assets/index-CMlGQMcl.css">
17
17
  </head>
18
18
  <body>
package/dist/server.js CHANGED
@@ -221167,6 +221167,10 @@ async function createProjectConnections(connections = [], projectPath = "", isUp
221167
221167
  password: connection.snowflakeConnection.password || undefined
221168
221168
  },
221169
221169
  timeout: connection.snowflakeConnection.responseTimeoutMilliseconds
221170
+ },
221171
+ poolOptions: {
221172
+ min: 1,
221173
+ max: 5
221170
221174
  }
221171
221175
  };
221172
221176
  const snowflakeConnection = new import_db_snowflake.SnowflakeConnection(connection.name, snowflakeConnectionOptions);
@@ -221827,10 +221831,14 @@ async function getSchemasForConnection(connection, malloyConnection) {
221827
221831
  const rows = standardizeRunSQLResult2(result);
221828
221832
  return rows.map((row) => {
221829
221833
  const typedRow = row;
221834
+ const databaseName = String(typedRow.database_name ?? typedRow.DATABASE_NAME ?? "");
221835
+ const name = String(typedRow.name ?? typedRow.NAME ?? "");
221836
+ const owner = String(typedRow.owner ?? typedRow.OWNER ?? "");
221837
+ const isDefaultVal = typedRow.is_default ?? typedRow.isDefault ?? typedRow.IS_DEFAULT;
221830
221838
  return {
221831
- name: typedRow.name,
221832
- isHidden: ["SNOWFLAKE", ""].includes(typedRow.owner),
221833
- isDefault: typedRow.isDefault === "Y"
221839
+ name: `${databaseName}.${name}`,
221840
+ isHidden: ["SNOWFLAKE", ""].includes(owner),
221841
+ isDefault: isDefaultVal === "Y"
221834
221842
  };
221835
221843
  });
221836
221844
  } catch (error) {
@@ -222147,7 +222155,7 @@ async function describeAzureFile(malloyConnection, fileUri, azureConnection) {
222147
222155
  throw new Error(`Failed to describe Azure file: ${error instanceof Error ? error.message : String(error)}`);
222148
222156
  }
222149
222157
  }
222150
- async function getTablesForSchema(connection, schemaName, malloyConnection) {
222158
+ async function getTablesForSchema(connection, schemaName, malloyConnection, fetchTableSchema = true) {
222151
222159
  if (connection.type === "duckdb") {
222152
222160
  const attachedDbs = connection.duckdbConnection?.attachedDatabases || [];
222153
222161
  const azureDb = attachedDbs.find((db) => db.type === "azure" && db.name === schemaName && db.azureConnection);
@@ -222199,10 +222207,13 @@ async function getTablesForSchema(connection, schemaName, malloyConnection) {
222199
222207
  tablePath = `${schemaName}.${tableName}`;
222200
222208
  }
222201
222209
  logger.info(`Processing table: ${tableName} in schema: ${schemaName}`, { tablePath, connectionType: connection.type });
222202
- const tableSource = await getConnectionTableSource(malloyConnection, tableName, tablePath);
222210
+ let tableSource;
222211
+ if (fetchTableSchema) {
222212
+ tableSource = await getConnectionTableSource(malloyConnection, tableName, tablePath);
222213
+ }
222203
222214
  return {
222204
222215
  resource: tablePath,
222205
- columns: tableSource.columns
222216
+ columns: tableSource?.columns || []
222206
222217
  };
222207
222218
  } catch (error) {
222208
222219
  logger.warn(`Failed to get schema for table ${tableName}`, {
@@ -222308,12 +222319,18 @@ async function listTablesForSchema(connection, schemaName, malloyConnection) {
222308
222319
  throw new Error("Snowflake connection is required");
222309
222320
  }
222310
222321
  try {
222311
- const result = await malloyConnection.runSQL(`SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${schemaName}' AND TABLE_TYPE = 'BASE TABLE'`);
222312
- const rows = standardizeRunSQLResult2(result);
222322
+ const tablesResult = await malloyConnection.runSQL(`SHOW TABLES IN SCHEMA ${schemaName} LIMIT 1000`);
222323
+ const viewsResult = await malloyConnection.runSQL(`SHOW VIEWS IN SCHEMA ${schemaName} LIMIT 1000`);
222324
+ const tableRows = standardizeRunSQLResult2(tablesResult);
222325
+ const viewRows = standardizeRunSQLResult2(viewsResult);
222326
+ logger.debug("Snowflake Tables Listed", { tableRows });
222327
+ logger.debug("Snowflake Views Listed", { viewRows });
222328
+ const rows = [...tableRows, ...viewRows];
222313
222329
  return rows.map((row) => {
222314
222330
  const typedRow = row;
222315
- return typedRow.TABLE_NAME;
222316
- });
222331
+ const name = typedRow.name ?? typedRow.NAME;
222332
+ return typeof name === "string" ? name : String(name);
222333
+ }).filter((id) => id.length > 0);
222317
222334
  } catch (error) {
222318
222335
  logger.error(`Error getting tables for Snowflake schema ${schemaName} in connection ${connection.name}`, { error });
222319
222336
  throw new Error(`Failed to get tables for Snowflake schema ${schemaName} in connection ${connection.name}: ${error.message}`);
@@ -222438,6 +222455,23 @@ var AZURE_DATA_EXTENSIONS = [
222438
222455
  ".jsonl",
222439
222456
  ".ndjson"
222440
222457
  ];
222458
+ function parseFetchTableSchemaQueryParam(raw) {
222459
+ if (raw === undefined || raw === null) {
222460
+ return true;
222461
+ }
222462
+ const v = Array.isArray(raw) ? raw[0] : raw;
222463
+ if (v === "" || v === undefined) {
222464
+ return true;
222465
+ }
222466
+ if (typeof v === "boolean") {
222467
+ return v;
222468
+ }
222469
+ const s = String(v).trim().toLowerCase();
222470
+ if (s === "false" || s === "0") {
222471
+ return false;
222472
+ }
222473
+ return true;
222474
+ }
222441
222475
  function validateAzureUrl(url2, fieldName) {
222442
222476
  if (!AZURE_SUPPORTED_SCHEMES.some((s) => url2.startsWith(s))) {
222443
222477
  throw new BadRequestError(`Azure ${fieldName} must use one of: ${AZURE_SUPPORTED_SCHEMES.join(", ")}`);
@@ -222518,11 +222552,11 @@ class ConnectionController {
222518
222552
  const malloyConnection = await this.getMalloyConnection(projectName, connectionName);
222519
222553
  return getSchemasForConnection(connection, malloyConnection);
222520
222554
  }
222521
- async listTables(projectName, connectionName, schemaName) {
222555
+ async listTables(projectName, connectionName, schemaName, fetchTableSchema = true) {
222522
222556
  const project = await this.projectStore.getProject(projectName, false);
222523
222557
  const connection = project.getApiConnection(connectionName);
222524
222558
  const malloyConnection = await this.getMalloyConnection(projectName, connectionName);
222525
- return getTablesForSchema(connection, schemaName, malloyConnection);
222559
+ return getTablesForSchema(connection, schemaName, malloyConnection, fetchTableSchema);
222526
222560
  }
222527
222561
  async getConnectionSqlSource(projectName, connectionName, sqlStatement) {
222528
222562
  const malloyConnection = await this.getMalloyConnection(projectName, connectionName);
@@ -235888,7 +235922,7 @@ app.get(`${API_PREFIX2}/projects/:projectName/connections/:connectionName/schema
235888
235922
  app.get(`${API_PREFIX2}/projects/:projectName/connections/:connectionName/schemas/:schemaName/tables`, async (req, res) => {
235889
235923
  logger.info("req.params", { params: req.params });
235890
235924
  try {
235891
- const results = await connectionController.listTables(req.params.projectName, req.params.connectionName, req.params.schemaName);
235925
+ const results = await connectionController.listTables(req.params.projectName, req.params.connectionName, req.params.schemaName, parseFetchTableSchemaQueryParam(req.query.fetchTableSchema));
235892
235926
  res.status(200).json(results);
235893
235927
  } catch (error) {
235894
235928
  logger.error(error);
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.173",
4
+ "version": "0.0.175",
5
5
  "main": "dist/server.js",
6
6
  "bin": {
7
7
  "malloy-publisher": "dist/server.js"
@@ -32,15 +32,15 @@
32
32
  "@azure/identity": "^4.13.0",
33
33
  "@azure/storage-blob": "^12.26.0",
34
34
  "@google-cloud/storage": "^7.16.0",
35
- "@malloydata/db-bigquery": "^0.0.358",
36
- "@malloydata/db-duckdb": "^0.0.358",
37
- "@malloydata/db-mysql": "^0.0.358",
38
- "@malloydata/db-postgres": "^0.0.358",
39
- "@malloydata/db-snowflake": "^0.0.358",
40
- "@malloydata/db-trino": "^0.0.358",
41
- "@malloydata/malloy": "^0.0.358",
42
- "@malloydata/malloy-sql": "^0.0.358",
43
- "@malloydata/render-validator": "^0.0.358",
35
+ "@malloydata/db-bigquery": "^0.0.369",
36
+ "@malloydata/db-duckdb": "^0.0.369",
37
+ "@malloydata/db-mysql": "^0.0.369",
38
+ "@malloydata/db-postgres": "^0.0.369",
39
+ "@malloydata/db-snowflake": "^0.0.369",
40
+ "@malloydata/db-trino": "^0.0.369",
41
+ "@malloydata/malloy": "^0.0.369",
42
+ "@malloydata/malloy-sql": "^0.0.369",
43
+ "@malloydata/render-validator": "^0.0.369",
44
44
  "@modelcontextprotocol/sdk": "^1.13.2",
45
45
  "@opentelemetry/api": "^1.9.0",
46
46
  "@opentelemetry/auto-instrumentations-node": "^0.57.0",
@@ -29,6 +29,28 @@ const AZURE_DATA_EXTENSIONS = [
29
29
  ".ndjson",
30
30
  ];
31
31
 
32
+ /**
33
+ * `fetchTableSchema` query param: default true when omitted, null, or empty.
34
+ * Only explicit false/0 disables schema fetch.
35
+ */
36
+ export function parseFetchTableSchemaQueryParam(raw: unknown): boolean {
37
+ if (raw === undefined || raw === null) {
38
+ return true;
39
+ }
40
+ const v = Array.isArray(raw) ? raw[0] : raw;
41
+ if (v === "" || v === undefined) {
42
+ return true;
43
+ }
44
+ if (typeof v === "boolean") {
45
+ return v;
46
+ }
47
+ const s = String(v).trim().toLowerCase();
48
+ if (s === "false" || s === "0") {
49
+ return false;
50
+ }
51
+ return true;
52
+ }
53
+
32
54
  /**
33
55
  * Validates an Azure URL against the three supported patterns:
34
56
  * 1. Single file: path/file.parquet
@@ -161,6 +183,7 @@ export class ConnectionController {
161
183
  projectName: string,
162
184
  connectionName: string,
163
185
  schemaName: string,
186
+ fetchTableSchema = true,
164
187
  ): Promise<ApiTable[]> {
165
188
  const project = await this.projectStore.getProject(projectName, false);
166
189
  const connection = project.getApiConnection(connectionName);
@@ -169,7 +192,12 @@ export class ConnectionController {
169
192
  connectionName,
170
193
  );
171
194
 
172
- return getTablesForSchema(connection, schemaName, malloyConnection);
195
+ return getTablesForSchema(
196
+ connection,
197
+ schemaName,
198
+ malloyConnection,
199
+ fetchTableSchema,
200
+ );
173
201
  }
174
202
 
175
203
  public async getConnectionSqlSource(
package/src/server.ts CHANGED
@@ -14,7 +14,10 @@ import { createProxyMiddleware } from "http-proxy-middleware";
14
14
  import { AddressInfo } from "net";
15
15
  import * as path from "path";
16
16
  import { CompileController } from "./controller/compile.controller";
17
- import { ConnectionController } from "./controller/connection.controller";
17
+ import {
18
+ ConnectionController,
19
+ parseFetchTableSchemaQueryParam,
20
+ } from "./controller/connection.controller";
18
21
  import { DatabaseController } from "./controller/database.controller";
19
22
  import { ModelController } from "./controller/model.controller";
20
23
  import { PackageController } from "./controller/package.controller";
@@ -467,6 +470,7 @@ app.get(
467
470
  req.params.projectName,
468
471
  req.params.connectionName,
469
472
  req.params.schemaName,
473
+ parseFetchTableSchemaQueryParam(req.query.fetchTableSchema),
470
474
  );
471
475
  res.status(200).json(results);
472
476
  } catch (error) {
@@ -1091,6 +1091,10 @@ export async function createProjectConnections(
1091
1091
  timeout:
1092
1092
  connection.snowflakeConnection.responseTimeoutMilliseconds,
1093
1093
  },
1094
+ poolOptions: {
1095
+ min: 1,
1096
+ max: 5,
1097
+ },
1094
1098
  };
1095
1099
  const snowflakeConnection = new SnowflakeConnection(
1096
1100
  connection.name,
@@ -199,14 +199,20 @@ export async function getSchemasForConnection(
199
199
  try {
200
200
  // Use the connection's runSQL method to query schemas
201
201
  const result = await malloyConnection.runSQL("SHOW SCHEMAS");
202
-
203
202
  const rows = standardizeRunSQLResult(result);
204
203
  return rows.map((row: unknown) => {
205
204
  const typedRow = row as Record<string, unknown>;
205
+ const databaseName = String(
206
+ typedRow.database_name ?? typedRow.DATABASE_NAME ?? "",
207
+ );
208
+ const name = String(typedRow.name ?? typedRow.NAME ?? "");
209
+ const owner = String(typedRow.owner ?? typedRow.OWNER ?? "");
210
+ const isDefaultVal =
211
+ typedRow.is_default ?? typedRow.isDefault ?? typedRow.IS_DEFAULT;
206
212
  return {
207
- name: typedRow.name as string,
208
- isHidden: ["SNOWFLAKE", ""].includes(typedRow.owner as string),
209
- isDefault: typedRow.isDefault === "Y",
213
+ name: `${databaseName}.${name}`,
214
+ isHidden: ["SNOWFLAKE", ""].includes(owner),
215
+ isDefault: isDefaultVal === "Y",
210
216
  };
211
217
  });
212
218
  } catch (error) {
@@ -693,6 +699,7 @@ export async function getTablesForSchema(
693
699
  connection: ApiConnection,
694
700
  schemaName: string,
695
701
  malloyConnection: Connection,
702
+ fetchTableSchema = true,
696
703
  ): Promise<ApiTable[]> {
697
704
  // Check if schemaName matches an Azure attached database name
698
705
  if (connection.type === "duckdb") {
@@ -798,14 +805,17 @@ export async function getTablesForSchema(
798
805
  `Processing table: ${tableName} in schema: ${schemaName}`,
799
806
  { tablePath, connectionType: connection.type },
800
807
  );
801
- const tableSource = await getConnectionTableSource(
802
- malloyConnection,
803
- tableName,
804
- tablePath,
805
- );
808
+ let tableSource: ApiTableSource | undefined;
809
+ if (fetchTableSchema) {
810
+ tableSource = await getConnectionTableSource(
811
+ malloyConnection,
812
+ tableName,
813
+ tablePath,
814
+ );
815
+ }
806
816
  return {
807
817
  resource: tablePath,
808
- columns: tableSource.columns,
818
+ columns: tableSource?.columns || [],
809
819
  };
810
820
  } catch (error) {
811
821
  logger.warn(`Failed to get schema for table ${tableName}`, {
@@ -976,14 +986,26 @@ export async function listTablesForSchema(
976
986
  throw new Error("Snowflake connection is required");
977
987
  }
978
988
  try {
979
- const result = await malloyConnection.runSQL(
980
- `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${schemaName}' AND TABLE_TYPE = 'BASE TABLE'`,
989
+ // TODO: Switch to INFORMATION_SCHEMA.TABLES and INFORMATION_SCHEMA.VIEWS, with pagination support implemented in both backend and the frontend.
990
+ // Note: LIMIT 1000 is a temporary workaround to avoid pagination.
991
+ const tablesResult = await malloyConnection.runSQL(
992
+ `SHOW TABLES IN SCHEMA ${schemaName} LIMIT 1000`,
981
993
  );
982
- const rows = standardizeRunSQLResult(result);
983
- return rows.map((row: unknown) => {
984
- const typedRow = row as Record<string, unknown>;
985
- return typedRow.TABLE_NAME as string;
986
- });
994
+ const viewsResult = await malloyConnection.runSQL(
995
+ `SHOW VIEWS IN SCHEMA ${schemaName} LIMIT 1000`,
996
+ );
997
+ const tableRows = standardizeRunSQLResult(tablesResult);
998
+ const viewRows = standardizeRunSQLResult(viewsResult);
999
+ logger.debug("Snowflake Tables Listed", { tableRows });
1000
+ logger.debug("Snowflake Views Listed", { viewRows });
1001
+ const rows = [...tableRows, ...viewRows];
1002
+ return rows
1003
+ .map((row: unknown) => {
1004
+ const typedRow = row as Record<string, unknown>;
1005
+ const name = typedRow.name ?? typedRow.NAME;
1006
+ return typeof name === "string" ? name : String(name);
1007
+ })
1008
+ .filter((id) => id.length > 0);
987
1009
  } catch (error) {
988
1010
  logger.error(
989
1011
  `Error getting tables for Snowflake schema ${schemaName} in connection ${connection.name}`,
@@ -1 +0,0 @@
1
- import{u as o,j as a,i as e}from"./index-CGKwxuak.js";function i(){const t=o();return a.jsx(e,{onClickProject:t})}export{i as default};
@@ -1 +0,0 @@
1
- import{t as n,j as e,D as i,E as t,F as c}from"./index-CGKwxuak.js";function o(){const a=n(),s=a["*"];if(!a.projectName)return e.jsx("div",{children:e.jsx("h2",{children:"Missing project name"})});if(!a.packageName)return e.jsx("div",{children:e.jsx("h2",{children:"Missing package name"})});const r=i({projectName:a.projectName,packageName:a.packageName,modelPath:s});return s?.endsWith(".malloy")?e.jsx(t,{resourceUri:r,runOnDemand:!0,maxResultSize:512*1024}):s?.endsWith(".malloynb")?e.jsx(c,{resourceUri:r,maxResultSize:1024*1024}):e.jsx("div",{children:e.jsxs("h2",{children:["Unrecognized file type: ",s]})})}export{o as default};
@@ -1 +0,0 @@
1
- import{t as a,j as e,D as t,K as c}from"./index-CGKwxuak.js";function d(){const{workspace:s,workbookPath:r,projectName:i,packageName:n}=a();if(s)if(r)if(i)if(n){const o=t({projectName:i,packageName:n});return e.jsx(c,{workbookPath:{path:r,workspace:s},resourceUri:o},`${r}`)}else return e.jsx("div",{children:e.jsx("h2",{children:"Missing package name"})});else return e.jsx("div",{children:e.jsx("h2",{children:"Missing project name"})});else return e.jsx("div",{children:e.jsx("h2",{children:"Missing workbook path"})});else return e.jsx("div",{children:e.jsx("h2",{children:"Missing workspace"})})}export{d as default};