@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.
- package/dist/app/api-doc.yaml +324 -335
- package/dist/app/assets/{HomePage-BxFnfH3M.js → HomePage-z6NLKLPp.js} +1 -1
- package/dist/app/assets/{MainPage-D301Y0mT.js → MainPage-C9McOjLb.js} +2 -2
- package/dist/app/assets/{ModelPage-Df8ivC1J.js → ModelPage-DjlTuT2G.js} +1 -1
- package/dist/app/assets/{PackagePage-CE41SCV_.js → PackagePage-CDh_gnAZ.js} +1 -1
- package/dist/app/assets/ProjectPage-vyvZZWAB.js +1 -0
- package/dist/app/assets/{RouteError-l_WGtNhS.js → RouteError-FbxztVnz.js} +1 -1
- package/dist/app/assets/{WorkbookPage-CY-1oBvt.js → WorkbookPage-DNXFxaeZ.js} +1 -1
- package/dist/app/assets/{index-D5BBaLz8.js → index-BMyI9XZS.js} +1 -1
- package/dist/app/assets/{index-DlZbNvNc.js → index-DHFp2DLx.js} +1 -1
- package/dist/app/assets/{index-DjbXd602.js → index-a6hx_UrL.js} +113 -113
- package/dist/app/assets/{index.umd-DQiSWsWe.js → index.umd-Cv1NyZL8.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/server.js +35395 -144722
- package/k6-tests/common.ts +12 -3
- package/package.json +1 -1
- package/src/controller/connection.controller.ts +82 -72
- package/src/controller/query.controller.ts +1 -1
- package/src/server.ts +6 -48
- package/src/service/connection.ts +384 -305
- package/src/service/db_utils.ts +416 -301
- package/src/service/package.spec.ts +8 -97
- package/src/service/package.ts +24 -46
- package/src/service/project.ts +8 -24
- package/src/service/project_store.ts +0 -1
- package/dist/app/assets/ProjectPage-DA66xbmQ.js +0 -1
- package/src/controller/schedule.controller.ts +0 -21
- package/src/service/scheduler.ts +0 -190
package/k6-tests/common.ts
CHANGED
|
@@ -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.
|
|
141
|
-
`${PUBLISHER_URL}/api/v0/projects/home/packages/${encodedPackageName}/
|
|
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,25 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
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 {
|
|
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
|
|
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<
|
|
76
|
+
): Promise<ApiSchema[]> {
|
|
48
77
|
const project = await this.projectStore.getProject(projectName, false);
|
|
49
78
|
const connection = project.getApiConnection(connectionName);
|
|
50
|
-
|
|
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<
|
|
92
|
+
): Promise<ApiTable[]> {
|
|
59
93
|
const project = await this.projectStore.getProject(projectName, false);
|
|
60
94
|
const connection = project.getApiConnection(connectionName);
|
|
61
|
-
|
|
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
|
-
)
|
|
72
|
-
|
|
73
|
-
|
|
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
|
|
95
|
-
|
|
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
|
|
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
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
148
|
-
|
|
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
|
|
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
|
|
176
|
-
|
|
185
|
+
const malloyConnection = await this.getMalloyConnection(
|
|
186
|
+
projectName,
|
|
177
187
|
connectionName,
|
|
178
|
-
)
|
|
188
|
+
);
|
|
179
189
|
|
|
180
190
|
try {
|
|
181
191
|
return {
|
|
182
192
|
table: JSON.stringify(
|
|
183
|
-
await (
|
|
184
|
-
|
|
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}/
|
|
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.
|
|
719
|
-
`${API_PREFIX}/projects/:projectName/packages/:packageName/
|
|
699
|
+
app.post(
|
|
700
|
+
`${API_PREFIX}/projects/:projectName/packages/:packageName/models/*?/query`,
|
|
720
701
|
async (req, res) => {
|
|
721
|
-
if (req.
|
|
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.
|
|
734
|
-
req.
|
|
735
|
-
req.
|
|
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) {
|