@malloy-publisher/server 0.0.119 → 0.0.121

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.
Files changed (28) hide show
  1. package/dist/app/api-doc.yaml +324 -335
  2. package/dist/app/assets/{HomePage-BxFnfH3M.js → HomePage-z6NLKLPp.js} +1 -1
  3. package/dist/app/assets/{MainPage-D301Y0mT.js → MainPage-C9McOjLb.js} +2 -2
  4. package/dist/app/assets/{ModelPage-Df8ivC1J.js → ModelPage-DjlTuT2G.js} +1 -1
  5. package/dist/app/assets/{PackagePage-CE41SCV_.js → PackagePage-CDh_gnAZ.js} +1 -1
  6. package/dist/app/assets/ProjectPage-vyvZZWAB.js +1 -0
  7. package/dist/app/assets/{RouteError-l_WGtNhS.js → RouteError-FbxztVnz.js} +1 -1
  8. package/dist/app/assets/{WorkbookPage-CY-1oBvt.js → WorkbookPage-DNXFxaeZ.js} +1 -1
  9. package/dist/app/assets/{index-D5BBaLz8.js → index-BMyI9XZS.js} +1 -1
  10. package/dist/app/assets/{index-DlZbNvNc.js → index-DHFp2DLx.js} +1 -1
  11. package/dist/app/assets/{index-DjbXd602.js → index-a6hx_UrL.js} +113 -113
  12. package/dist/app/assets/{index.umd-DQiSWsWe.js → index.umd-Cv1NyZL8.js} +1 -1
  13. package/dist/app/index.html +1 -1
  14. package/dist/server.js +35395 -144722
  15. package/k6-tests/common.ts +12 -3
  16. package/package.json +1 -1
  17. package/src/controller/connection.controller.ts +82 -72
  18. package/src/controller/query.controller.ts +1 -1
  19. package/src/server.ts +6 -48
  20. package/src/service/connection.ts +384 -305
  21. package/src/service/db_utils.ts +416 -301
  22. package/src/service/package.spec.ts +8 -97
  23. package/src/service/package.ts +24 -46
  24. package/src/service/project.ts +8 -24
  25. package/src/service/project_store.ts +0 -1
  26. package/dist/app/assets/ProjectPage-DA66xbmQ.js +0 -1
  27. package/src/controller/schedule.controller.ts +0 -21
  28. package/src/service/scheduler.ts +0 -190
@@ -1,5 +1,5 @@
1
- import http from "k6/http";
2
1
  import { check } from "k6";
2
+ import http from "k6/http";
3
3
 
