@malloy-publisher/server 0.0.80 → 0.0.81
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/assets/{RenderedResult-BAZuT25g-BCBDzvJl.js → RenderedResult-BAZuT25g-DO0bulFJ.js} +2 -2
- package/dist/app/assets/{index-89qYWF11.js → index-9YQno_QP.js} +1 -1
- package/dist/app/assets/{index-CKfraTxm.js → index-B3C-uSNk.js} +1 -1
- package/dist/app/assets/{index-DtRl_5zk.js → index-D2AowBDH.js} +114 -114
- package/dist/app/assets/{index.umd-DoxV6txm.js → index.umd-DAKfNegB.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/instrumentation.js +10262 -3
- package/dist/server.js +149448 -150333
- package/package.json +1 -1
- package/src/controller/connection.controller.ts +3 -2
- package/src/data_styles.ts +2 -1
- package/src/instrumentation.ts +5 -5
- package/src/logger.ts +46 -0
- package/src/mcp/handler_utils.ts +7 -6
- package/src/mcp/prompts/prompt_service.ts +12 -12
- package/src/mcp/resources/notebook_resource.ts +11 -10
- package/src/mcp/resources/package_resource.ts +21 -20
- package/src/mcp/resources/project_resource.ts +22 -23
- package/src/mcp/resources/query_resource.ts +12 -11
- package/src/mcp/resources/source_resource.ts +12 -11
- package/src/mcp/resources/view_resource.ts +12 -11
- package/src/mcp/server.ts +22 -21
- package/src/mcp/tools/execute_query_tool.ts +8 -11
- package/src/server.ts +41 -45
- package/src/service/db_utils.ts +17 -17
- package/src/service/model.ts +2 -1
- package/src/service/package.ts +3 -2
- package/src/service/project_store.ts +7 -7
- package/src/service/scheduler.ts +4 -3
package/src/mcp/server.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { ProjectStore } from "../service/project_store";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { logger } from "../logger";
|
|
5
|
+
import { registerPromptCapability } from "./prompts/prompt_service.js";
|
|
6
6
|
import { registerModelResource } from "./resources/model_resource";
|
|
7
|
-
import {
|
|
7
|
+
import { registerNotebookResource } from "./resources/notebook_resource";
|
|
8
|
+
import { registerPackageResource } from "./resources/package_resource";
|
|
9
|
+
import { registerProjectResource } from "./resources/project_resource";
|
|
8
10
|
import { registerQueryResource } from "./resources/query_resource";
|
|
11
|
+
import { registerSourceResource } from "./resources/source_resource";
|
|
9
12
|
import { registerViewResource } from "./resources/view_resource";
|
|
10
|
-
import { registerNotebookResource } from "./resources/notebook_resource";
|
|
11
|
-
import { registerExecuteQueryTool } from "./tools/execute_query_tool";
|
|
12
13
|
import { registerTools } from "./tools/discovery_tools";
|
|
13
|
-
import {
|
|
14
|
+
import { registerExecuteQueryTool } from "./tools/execute_query_tool";
|
|
14
15
|
|
|
15
16
|
export const testServerInfo = {
|
|
16
17
|
name: "malloy-publisher-mcp-server",
|
|
@@ -20,42 +21,42 @@ export const testServerInfo = {
|
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
export function initializeMcpServer(projectStore: ProjectStore): McpServer {
|
|
23
|
-
|
|
24
|
-
const startTime =
|
|
24
|
+
logger.info("[MCP Init] Starting initializeMcpServer...");
|
|
25
|
+
const startTime = performance.now();
|
|
25
26
|
|
|
26
27
|
const mcpServer = new McpServer(testServerInfo);
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
logger.info("[MCP Init] Registering project resource...");
|
|
29
30
|
registerProjectResource(mcpServer, projectStore);
|
|
30
|
-
|
|
31
|
+
logger.info("[MCP Init] Registering package resource...");
|
|
31
32
|
registerPackageResource(mcpServer, projectStore);
|
|
32
33
|
|
|
33
34
|
// Register more specific templates first
|
|
34
|
-
|
|
35
|
+
logger.info("[MCP Init] Registering notebook resource...");
|
|
35
36
|
registerNotebookResource(mcpServer, projectStore);
|
|
36
|
-
|
|
37
|
+
logger.info("[MCP Init] Registering source resource...");
|
|
37
38
|
registerSourceResource(mcpServer, projectStore);
|
|
38
|
-
|
|
39
|
+
logger.info("[MCP Init] Registering query resource...");
|
|
39
40
|
registerQueryResource(mcpServer, projectStore);
|
|
40
|
-
|
|
41
|
+
logger.info("[MCP Init] Registering view resource...");
|
|
41
42
|
registerViewResource(mcpServer, projectStore);
|
|
42
43
|
|
|
43
44
|
// Register the general model template last among resource types
|
|
44
|
-
|
|
45
|
+
logger.info("[MCP Init] Registering model resource...");
|
|
45
46
|
registerModelResource(mcpServer, projectStore);
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
logger.info("[MCP Init] Registering executeQuery tool...");
|
|
48
49
|
registerExecuteQueryTool(mcpServer, projectStore);
|
|
49
50
|
|
|
50
51
|
registerTools(mcpServer, projectStore);
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
logger.info("[MCP Init] Registering prompt capability...");
|
|
53
54
|
registerPromptCapability(mcpServer, projectStore);
|
|
54
55
|
|
|
55
|
-
const endTime =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
);
|
|
56
|
+
const endTime = performance.now();
|
|
57
|
+
logger.info(`[MCP Init] Finished initializeMcpServer`, {
|
|
58
|
+
duration: endTime - startTime,
|
|
59
|
+
});
|
|
59
60
|
|
|
60
61
|
return mcpServer;
|
|
61
62
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { logger } from "../../logger";
|
|
4
5
|
import { ProjectStore } from "../../service/project_store";
|
|
5
|
-
import { getModelForQuery } from "../handler_utils";
|
|
6
6
|
import { getMalloyErrorDetails, type ErrorDetails } from "../error_messages";
|
|
7
|
+
import { buildMalloyUri, getModelForQuery } from "../handler_utils";
|
|
7
8
|
import { MCP_ERROR_MESSAGES } from "../mcp_constants";
|
|
8
|
-
import { buildMalloyUri } from "../handler_utils";
|
|
9
9
|
|
|
10
10
|
// Zod shape defining required/optional params for executeQuery
|
|
11
11
|
const executeQueryShape = {
|
|
@@ -51,10 +51,7 @@ export function registerExecuteQueryTool(
|
|
|
51
51
|
queryName,
|
|
52
52
|
} = params;
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
"[MCP Tool executeQuery] Received params:",
|
|
56
|
-
JSON.stringify(params),
|
|
57
|
-
);
|
|
54
|
+
logger.info("[MCP Tool executeQuery] Received params:", { params });
|
|
58
55
|
|
|
59
56
|
const hasAdhocQuery = !!query;
|
|
60
57
|
const hasNamedQuery = !!queryName;
|
|
@@ -74,7 +71,7 @@ export function registerExecuteQueryTool(
|
|
|
74
71
|
// Zod/SDK handles missing required fields (packageName, modelPath) based on the shape
|
|
75
72
|
|
|
76
73
|
// --- Get Package and Model ---
|
|
77
|
-
|
|
74
|
+
logger.info(
|
|
78
75
|
`[MCP Tool executeQuery] Calling getModelForQuery for ${projectName}/${packageName}/${modelPath}`,
|
|
79
76
|
);
|
|
80
77
|
const modelResult = await getModelForQuery(
|
|
@@ -113,7 +110,7 @@ export function registerExecuteQueryTool(
|
|
|
113
110
|
|
|
114
111
|
// --- Execute Query ---
|
|
115
112
|
const { model } = modelResult;
|
|
116
|
-
|
|
113
|
+
logger.info(
|
|
117
114
|
`[MCP Tool executeQuery] Model found. Proceeding to execute query.`,
|
|
118
115
|
);
|
|
119
116
|
try {
|
|
@@ -192,9 +189,9 @@ export function registerExecuteQueryTool(
|
|
|
192
189
|
);
|
|
193
190
|
} catch (queryError) {
|
|
194
191
|
// Handle query execution errors (syntax errors, invalid queries, etc.)
|
|
195
|
-
|
|
192
|
+
logger.error(
|
|
196
193
|
`[MCP Server Error] Error executing query in ${projectName}/${packageName}/${modelPath}:`,
|
|
197
|
-
queryError,
|
|
194
|
+
{ error: queryError },
|
|
198
195
|
);
|
|
199
196
|
const errorDetails: ErrorDetails = getMalloyErrorDetails(
|
|
200
197
|
"executeQuery",
|
package/src/server.ts
CHANGED
|
@@ -4,7 +4,6 @@ import cors from "cors";
|
|
|
4
4
|
import express from "express";
|
|
5
5
|
import * as http from "http";
|
|
6
6
|
import { createProxyMiddleware } from "http-proxy-middleware";
|
|
7
|
-
import morgan from "morgan";
|
|
8
7
|
import { AddressInfo } from "net";
|
|
9
8
|
import * as path from "path";
|
|
10
9
|
import { ConnectionController } from "./controller/connection.controller";
|
|
@@ -14,6 +13,7 @@ import { PackageController } from "./controller/package.controller";
|
|
|
14
13
|
import { QueryController } from "./controller/query.controller";
|
|
15
14
|
import { ScheduleController } from "./controller/schedule.controller";
|
|
16
15
|
import { internalErrorToHttpError, NotImplementedError } from "./errors";
|
|
16
|
+
import { logger, loggerMiddleware } from "./logger";
|
|
17
17
|
import { initializeMcpServer } from "./mcp/server";
|
|
18
18
|
import { ProjectStore } from "./service/project_store";
|
|
19
19
|
|
|
@@ -79,7 +79,7 @@ const API_PREFIX = "/api/v0";
|
|
|
79
79
|
const isDevelopment = process.env["NODE_ENV"] === "development";
|
|
80
80
|
|
|
81
81
|
const app = express();
|
|
82
|
-
app.use(
|
|
82
|
+
app.use(loggerMiddleware);
|
|
83
83
|
app.use(cors());
|
|
84
84
|
|
|
85
85
|
const projectStore = new ProjectStore(SERVER_ROOT);
|
|
@@ -96,7 +96,7 @@ mcpApp.use(MCP_ENDPOINT, express.json());
|
|
|
96
96
|
mcpApp.use(MCP_ENDPOINT, cors());
|
|
97
97
|
|
|
98
98
|
mcpApp.all(MCP_ENDPOINT, async (req, res) => {
|
|
99
|
-
|
|
99
|
+
logger.info(`[MCP Debug] Handling ${req.method} (Stateless)`);
|
|
100
100
|
|
|
101
101
|
try {
|
|
102
102
|
if (req.method === "POST") {
|
|
@@ -105,35 +105,34 @@ mcpApp.all(MCP_ENDPOINT, async (req, res) => {
|
|
|
105
105
|
});
|
|
106
106
|
|
|
107
107
|
transport.onclose = () => {
|
|
108
|
-
|
|
108
|
+
logger.info(
|
|
109
109
|
`[MCP Transport Info] Stateless transport closed for a request.`,
|
|
110
110
|
);
|
|
111
111
|
};
|
|
112
112
|
transport.onerror = (err: Error) => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
);
|
|
113
|
+
logger.error(`[MCP Transport Error] Stateless transport error:`, {
|
|
114
|
+
error: err,
|
|
115
|
+
});
|
|
117
116
|
};
|
|
118
117
|
|
|
119
118
|
const requestMcpServer = initializeMcpServer(projectStore);
|
|
120
119
|
await requestMcpServer.connect(transport);
|
|
121
120
|
|
|
122
121
|
res.on("close", () => {
|
|
123
|
-
|
|
122
|
+
logger.info(
|
|
124
123
|
"[MCP Transport Info] Response closed, cleaning up stateless transport.",
|
|
125
124
|
);
|
|
126
125
|
transport.close().catch((err) => {
|
|
127
|
-
|
|
126
|
+
logger.error(
|
|
128
127
|
"[MCP Transport Error] Error closing stateless transport on response close:",
|
|
129
|
-
err,
|
|
128
|
+
{ error: err },
|
|
130
129
|
);
|
|
131
130
|
});
|
|
132
131
|
});
|
|
133
132
|
|
|
134
133
|
await transport.handleRequest(req, res, req.body);
|
|
135
134
|
} else if (req.method === "GET" || req.method === "DELETE") {
|
|
136
|
-
|
|
135
|
+
logger.warn(
|
|
137
136
|
`[MCP Transport Warn] Method Not Allowed in Stateless Mode: ${req.method}`,
|
|
138
137
|
);
|
|
139
138
|
res.setHeader("Allow", "POST");
|
|
@@ -147,7 +146,7 @@ mcpApp.all(MCP_ENDPOINT, async (req, res) => {
|
|
|
147
146
|
});
|
|
148
147
|
return;
|
|
149
148
|
} else {
|
|
150
|
-
|
|
149
|
+
logger.warn(`[MCP Transport Warn] Method Not Allowed: ${req.method}`);
|
|
151
150
|
res.setHeader("Allow", "POST");
|
|
152
151
|
res.status(405).json({
|
|
153
152
|
jsonrpc: "2.0",
|
|
@@ -157,9 +156,9 @@ mcpApp.all(MCP_ENDPOINT, async (req, res) => {
|
|
|
157
156
|
return;
|
|
158
157
|
}
|
|
159
158
|
} catch (error) {
|
|
160
|
-
|
|
159
|
+
logger.error(
|
|
161
160
|
`[MCP Transport Error] Unhandled error in ${req.method} handler (Stateless):`,
|
|
162
|
-
error,
|
|
161
|
+
{ error },
|
|
163
162
|
);
|
|
164
163
|
if (!res.headersSent) {
|
|
165
164
|
res.status(500).json({
|
|
@@ -184,10 +183,7 @@ if (!isDevelopment) {
|
|
|
184
183
|
} else {
|
|
185
184
|
// In development mode, proxy requests to React dev server
|
|
186
185
|
// Handle API routes first
|
|
187
|
-
app.use(`${API_PREFIX}`,
|
|
188
|
-
console.log(`[Express] Handling API request: ${req.method} ${req.url}`);
|
|
189
|
-
next();
|
|
190
|
-
});
|
|
186
|
+
app.use(`${API_PREFIX}`, loggerMiddleware);
|
|
191
187
|
|
|
192
188
|
// Proxy everything else to Vite
|
|
193
189
|
app.use(
|
|
@@ -214,7 +210,7 @@ app.get(`${API_PREFIX}/projects`, async (_req, res) => {
|
|
|
214
210
|
try {
|
|
215
211
|
res.status(200).json(await projectStore.listProjects());
|
|
216
212
|
} catch (error) {
|
|
217
|
-
|
|
213
|
+
logger.error(error);
|
|
218
214
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
219
215
|
res.status(status).json(json);
|
|
220
216
|
}
|
|
@@ -228,7 +224,7 @@ app.get(`${API_PREFIX}/projects/:projectName`, async (req, res) => {
|
|
|
228
224
|
);
|
|
229
225
|
res.status(200).json(await project.getProjectMetadata());
|
|
230
226
|
} catch (error) {
|
|
231
|
-
|
|
227
|
+
logger.error(error);
|
|
232
228
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
233
229
|
res.status(status).json(json);
|
|
234
230
|
}
|
|
@@ -240,7 +236,7 @@ app.get(`${API_PREFIX}/projects/:projectName/connections`, async (req, res) => {
|
|
|
240
236
|
await connectionController.listConnections(req.params.projectName),
|
|
241
237
|
);
|
|
242
238
|
} catch (error) {
|
|
243
|
-
|
|
239
|
+
logger.error(error);
|
|
244
240
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
245
241
|
res.status(status).json(json);
|
|
246
242
|
}
|
|
@@ -257,7 +253,7 @@ app.get(
|
|
|
257
253
|
),
|
|
258
254
|
);
|
|
259
255
|
} catch (error) {
|
|
260
|
-
|
|
256
|
+
logger.error(error);
|
|
261
257
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
262
258
|
res.status(status).json(json);
|
|
263
259
|
}
|
|
@@ -275,7 +271,7 @@ app.get(
|
|
|
275
271
|
),
|
|
276
272
|
);
|
|
277
273
|
} catch (error) {
|
|
278
|
-
|
|
274
|
+
logger.error(error);
|
|
279
275
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
280
276
|
res.status(status).json(json);
|
|
281
277
|
}
|
|
@@ -293,7 +289,7 @@ app.get(
|
|
|
293
289
|
),
|
|
294
290
|
);
|
|
295
291
|
} catch (error) {
|
|
296
|
-
|
|
292
|
+
logger.error(error);
|
|
297
293
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
298
294
|
res.status(status).json(json);
|
|
299
295
|
}
|
|
@@ -303,17 +299,17 @@ app.get(
|
|
|
303
299
|
app.get(
|
|
304
300
|
`${API_PREFIX}/projects/:projectName/connections/:connectionName/schemas/:schemaName/tables`,
|
|
305
301
|
async (req, res) => {
|
|
306
|
-
|
|
302
|
+
logger.info("req.params", { params: req.params });
|
|
307
303
|
try {
|
|
308
304
|
const results = await connectionController.listTables(
|
|
309
305
|
req.params.projectName,
|
|
310
306
|
req.params.connectionName,
|
|
311
307
|
req.params.schemaName,
|
|
312
308
|
);
|
|
313
|
-
|
|
309
|
+
logger.info("results", { results });
|
|
314
310
|
res.status(200).json(results);
|
|
315
311
|
} catch (error) {
|
|
316
|
-
|
|
312
|
+
logger.error(error);
|
|
317
313
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
318
314
|
res.status(status).json(json);
|
|
319
315
|
}
|
|
@@ -332,7 +328,7 @@ app.get(
|
|
|
332
328
|
),
|
|
333
329
|
);
|
|
334
330
|
} catch (error) {
|
|
335
|
-
|
|
331
|
+
logger.error(error);
|
|
336
332
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
337
333
|
res.status(status).json(json);
|
|
338
334
|
}
|
|
@@ -352,7 +348,7 @@ app.get(
|
|
|
352
348
|
),
|
|
353
349
|
);
|
|
354
350
|
} catch (error) {
|
|
355
|
-
|
|
351
|
+
logger.error(error);
|
|
356
352
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
357
353
|
res.status(status).json(json);
|
|
358
354
|
}
|
|
@@ -372,7 +368,7 @@ app.get(
|
|
|
372
368
|
),
|
|
373
369
|
);
|
|
374
370
|
} catch (error) {
|
|
375
|
-
|
|
371
|
+
logger.error(error);
|
|
376
372
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
377
373
|
res.status(status).json(json);
|
|
378
374
|
}
|
|
@@ -391,7 +387,7 @@ app.get(
|
|
|
391
387
|
),
|
|
392
388
|
);
|
|
393
389
|
} catch (error) {
|
|
394
|
-
|
|
390
|
+
logger.error(error);
|
|
395
391
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
396
392
|
res.status(status).json(json);
|
|
397
393
|
}
|
|
@@ -409,7 +405,7 @@ app.get(`${API_PREFIX}/projects/:projectName/packages`, async (req, res) => {
|
|
|
409
405
|
await packageController.listPackages(req.params.projectName),
|
|
410
406
|
);
|
|
411
407
|
} catch (error) {
|
|
412
|
-
|
|
408
|
+
logger.error(error);
|
|
413
409
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
414
410
|
res.status(status).json(json);
|
|
415
411
|
}
|
|
@@ -432,7 +428,7 @@ app.get(
|
|
|
432
428
|
),
|
|
433
429
|
);
|
|
434
430
|
} catch (error) {
|
|
435
|
-
|
|
431
|
+
logger.error(error);
|
|
436
432
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
437
433
|
res.status(status).json(json);
|
|
438
434
|
}
|
|
@@ -455,7 +451,7 @@ app.get(
|
|
|
455
451
|
),
|
|
456
452
|
);
|
|
457
453
|
} catch (error) {
|
|
458
|
-
|
|
454
|
+
logger.error(error);
|
|
459
455
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
460
456
|
res.status(status).json(json);
|
|
461
457
|
}
|
|
@@ -480,7 +476,7 @@ app.get(
|
|
|
480
476
|
),
|
|
481
477
|
);
|
|
482
478
|
} catch (error) {
|
|
483
|
-
|
|
479
|
+
logger.error(error);
|
|
484
480
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
485
481
|
res.status(status).json(json);
|
|
486
482
|
}
|
|
@@ -503,7 +499,7 @@ app.get(
|
|
|
503
499
|
),
|
|
504
500
|
);
|
|
505
501
|
} catch (error) {
|
|
506
|
-
|
|
502
|
+
logger.error(error);
|
|
507
503
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
508
504
|
res.status(status).json(json);
|
|
509
505
|
}
|
|
@@ -528,7 +524,7 @@ app.get(
|
|
|
528
524
|
),
|
|
529
525
|
);
|
|
530
526
|
} catch (error) {
|
|
531
|
-
|
|
527
|
+
logger.error(error);
|
|
532
528
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
533
529
|
res.status(status).json(json);
|
|
534
530
|
}
|
|
@@ -556,7 +552,7 @@ app.get(
|
|
|
556
552
|
),
|
|
557
553
|
);
|
|
558
554
|
} catch (error) {
|
|
559
|
-
|
|
555
|
+
logger.error(error);
|
|
560
556
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
561
557
|
res.status(status).json(json);
|
|
562
558
|
}
|
|
@@ -579,7 +575,7 @@ app.get(
|
|
|
579
575
|
),
|
|
580
576
|
);
|
|
581
577
|
} catch (error) {
|
|
582
|
-
|
|
578
|
+
logger.error(error);
|
|
583
579
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
584
580
|
res.status(status).json(json);
|
|
585
581
|
}
|
|
@@ -602,7 +598,7 @@ app.get(
|
|
|
602
598
|
),
|
|
603
599
|
);
|
|
604
600
|
} catch (error) {
|
|
605
|
-
|
|
601
|
+
logger.error(error);
|
|
606
602
|
const { json, status } = internalErrorToHttpError(error as Error);
|
|
607
603
|
res.status(status).json(json);
|
|
608
604
|
}
|
|
@@ -615,7 +611,7 @@ if (!isDevelopment) {
|
|
|
615
611
|
}
|
|
616
612
|
|
|
617
613
|
app.use((err: Error, _req: express.Request, res: express.Response) => {
|
|
618
|
-
|
|
614
|
+
logger.error("Unhandled error:", err);
|
|
619
615
|
const { json, status } = internalErrorToHttpError(err);
|
|
620
616
|
res.status(status).json(json);
|
|
621
617
|
});
|
|
@@ -623,18 +619,18 @@ app.use((err: Error, _req: express.Request, res: express.Response) => {
|
|
|
623
619
|
const mainServer = http.createServer(app);
|
|
624
620
|
mainServer.listen(PUBLISHER_PORT, PUBLISHER_HOST, () => {
|
|
625
621
|
const address = mainServer.address() as AddressInfo;
|
|
626
|
-
|
|
622
|
+
logger.info(
|
|
627
623
|
`Publisher server listening at http://${address.address}:${address.port}`,
|
|
628
624
|
);
|
|
629
625
|
if (isDevelopment) {
|
|
630
|
-
|
|
626
|
+
logger.info(
|
|
631
627
|
"Running in development mode - proxying to React dev server at http://localhost:5173",
|
|
632
628
|
);
|
|
633
629
|
}
|
|
634
630
|
});
|
|
635
631
|
|
|
636
632
|
const mcpHttpServer = mcpApp.listen(MCP_PORT, PUBLISHER_HOST, () => {
|
|
637
|
-
|
|
633
|
+
logger.info(`MCP server listening at http://${PUBLISHER_HOST}:${MCP_PORT}`);
|
|
638
634
|
});
|
|
639
635
|
|
|
640
636
|
export { app, mainServer as httpServer, mcpApp, mcpHttpServer };
|
package/src/service/db_utils.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import { BigQuery } from "@google-cloud/bigquery";
|
|
1
2
|
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
3
|
import os from "os";
|
|
4
|
-
import
|
|
4
|
+
import path from "path";
|
|
5
5
|
import { Pool } from "pg";
|
|
6
|
-
import { BigQuery } from "@google-cloud/bigquery";
|
|
7
6
|
import * as snowflake from "snowflake-sdk";
|
|
7
|
+
import { v4 as uuidv4 } from "uuid";
|
|
8
|
+
import { components } from "../api";
|
|
9
|
+
import { logger } from "../logger";
|
|
8
10
|
import {
|
|
9
11
|
ApiConnection,
|
|
12
|
+
MysqlConnection,
|
|
10
13
|
PostgresConnection,
|
|
11
14
|
SnowflakeConnection,
|
|
12
|
-
MysqlConnection,
|
|
13
15
|
} from "./model";
|
|
14
|
-
import { components } from "../api";
|
|
15
16
|
|
|
16
17
|
type ApiSchemaName = components["schemas"]["SchemaName"];
|
|
17
18
|
|
|
@@ -156,7 +157,7 @@ export async function getSchemasForConnection(
|
|
|
156
157
|
} finally {
|
|
157
158
|
snowflakeConn.destroy((error) => {
|
|
158
159
|
if (error) {
|
|
159
|
-
|
|
160
|
+
logger.error(`Error closing SnowflakeConnection: ${error}`);
|
|
160
161
|
}
|
|
161
162
|
});
|
|
162
163
|
}
|
|
@@ -188,9 +189,9 @@ export async function getTablesForSchema(
|
|
|
188
189
|
const [tables] = await dataset.getTables();
|
|
189
190
|
return tables.map((table) => table.id).filter((id) => id) as string[];
|
|
190
191
|
} catch (error) {
|
|
191
|
-
|
|
192
|
-
`Error getting tables for BigQuery schema ${schemaName} in connection ${connection.name}
|
|
193
|
-
error,
|
|
192
|
+
logger.error(
|
|
193
|
+
`Error getting tables for BigQuery schema ${schemaName} in connection ${connection.name}`,
|
|
194
|
+
{ error },
|
|
194
195
|
);
|
|
195
196
|
throw new Error(
|
|
196
197
|
`Failed to get tables for BigQuery schema ${schemaName} in connection ${connection.name}: ${(error as Error).message}`,
|
|
@@ -232,7 +233,7 @@ export async function getTablesForSchema(
|
|
|
232
233
|
} finally {
|
|
233
234
|
snowflakeConn.destroy((error) => {
|
|
234
235
|
if (error) {
|
|
235
|
-
|
|
236
|
+
logger.error(`Error closing SnowflakeConnection`, { error });
|
|
236
237
|
}
|
|
237
238
|
});
|
|
238
239
|
}
|
|
@@ -274,10 +275,9 @@ async function getSnowflakeTables(
|
|
|
274
275
|
sqlText: `USE DATABASE ${connInfo?.database} `,
|
|
275
276
|
complete: (err) => {
|
|
276
277
|
if (err) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
);
|
|
278
|
+
logger.error(`Error setting database ${connInfo.database}:`, {
|
|
279
|
+
error: err,
|
|
280
|
+
});
|
|
281
281
|
reject([]);
|
|
282
282
|
return;
|
|
283
283
|
}
|
|
@@ -293,9 +293,9 @@ async function getSnowflakeTables(
|
|
|
293
293
|
binds: [schemaName],
|
|
294
294
|
complete: (err, _, rows) => {
|
|
295
295
|
if (err) {
|
|
296
|
-
|
|
296
|
+
logger.error(
|
|
297
297
|
`Error fetching tables from ${connInfo.database}:`,
|
|
298
|
-
err,
|
|
298
|
+
{ error: err },
|
|
299
299
|
);
|
|
300
300
|
reject([]);
|
|
301
301
|
} else {
|
|
@@ -320,7 +320,7 @@ async function getSnowflakeSchemas(
|
|
|
320
320
|
} else {
|
|
321
321
|
resolve(
|
|
322
322
|
rows?.map((row) => {
|
|
323
|
-
|
|
323
|
+
logger.info("row", { row });
|
|
324
324
|
return {
|
|
325
325
|
name: row.name,
|
|
326
326
|
isDefault: row.isDefault === "Y",
|
package/src/service/model.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
ModelCompilationError,
|
|
32
32
|
ModelNotFoundError,
|
|
33
33
|
} from "../errors";
|
|
34
|
+
import { logger } from "../logger";
|
|
34
35
|
import {
|
|
35
36
|
MODEL_FILE_SUFFIX,
|
|
36
37
|
NOTEBOOK_FILE_SUFFIX,
|
|
@@ -238,7 +239,7 @@ export class Model {
|
|
|
238
239
|
if (this.compilationError) {
|
|
239
240
|
throw this.compilationError;
|
|
240
241
|
}
|
|
241
|
-
|
|
242
|
+
logger.info("queryName", { queryName, query });
|
|
242
243
|
let runnable: QueryMaterializer;
|
|
243
244
|
if (!this.modelMaterializer || !this.modelDef || !this.modelInfo)
|
|
244
245
|
throw new BadRequestError("Model has no queryable entities.");
|
package/src/service/package.ts
CHANGED
|
@@ -13,6 +13,7 @@ import recursive from "recursive-readdir";
|
|
|
13
13
|
import { components } from "../api";
|
|
14
14
|
import { API_PREFIX } from "../constants";
|
|
15
15
|
import { PackageNotFoundError } from "../errors";
|
|
16
|
+
import { logger } from "../logger";
|
|
16
17
|
import {
|
|
17
18
|
MODEL_FILE_SUFFIX,
|
|
18
19
|
NOTEBOOK_FILE_SUFFIX,
|
|
@@ -116,7 +117,7 @@ export class Package {
|
|
|
116
117
|
scheduler,
|
|
117
118
|
);
|
|
118
119
|
} catch (error) {
|
|
119
|
-
|
|
120
|
+
logger.error("Error loading package", { error });
|
|
120
121
|
const endTime = performance.now();
|
|
121
122
|
const executionTime = endTime - startTime;
|
|
122
123
|
this.packageLoadHistogram.record(executionTime, {
|
|
@@ -219,7 +220,7 @@ export class Package {
|
|
|
219
220
|
try {
|
|
220
221
|
files = await recursive(packagePath);
|
|
221
222
|
} catch (error) {
|
|
222
|
-
|
|
223
|
+
logger.error(error);
|
|
223
224
|
throw new PackageNotFoundError(
|
|
224
225
|
`Package config for ${packagePath} does not exist.`,
|
|
225
226
|
);
|
|
@@ -3,6 +3,7 @@ import * as path from "path";
|
|
|
3
3
|
import { components } from "../api";
|
|
4
4
|
import { API_PREFIX } from "../constants";
|
|
5
5
|
import { ProjectNotFoundError } from "../errors";
|
|
6
|
+
import { logger } from "../logger";
|
|
6
7
|
import { Project } from "./project";
|
|
7
8
|
type ApiProject = components["schemas"]["Project"];
|
|
8
9
|
|
|
@@ -68,9 +69,9 @@ export class ProjectStore {
|
|
|
68
69
|
return JSON.parse(projectManifestContent);
|
|
69
70
|
} catch (error) {
|
|
70
71
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
logger.error(
|
|
73
|
+
`Error reading publisher.config.json. Generating from directory`,
|
|
74
|
+
{ error },
|
|
74
75
|
);
|
|
75
76
|
return { projects: {} };
|
|
76
77
|
} else {
|
|
@@ -87,10 +88,9 @@ export class ProjectStore {
|
|
|
87
88
|
}
|
|
88
89
|
return { projects };
|
|
89
90
|
} catch (lsError) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
);
|
|
91
|
+
logger.error(`Error listing directories in ${serverRootPath}`, {
|
|
92
|
+
error: lsError,
|
|
93
|
+
});
|
|
94
94
|
return { projects: {} };
|
|
95
95
|
}
|
|
96
96
|
}
|
package/src/service/scheduler.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Model } from "./model";
|
|
2
|
-
import { components } from "../api";
|
|
3
1
|
import cron from "node-cron";
|
|
2
|
+
import { components } from "../api";
|
|
3
|
+
import { logger } from "../logger";
|
|
4
|
+
import { Model } from "./model";
|
|
4
5
|
|
|
5
6
|
type ApiSource = components["schemas"]["Source"];
|
|
6
7
|
type ApiView = components["schemas"]["View"];
|
|
@@ -72,7 +73,7 @@ class Schedule {
|
|
|
72
73
|
// TODO: Don't split quoted strings. Use regex instead of split.
|
|
73
74
|
const annotationSplit = annotation.split(/\s+/);
|
|
74
75
|
if (annotationSplit.length != 6) {
|
|
75
|
-
|
|
76
|
+
logger.info("Length: " + annotationSplit.length);
|
|
76
77
|
throw new Error(
|
|
77
78
|
"Invalid annotation string does not have enough parts: " +
|
|
78
79
|
annotation,
|