4
4
  type ApiPackage = {
5
5
  name: string;
@@ -137,7 +137,16 @@ export const queryModelView = (
137
137
  const encodedModelPath = encodeURIComponent(modelPath);
138
138
  const encodedSourceName = encodeURIComponent(sourceName);
139
139
  const encodedQueryName = encodeURIComponent(queryName);
140
- return http.get(
141
- `${PUBLISHER_URL}/api/v0/projects/home/packages/${encodedPackageName}/queryResults/${encodedModelPath}?sourceName=${encodedSourceName}&queryName=${encodedQueryName}`,
140
+ return http.post(
141
+ `${PUBLISHER_URL}/api/v0/projects/home/packages/${encodedPackageName}/models/${encodedModelPath}/query`,
142
+ JSON.stringify({
143
+ sourceName: sourceName,
144
+ queryName: queryName,
145
+ }),
146
+ {
147
+ headers: {
148
+ "Content-Type": "application/json",
149
+ },
150
+ },
142
151
  );
143
152
  };
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.119",
4
+ "version": "0.0.121",
5
5
  "main": "dist/server.js",
6
6
  "bin": {
7
7
  "malloy-publisher": "dist/server.js"
@@ -1,25 +1,23 @@
1
- import {
2
- RunSQLOptions,
3
- TableSourceDef,
4
- TestableConnection,
5
- } from "@malloydata/malloy";
6
- import { Connection, PersistSQLResults } from "@malloydata/malloy/connection";
1
+ import { Connection, RunSQLOptions } from "@malloydata/malloy";
2
+ import { PersistSQLResults } from "@malloydata/malloy/connection";
3
+ import { components } from "../api";
7
4
  import { BadRequestError, ConnectionError } from "../errors";
8
5
  import { logger } from "../logger";
9
- import { components } from "../api";
6
+ import { testConnectionConfig } from "../service/connection";
10
7
  import {
8
+ getConnectionTableSource,
11
9
  getSchemasForConnection,
12
10
  getTablesForSchema,
13
11
  } from "../service/db_utils";
14
12
  import { ProjectStore } from "../service/project_store";
15
- import { testConnectionConfig } from "../service/connection";
16
13
  type ApiConnection = components["schemas"]["Connection"];
17
14
  type ApiConnectionStatus = components["schemas"]["ConnectionStatus"];
18
15
  type ApiSqlSource = components["schemas"]["SqlSource"];
19
16
  type ApiTableSource = components["schemas"]["TableSource"];
17
+ type ApiTable = components["schemas"]["Table"];
20
18
  type ApiQueryData = components["schemas"]["QueryData"];
21
19
  type ApiTemporaryTable = components["schemas"]["TemporaryTable"];
22
- type ApiSchemaName = components["schemas"]["SchemaName"];
20
+ type ApiSchema = components["schemas"]["Schema"];
23
21
  export class ConnectionController {
24
22
  private projectStore: ProjectStore;
25
23
 
@@ -27,6 +25,37 @@ export class ConnectionController {
27
25
  this.projectStore = projectStore;
28
26
  }
29
27
 
28
+ /**
29
+ * Gets the appropriate Malloy connection for a given connection name.
30
+ * For DuckDB connections, retrieves from package level; for others, from project level.
31
+ */
32
+ private async getMalloyConnection(
33
+ projectName: string,
34
+ connectionName: string,
35
+ ): Promise<Connection> {
36
+ const project = await this.projectStore.getProject(projectName, false);
37
+ const connection = project.getApiConnection(connectionName);
38
+
39
+ // For DuckDB connections, get the connection from a package
40
+ if (connection.type === "duckdb") {
41
+ const packages = await project.listPackages();
42
+ if (packages.length === 0) {
43
+ throw new ConnectionError(
44
+ "No packages found for DuckDB connection",
45
+ );
46
+ }
47
+ // For now, use the first package's DuckDB connection
48
+ const packageName = packages[0].name;
49
+ if (!packageName) {
50
+ throw new ConnectionError("Package name is undefined");
51
+ }
52
+ const pkg = await project.getPackage(packageName);
53
+ return pkg.getMalloyConnection(connectionName);
54
+ } else {
55
+ return project.getMalloyConnection(connectionName);
56
+ }
57
+ }
58
+
30
59
  public async getConnection(
31
60
  projectName: string,
32
61
  connectionName: string,
@@ -44,10 +73,15 @@ export class ConnectionController {
44
73
  public async listSchemas(
45
74
  projectName: string,
46
75
  connectionName: string,
47
- ): Promise<ApiSchemaName[]> {
76
+ ): Promise<ApiSchema[]> {
48
77
  const project = await this.projectStore.getProject(projectName, false);
49
78
  const connection = project.getApiConnection(connectionName);
50
- return getSchemasForConnection(connection);
79
+ const malloyConnection = await this.getMalloyConnection(
80
+ projectName,
81
+ connectionName,
82
+ );
83
+
84
+ return getSchemasForConnection(connection, malloyConnection);
51
85
  }
52
86
 
53
87
  // Lists tables available in a schema. For postgres the schema is usually "public"
@@ -55,35 +89,15 @@ export class ConnectionController {
55
89
  projectName: string,
56
90
  connectionName: string,
57
91
  schemaName: string,
58
- ): Promise<string[]> {
92
+ ): Promise<ApiTable[]> {
59
93
  const project = await this.projectStore.getProject(projectName, false);
60
94
  const connection = project.getApiConnection(connectionName);
61
- return getTablesForSchema(connection, schemaName);
62
- }
63
-
64
- public async testConnection(
65
- projectName: string,
66
- connectionName: string,
67
- ): Promise<ApiConnectionStatus> {
68
- const project = await this.projectStore.getProject(projectName, false);
69
- const connection = project.getMalloyConnection(
95
+ const malloyConnection = await this.getMalloyConnection(
96
+ projectName,
70
97
  connectionName,
71
- ) as Connection;
72
- try {
73
- await (connection as TestableConnection).test();
74
- return {
75
- status: "ok",
76
- errorMessage: "",
77
- };
78
- } catch (error) {
79
- if (error instanceof ConnectionError) {
80
- return {
81
- status: "failed",
82
- errorMessage: error.message,
83
- };
84
- }
85
- throw error;
86
- }
98
+ );
99
+
100
+ return getTablesForSchema(connection, schemaName, malloyConnection);
87
101
  }
88
102
 
89
103
  public async getConnectionSqlSource(
@@ -91,12 +105,22 @@ export class ConnectionController {
91
105
  connectionName: string,
92
106
  sqlStatement: string,
93
107
  ): Promise<ApiSqlSource> {
94
- const project = await this.projectStore.getProject(projectName, false);
95
- const connection = project.getMalloyConnection(connectionName);
108
+ const malloyConnection = await this.getMalloyConnection(
109
+ projectName,
110
+ connectionName,
111
+ );
112
+
96
113
  try {
97
114
  return {
98
115
  source: JSON.stringify(
99
- await connection.fetchSelectSchema({
116
+ await (
117
+ malloyConnection as Connection & {
118
+ fetchSelectSchema: (params: {
119
+ connection: string;
120
+ selectStr: string;
121
+ }) => Promise<unknown>;
122
+ }
123
+ ).fetchSelectSchema({
100
124
  connection: connectionName,
101
125
  selectStr: sqlStatement,
102
126
  }),
@@ -113,29 +137,12 @@ export class ConnectionController {
113
137
  tableKey: string,
114
138
  tablePath: string,
115
139
  ): Promise<ApiTableSource> {
116
- const project = await this.projectStore.getProject(projectName, false);
117
- const connection = project.getMalloyConnection(connectionName);
118
- try {
119
- const source = await connection.fetchTableSchema(tableKey, tablePath);
120
- if (source === undefined) {
121
- throw new ConnectionError(`Table ${tablePath} not found`);
122
- }
123
- const malloyFields = (source as TableSourceDef).fields;
124
- const fields = malloyFields.map((field) => {
125
- return {
126
- name: field.name,
127
- type: field.type,
128
- };
129
- });
130
- return {
131
- source: JSON.stringify(source),
132
- resource: tablePath,
133
- columns: fields,
134
- };
135
- } catch (error) {
136
- logger.error("error", { error });
137
- throw new ConnectionError((error as Error).message);
138
- }
140
+ const malloyConnection = await this.getMalloyConnection(
141
+ projectName,
142
+ connectionName,
143
+ );
144
+
145
+ return getConnectionTableSource(malloyConnection, tableKey, tablePath);
139
146
  }
140
147
 
141
148
  public async getConnectionQueryData(
@@ -144,8 +151,11 @@ export class ConnectionController {
144
151
  sqlStatement: string,
145
152
  options: string,
146
153
  ): Promise<ApiQueryData> {
147
- const project = await this.projectStore.getProject(projectName, false);
148
- const connection = project.getMalloyConnection(connectionName);
154
+ const malloyConnection = await this.getMalloyConnection(
155
+ projectName,
156
+ connectionName,
157
+ );
158
+
149
159
  let runSQLOptions: RunSQLOptions = {};
150
160
  if (options) {
151
161
  runSQLOptions = JSON.parse(options) as RunSQLOptions;
@@ -159,7 +169,7 @@ export class ConnectionController {
159
169
  try {
160
170
  return {
161
171
  data: JSON.stringify(
162
- await connection.runSQL(sqlStatement, runSQLOptions),
172
+ await malloyConnection.runSQL(sqlStatement, runSQLOptions),
163
173
  ),
164
174
  };
165
175
  } catch (error) {
@@ -172,17 +182,17 @@ export class ConnectionController {
172
182
  connectionName: string,
173
183
  sqlStatement: string,
174
184
  ): Promise<ApiTemporaryTable> {
175
- const project = await this.projectStore.getProject(projectName, false);
176
- const connection = project.getMalloyConnection(
185
+ const malloyConnection = await this.getMalloyConnection(
186
+ projectName,
177
187
  connectionName,
178
- ) as Connection;
188
+ );
179
189
 
180
190
  try {
181
191
  return {
182
192
  table: JSON.stringify(
183
- await (connection as PersistSQLResults).manifestTemporaryTable(
184
- sqlStatement,
185
- ),
193
+ await (
194
+ malloyConnection as PersistSQLResults
195
+ ).manifestTemporaryTable(sqlStatement),
186
196
  ),
187
197
  };
188
198
  } catch (error) {
@@ -34,7 +34,7 @@ export class QueryController {
34
34
  );
35
35
  return {
36
36
  result: JSON.stringify(result),
37
- resource: `${API_PREFIX}/projects/${projectName}/packages/${packageName}/queryResults/${modelPath}`,
37
+ resource: `${API_PREFIX}/projects/${projectName}/packages/${packageName}/models/${modelPath}/query`,
38
38
  } as ApiQuery;
39
39
  }
40
40
  }
package/src/server.ts CHANGED
@@ -11,7 +11,6 @@ import { DatabaseController } from "./controller/database.controller";
11
11
  import { ModelController } from "./controller/model.controller";
12
12
  import { PackageController } from "./controller/package.controller";
13
13
  import { QueryController } from "./controller/query.controller";
14
- import { ScheduleController } from "./controller/schedule.controller";
15
14
  import { WatchModeController } from "./controller/watch-mode.controller";
16
15
  import { internalErrorToHttpError, NotImplementedError } from "./errors";
17
16
  import { logger, loggerMiddleware } from "./logger";
@@ -89,7 +88,6 @@ const modelController = new ModelController(projectStore);
89
88
  const packageController = new PackageController(projectStore);
90
89
  const databaseController = new DatabaseController(projectStore);
91
90
  const queryController = new QueryController(projectStore);
92
- const scheduleController = new ScheduleController(projectStore);
93
91
 
94
92
  export const mcpApp = express();
95
93
 
@@ -327,23 +325,6 @@ app.post(`${API_PREFIX}/connections/test`, async (req, res) => {
327
325
  }
328
326
  });
329
327
 
330
- app.get(
331
- `${API_PREFIX}/projects/:projectName/connections/:connectionName/test`,
332
- async (req, res) => {
333
- try {
334
- const connectionStatus = await connectionController.testConnection(
335
- req.params.projectName,
336
- req.params.connectionName,
337
- );
338
- res.status(200).json(connectionStatus);
339
- } catch (error) {
340
- logger.error(error);
341
- const { json, status } = internalErrorToHttpError(error as Error);
342
- res.status(status).json(json);
343
- }
344
- },
345
- );
346
-
347
328
  app.get(
348
329
  `${API_PREFIX}/projects/:projectName/connections/:connectionName/schemas`,
349
330
  async (req, res) => {
@@ -715,10 +696,10 @@ app.get(
715
696
  },
716
697
  );
717
698
 
718
- app.get(
719
- `${API_PREFIX}/projects/:projectName/packages/:packageName/queryResults/*?`,
699
+ app.post(
700
+ `${API_PREFIX}/projects/:projectName/packages/:packageName/models/*?/query`,
720
701
  async (req, res) => {
721
- if (req.query.versionId) {
702
+ if (req.body.versionId) {
722
703
  setVersionIdError(res);
723
704
  return;
724
705
  }
@@ -730,32 +711,9 @@ app.get(
730
711
  req.params.projectName,
731
712
  req.params.packageName,
732
713
  req.params[zero as keyof typeof req.params],
733
- req.query.sourceName as string,
734
- req.query.queryName as string,
735
- req.query.query as string,
736
- ),
737
- );
738
- } catch (error) {
739
- logger.error(error);
740
- const { json, status } = internalErrorToHttpError(error as Error);
741
- res.status(status).json(json);
742
- }
743
- },
744
- );
745
-
746
- app.get(
747
- `${API_PREFIX}/projects/:projectName/packages/:packageName/schedules`,
748
- async (req, res) => {
749
- if (req.query.versionId) {
750
- setVersionIdError(res);
751
- return;
752
- }
753
-
754
- try {
755
- res.status(200).json(
756
- await scheduleController.listSchedules(
757
- req.params.projectName,
758
- req.params.packageName,
714
+ req.body.sourceName as string,
715
+ req.body.queryName as string,
716
+ req.body.query as string,
759
717
  ),
760
718
  );
761
719
  } catch (error) {