@malloy-publisher/server 0.0.137 → 0.0.138
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 +79 -11
- package/dist/app/assets/{HomePage-Dy1B_c_L.js → HomePage-BlrCLLW3.js} +1 -1
- package/dist/app/assets/{MainPage-gzYdjQav.js → MainPage-DdMiKT2B.js} +1 -1
- package/dist/app/assets/{ModelPage-CUAaN3rL.js → ModelPage-JehFN4Kt.js} +1 -1
- package/dist/app/assets/{PackagePage-DcWkgxZq.js → PackagePage-CwvugLqQ.js} +1 -1
- package/dist/app/assets/{ProjectPage-Bpgs1NvH.js → ProjectPage-CIN0WvAX.js} +1 -1
- package/dist/app/assets/{RouteError-Cd7ol6pR.js → RouteError-C8KTs0Dq.js} +1 -1
- package/dist/app/assets/{WorkbookPage-B2aMRJtD.js → WorkbookPage-BPDyPPd-.js} +1 -1
- package/dist/app/assets/{index-D0zwjcl_.js → index-BnKpD5AP.js} +1 -1
- package/dist/app/assets/{index-CpcugJHa.js → index-Cv4oxotF.js} +83 -83
- package/dist/app/assets/{index-CKzqCaQl.js → index-DxYt9qtJ.js} +1 -1
- package/dist/app/assets/{index.umd-bQQqKuZo.js → index.umd-DMLKZdiT.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/server.js +100 -24
- package/package.json +1 -1
- package/src/controller/model.controller.ts +27 -2
- package/src/mcp/resources/notebook_resource.ts +3 -3
- package/src/server.ts +47 -6
- package/src/service/model.ts +96 -39
package/dist/server.js
CHANGED
|
@@ -133002,6 +133002,18 @@ class ModelController {
|
|
|
133002
133002
|
}
|
|
133003
133003
|
return model.getNotebook();
|
|
133004
133004
|
}
|
|
133005
|
+
async executeNotebookCell(projectName, packageName, notebookPath, cellIndex) {
|
|
133006
|
+
const project = await this.projectStore.getProject(projectName, false);
|
|
133007
|
+
const p = await project.getPackage(packageName, false);
|
|
133008
|
+
const model = p.getModel(notebookPath);
|
|
133009
|
+
if (!model) {
|
|
133010
|
+
throw new ModelNotFoundError(`${notebookPath} does not exist`);
|
|
133011
|
+
}
|
|
133012
|
+
if (model.getType() === "model") {
|
|
133013
|
+
throw new ModelNotFoundError(`${notebookPath} is a model`);
|
|
133014
|
+
}
|
|
133015
|
+
return model.executeNotebookCell(cellIndex);
|
|
133016
|
+
}
|
|
133005
133017
|
}
|
|
133006
133018
|
|
|
133007
133019
|
// src/controller/package.controller.ts
|
|
@@ -139036,7 +139048,14 @@ class Model {
|
|
|
139036
139048
|
return new Model(packageName, modelPath, dataStyles, modelType, modelMaterializer, modelDef, sources, queries, runnableNotebookCells, undefined);
|
|
139037
139049
|
} catch (error) {
|
|
139038
139050
|
let computedError = error;
|
|
139051
|
+
if (error instanceof Error && error.stack) {
|
|
139052
|
+
console.error("Error stack", error.stack);
|
|
139053
|
+
}
|
|
139039
139054
|
if (error instanceof import_malloy2.MalloyError) {
|
|
139055
|
+
const problems = error.problems;
|
|
139056
|
+
for (const problem of problems) {
|
|
139057
|
+
console.error("Problem", problem);
|
|
139058
|
+
}
|
|
139040
139059
|
computedError = new ModelCompilationError(error);
|
|
139041
139060
|
}
|
|
139042
139061
|
return new Model(packageName, modelPath, dataStyles, modelType, undefined, undefined, undefined, undefined, undefined, computedError);
|
|
@@ -139171,27 +139190,12 @@ run: ${sourceName ? sourceName + "->" : ""}${queryName}`);
|
|
|
139171
139190
|
};
|
|
139172
139191
|
}
|
|
139173
139192
|
async getNotebookModel() {
|
|
139174
|
-
const notebookCells =
|
|
139175
|
-
let queryName = undefined;
|
|
139176
|
-
let queryResult = undefined;
|
|
139177
|
-
if (cell.runnable) {
|
|
139178
|
-
try {
|
|
139179
|
-
const rowLimit = (await cell.runnable.getPreparedResult()).resultExplore.limit || ROW_LIMIT;
|
|
139180
|
-
const result = await cell.runnable.run({ rowLimit });
|
|
139181
|
-
const query = (await cell.runnable.getPreparedQuery())._query;
|
|
139182
|
-
queryName = query.as || query.name;
|
|
139183
|
-
queryResult = result?._queryResult && this.modelInfo && JSON.stringify(import_malloy2.API.util.wrapResult(result));
|
|
139184
|
-
} catch {
|
|
139185
|
-
}
|
|
139186
|
-
}
|
|
139193
|
+
const notebookCells = this.runnableNotebookCells.map((cell) => {
|
|
139187
139194
|
return {
|
|
139188
139195
|
type: cell.type,
|
|
139189
|
-
text: cell.text
|
|
139190
|
-
queryName,
|
|
139191
|
-
result: queryResult,
|
|
139192
|
-
newSources: cell.newSources?.map((source) => JSON.stringify(source))
|
|
139196
|
+
text: cell.text
|
|
139193
139197
|
};
|
|
139194
|
-
})
|
|
139198
|
+
});
|
|
139195
139199
|
return {
|
|
139196
139200
|
type: "notebook",
|
|
139197
139201
|
packageName: this.packageName,
|
|
@@ -139203,6 +139207,57 @@ run: ${sourceName ? sourceName + "->" : ""}${queryName}`);
|
|
|
139203
139207
|
notebookCells
|
|
139204
139208
|
};
|
|
139205
139209
|
}
|
|
139210
|
+
async executeNotebookCell(cellIndex) {
|
|
139211
|
+
if (this.compilationError) {
|
|
139212
|
+
throw this.compilationError;
|
|
139213
|
+
}
|
|
139214
|
+
if (!this.runnableNotebookCells) {
|
|
139215
|
+
throw new BadRequestError("No notebook cells available");
|
|
139216
|
+
}
|
|
139217
|
+
if (cellIndex < 0 || cellIndex >= this.runnableNotebookCells.length) {
|
|
139218
|
+
throw new BadRequestError(`Cell index ${cellIndex} out of range (0-${this.runnableNotebookCells.length - 1})`);
|
|
139219
|
+
}
|
|
139220
|
+
const cell = this.runnableNotebookCells[cellIndex];
|
|
139221
|
+
if (cell.type === "markdown") {
|
|
139222
|
+
return {
|
|
139223
|
+
type: cell.type,
|
|
139224
|
+
text: cell.text
|
|
139225
|
+
};
|
|
139226
|
+
}
|
|
139227
|
+
let queryName = undefined;
|
|
139228
|
+
let queryResult = undefined;
|
|
139229
|
+
if (cell.runnable) {
|
|
139230
|
+
try {
|
|
139231
|
+
const rowLimit = (await cell.runnable.getPreparedResult()).resultExplore.limit || ROW_LIMIT;
|
|
139232
|
+
const result = await cell.runnable.run({ rowLimit });
|
|
139233
|
+
const query = (await cell.runnable.getPreparedQuery())._query;
|
|
139234
|
+
queryName = query.as || query.name;
|
|
139235
|
+
queryResult = result?._queryResult && this.modelInfo && JSON.stringify(import_malloy2.API.util.wrapResult(result));
|
|
139236
|
+
} catch (error) {
|
|
139237
|
+
if (error instanceof import_malloy2.MalloyError) {
|
|
139238
|
+
throw error;
|
|
139239
|
+
}
|
|
139240
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
139241
|
+
if (errorMessage.trim() === "Model has no queries.") {
|
|
139242
|
+
return {
|
|
139243
|
+
type: "code",
|
|
139244
|
+
text: cell.text
|
|
139245
|
+
};
|
|
139246
|
+
} else {
|
|
139247
|
+
console.log("Error message: ", errorMessage);
|
|
139248
|
+
}
|
|
139249
|
+
console.log("Cell content: ", cellIndex, cell.type, cell.text);
|
|
139250
|
+
throw new BadRequestError(`Cell execution failed: ${errorMessage}`);
|
|
139251
|
+
}
|
|
139252
|
+
}
|
|
139253
|
+
return {
|
|
139254
|
+
type: cell.type,
|
|
139255
|
+
text: cell.text,
|
|
139256
|
+
queryName,
|
|
139257
|
+
result: queryResult,
|
|
139258
|
+
newSources: cell.newSources?.map((source) => JSON.stringify(source))
|
|
139259
|
+
};
|
|
139260
|
+
}
|
|
139206
139261
|
static async getModelRuntime(packagePath, modelPath, connections) {
|
|
139207
139262
|
const fullModelPath = path4.join(packagePath, modelPath);
|
|
139208
139263
|
try {
|
|
@@ -144712,8 +144767,8 @@ app.get(`${API_PREFIX2}/projects/:projectName/packages/:packageName/models/*?`,
|
|
|
144712
144767
|
return;
|
|
144713
144768
|
}
|
|
144714
144769
|
try {
|
|
144715
|
-
const
|
|
144716
|
-
res.status(200).json(await modelController.getModel(req.params.projectName, req.params.packageName,
|
|
144770
|
+
const modelPath = req.params["0"];
|
|
144771
|
+
res.status(200).json(await modelController.getModel(req.params.projectName, req.params.packageName, modelPath));
|
|
144717
144772
|
} catch (error) {
|
|
144718
144773
|
logger2.error(error);
|
|
144719
144774
|
const { json: json2, status } = internalErrorToHttpError(error);
|
|
@@ -144733,14 +144788,35 @@ app.get(`${API_PREFIX2}/projects/:projectName/packages/:packageName/notebooks`,
|
|
|
144733
144788
|
res.status(status).json(json2);
|
|
144734
144789
|
}
|
|
144735
144790
|
});
|
|
144791
|
+
app.get(`${API_PREFIX2}/projects/:projectName/packages/:packageName/notebooks/*/cells/:cellIndex`, async (req, res) => {
|
|
144792
|
+
if (req.query.versionId) {
|
|
144793
|
+
setVersionIdError(res);
|
|
144794
|
+
return;
|
|
144795
|
+
}
|
|
144796
|
+
try {
|
|
144797
|
+
const cellIndex = parseInt(req.params.cellIndex, 10);
|
|
144798
|
+
if (isNaN(cellIndex)) {
|
|
144799
|
+
res.status(400).json({
|
|
144800
|
+
error: "Invalid cell index"
|
|
144801
|
+
});
|
|
144802
|
+
return;
|
|
144803
|
+
}
|
|
144804
|
+
const notebookPath = req.params["0"];
|
|
144805
|
+
res.status(200).json(await modelController.executeNotebookCell(req.params.projectName, req.params.packageName, notebookPath, cellIndex));
|
|
144806
|
+
} catch (error) {
|
|
144807
|
+
logger2.error(error);
|
|
144808
|
+
const { json: json2, status } = internalErrorToHttpError(error);
|
|
144809
|
+
res.status(status).json(json2);
|
|
144810
|
+
}
|
|
144811
|
+
});
|
|
144736
144812
|
app.get(`${API_PREFIX2}/projects/:projectName/packages/:packageName/notebooks/*?`, async (req, res) => {
|
|
144737
144813
|
if (req.query.versionId) {
|
|
144738
144814
|
setVersionIdError(res);
|
|
144739
144815
|
return;
|
|
144740
144816
|
}
|
|
144741
144817
|
try {
|
|
144742
|
-
const
|
|
144743
|
-
res.status(200).json(await modelController.getNotebook(req.params.projectName, req.params.packageName,
|
|
144818
|
+
const notebookPath = req.params["0"];
|
|
144819
|
+
res.status(200).json(await modelController.getNotebook(req.params.projectName, req.params.packageName, notebookPath));
|
|
144744
144820
|
} catch (error) {
|
|
144745
144821
|
logger2.error(error);
|
|
144746
144822
|
const { json: json2, status } = internalErrorToHttpError(error);
|
|
@@ -144753,8 +144829,8 @@ app.post(`${API_PREFIX2}/projects/:projectName/packages/:packageName/models/*?/q
|
|
|
144753
144829
|
return;
|
|
144754
144830
|
}
|
|
144755
144831
|
try {
|
|
144756
|
-
const
|
|
144757
|
-
res.status(200).json(await queryController.getQuery(req.params.projectName, req.params.packageName,
|
|
144832
|
+
const modelPath = req.params["0"];
|
|
144833
|
+
res.status(200).json(await queryController.getQuery(req.params.projectName, req.params.packageName, modelPath, req.body.sourceName, req.body.queryName, req.body.query));
|
|
144758
144834
|
} catch (error) {
|
|
144759
144835
|
logger2.error(error);
|
|
144760
144836
|
const { json: json2, status } = internalErrorToHttpError(error);
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import { ProjectStore } from "../service/project_store";
|
|
|
5
5
|
type ApiNotebook = components["schemas"]["Notebook"];
|
|
6
6
|
type ApiModel = components["schemas"]["Model"];
|
|
7
7
|
type ApiCompiledModel = components["schemas"]["CompiledModel"];
|
|
8
|
-
type
|
|
8
|
+
type ApiRawNotebook = components["schemas"]["RawNotebook"];
|
|
9
9
|
export type ListModelsFilterEnum =
|
|
10
10
|
components["parameters"]["ListModelsFilterEnum"];
|
|
11
11
|
export class ModelController {
|
|
@@ -54,7 +54,7 @@ export class ModelController {
|
|
|
54
54
|
projectName: string,
|
|
55
55
|
packageName: string,
|
|
56
56
|
notebookPath: string,
|
|
57
|
-
): Promise<
|
|
57
|
+
): Promise<ApiRawNotebook> {
|
|
58
58
|
const project = await this.projectStore.getProject(projectName, false);
|
|
59
59
|
const p = await project.getPackage(packageName, false);
|
|
60
60
|
const model = p.getModel(notebookPath);
|
|
@@ -67,4 +67,29 @@ export class ModelController {
|
|
|
67
67
|
|
|
68
68
|
return model.getNotebook();
|
|
69
69
|
}
|
|
70
|
+
|
|
71
|
+
public async executeNotebookCell(
|
|
72
|
+
projectName: string,
|
|
73
|
+
packageName: string,
|
|
74
|
+
notebookPath: string,
|
|
75
|
+
cellIndex: number,
|
|
76
|
+
): Promise<{
|
|
77
|
+
type: "code" | "markdown";
|
|
78
|
+
text: string;
|
|
79
|
+
queryName?: string;
|
|
80
|
+
result?: string;
|
|
81
|
+
newSources?: string[];
|
|
82
|
+
}> {
|
|
83
|
+
const project = await this.projectStore.getProject(projectName, false);
|
|
84
|
+
const p = await project.getPackage(packageName, false);
|
|
85
|
+
const model = p.getModel(notebookPath);
|
|
86
|
+
if (!model) {
|
|
87
|
+
throw new ModelNotFoundError(`${notebookPath} does not exist`);
|
|
88
|
+
}
|
|
89
|
+
if (model.getType() === "model") {
|
|
90
|
+
throw new ModelNotFoundError(`${notebookPath} is a model`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return model.executeNotebookCell(cellIndex);
|
|
94
|
+
}
|
|
70
95
|
}
|
|
@@ -82,9 +82,9 @@ export function registerNotebookResource(
|
|
|
82
82
|
throw new McpGetResourceError(errorDetails);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// Now try to
|
|
86
|
-
const notebookContent: components["schemas"]["
|
|
87
|
-
await modelInstance.getNotebook();
|
|
85
|
+
// Now try to get the actual notebook content
|
|
86
|
+
const notebookContent: components["schemas"]["RawNotebook"] =
|
|
87
|
+
await modelInstance.getNotebook();
|
|
88
88
|
return notebookContent;
|
|
89
89
|
} catch (error) {
|
|
90
90
|
if (error instanceof McpGetResourceError) {
|
package/src/server.ts
CHANGED
|
@@ -653,12 +653,13 @@ app.get(
|
|
|
653
653
|
}
|
|
654
654
|
|
|
655
655
|
try {
|
|
656
|
-
|
|
656
|
+
// Express stores wildcard matches in params['0']
|
|
657
|
+
const modelPath = (req.params as Record<string, string>)["0"];
|
|
657
658
|
res.status(200).json(
|
|
658
659
|
await modelController.getModel(
|
|
659
660
|
req.params.projectName,
|
|
660
661
|
req.params.packageName,
|
|
661
|
-
|
|
662
|
+
modelPath,
|
|
662
663
|
),
|
|
663
664
|
);
|
|
664
665
|
} catch (error) {
|
|
@@ -692,6 +693,44 @@ app.get(
|
|
|
692
693
|
},
|
|
693
694
|
);
|
|
694
695
|
|
|
696
|
+
// Execute notebook cell route must come BEFORE the general get notebook route
|
|
697
|
+
// to avoid the wildcard matching incorrectly
|
|
698
|
+
app.get(
|
|
699
|
+
`${API_PREFIX}/projects/:projectName/packages/:packageName/notebooks/*/cells/:cellIndex`,
|
|
700
|
+
async (req, res) => {
|
|
701
|
+
if (req.query.versionId) {
|
|
702
|
+
setVersionIdError(res);
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
try {
|
|
707
|
+
const cellIndex = parseInt(req.params.cellIndex, 10);
|
|
708
|
+
if (isNaN(cellIndex)) {
|
|
709
|
+
res.status(400).json({
|
|
710
|
+
error: "Invalid cell index",
|
|
711
|
+
});
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// Express stores wildcard matches in params['0']
|
|
716
|
+
const notebookPath = (req.params as Record<string, string>)["0"];
|
|
717
|
+
|
|
718
|
+
res.status(200).json(
|
|
719
|
+
await modelController.executeNotebookCell(
|
|
720
|
+
req.params.projectName,
|
|
721
|
+
req.params.packageName,
|
|
722
|
+
notebookPath,
|
|
723
|
+
cellIndex,
|
|
724
|
+
),
|
|
725
|
+
);
|
|
726
|
+
} catch (error) {
|
|
727
|
+
logger.error(error);
|
|
728
|
+
const { json, status } = internalErrorToHttpError(error as Error);
|
|
729
|
+
res.status(status).json(json);
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
);
|
|
733
|
+
|
|
695
734
|
app.get(
|
|
696
735
|
`${API_PREFIX}/projects/:projectName/packages/:packageName/notebooks/*?`,
|
|
697
736
|
async (req, res) => {
|
|
@@ -701,12 +740,13 @@ app.get(
|
|
|
701
740
|
}
|
|
702
741
|
|
|
703
742
|
try {
|
|
704
|
-
|
|
743
|
+
// Express stores wildcard matches in params['0']
|
|
744
|
+
const notebookPath = (req.params as Record<string, string>)["0"];
|
|
705
745
|
res.status(200).json(
|
|
706
746
|
await modelController.getNotebook(
|
|
707
747
|
req.params.projectName,
|
|
708
748
|
req.params.packageName,
|
|
709
|
-
|
|
749
|
+
notebookPath,
|
|
710
750
|
),
|
|
711
751
|
);
|
|
712
752
|
} catch (error) {
|
|
@@ -726,12 +766,13 @@ app.post(
|
|
|
726
766
|
}
|
|
727
767
|
|
|
728
768
|
try {
|
|
729
|
-
|
|
769
|
+
// Express stores wildcard matches in params['0']
|
|
770
|
+
const modelPath = (req.params as Record<string, string>)["0"];
|
|
730
771
|
res.status(200).json(
|
|
731
772
|
await queryController.getQuery(
|
|
732
773
|
req.params.projectName,
|
|
733
774
|
req.params.packageName,
|
|
734
|
-
|
|
775
|
+
modelPath,
|
|
735
776
|
req.body.sourceName as string,
|
|
736
777
|
req.body.queryName as string,
|
|
737
778
|
req.body.query as string,
|
package/src/service/model.ts
CHANGED
|
@@ -41,7 +41,7 @@ import { URL_READER } from "../utils";
|
|
|
41
41
|
|
|
42
42
|
type ApiCompiledModel = components["schemas"]["CompiledModel"];
|
|
43
43
|
type ApiNotebookCell = components["schemas"]["NotebookCell"];
|
|
44
|
-
type
|
|
44
|
+
type ApiRawNotebook = components["schemas"]["RawNotebook"];
|
|
45
45
|
// @ts-expect-error TODO: Fix missing Source type in API
|
|
46
46
|
type ApiSource = components["schemas"]["Source"];
|
|
47
47
|
type ApiView = components["schemas"]["View"];
|
|
@@ -155,7 +155,15 @@ export class Model {
|
|
|
155
155
|
);
|
|
156
156
|
} catch (error) {
|
|
157
157
|
let computedError = error;
|
|
158
|
+
if (error instanceof Error && error.stack) {
|
|
159
|
+
console.error("Error stack", error.stack);
|
|
160
|
+
}
|
|
161
|
+
|
|
158
162
|
if (error instanceof MalloyError) {
|
|
163
|
+
const problems = error.problems;
|
|
164
|
+
for (const problem of problems) {
|
|
165
|
+
console.error("Problem", problem);
|
|
166
|
+
}
|
|
159
167
|
computedError = new ModelCompilationError(error);
|
|
160
168
|
}
|
|
161
169
|
return new Model(
|
|
@@ -215,7 +223,7 @@ export class Model {
|
|
|
215
223
|
return this.compilationError;
|
|
216
224
|
}
|
|
217
225
|
|
|
218
|
-
public async getNotebook(): Promise<
|
|
226
|
+
public async getNotebook(): Promise<ApiRawNotebook> {
|
|
219
227
|
if (this.compilationError) {
|
|
220
228
|
throw this.compilationError;
|
|
221
229
|
}
|
|
@@ -341,42 +349,16 @@ export class Model {
|
|
|
341
349
|
} as ApiCompiledModel;
|
|
342
350
|
}
|
|
343
351
|
|
|
344
|
-
private async getNotebookModel(): Promise<
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
.limit || ROW_LIMIT;
|
|
355
|
-
const result = await cell.runnable.run({ rowLimit });
|
|
356
|
-
const query = (await cell.runnable.getPreparedQuery())
|
|
357
|
-
._query;
|
|
358
|
-
queryName = (query as NamedQuery).as || query.name;
|
|
359
|
-
queryResult =
|
|
360
|
-
result?._queryResult &&
|
|
361
|
-
this.modelInfo &&
|
|
362
|
-
JSON.stringify(API.util.wrapResult(result));
|
|
363
|
-
} catch {
|
|
364
|
-
// Catch block intentionally left empty as per previous logic review.
|
|
365
|
-
// Error handling for specific cases might be added here if needed.
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
return {
|
|
369
|
-
type: cell.type,
|
|
370
|
-
text: cell.text,
|
|
371
|
-
queryName: queryName,
|
|
372
|
-
result: queryResult,
|
|
373
|
-
newSources: cell.newSources?.map((source) =>
|
|
374
|
-
JSON.stringify(source),
|
|
375
|
-
),
|
|
376
|
-
} as ApiNotebookCell;
|
|
377
|
-
},
|
|
378
|
-
),
|
|
379
|
-
);
|
|
352
|
+
private async getNotebookModel(): Promise<ApiRawNotebook> {
|
|
353
|
+
// Return raw cell contents without executing them
|
|
354
|
+
const notebookCells: ApiNotebookCell[] = (
|
|
355
|
+
this.runnableNotebookCells as RunnableNotebookCell[]
|
|
356
|
+
).map((cell) => {
|
|
357
|
+
return {
|
|
358
|
+
type: cell.type,
|
|
359
|
+
text: cell.text,
|
|
360
|
+
} as ApiNotebookCell;
|
|
361
|
+
});
|
|
380
362
|
|
|
381
363
|
return {
|
|
382
364
|
type: "notebook",
|
|
@@ -389,7 +371,82 @@ export class Model {
|
|
|
389
371
|
sources: this.modelDef && this.sources,
|
|
390
372
|
queries: this.modelDef && this.queries,
|
|
391
373
|
notebookCells,
|
|
392
|
-
} as
|
|
374
|
+
} as ApiRawNotebook;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
public async executeNotebookCell(cellIndex: number): Promise<{
|
|
378
|
+
type: "code" | "markdown";
|
|
379
|
+
text: string;
|
|
380
|
+
queryName?: string;
|
|
381
|
+
result?: string;
|
|
382
|
+
newSources?: string[];
|
|
383
|
+
}> {
|
|
384
|
+
if (this.compilationError) {
|
|
385
|
+
throw this.compilationError;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (!this.runnableNotebookCells) {
|
|
389
|
+
throw new BadRequestError("No notebook cells available");
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (cellIndex < 0 || cellIndex >= this.runnableNotebookCells.length) {
|
|
393
|
+
throw new BadRequestError(
|
|
394
|
+
`Cell index ${cellIndex} out of range (0-${this.runnableNotebookCells.length - 1})`,
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const cell = this.runnableNotebookCells[cellIndex];
|
|
399
|
+
|
|
400
|
+
if (cell.type === "markdown") {
|
|
401
|
+
return {
|
|
402
|
+
type: cell.type,
|
|
403
|
+
text: cell.text,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// For code cells, execute the runnable if available
|
|
408
|
+
let queryName: string | undefined = undefined;
|
|
409
|
+
let queryResult: string | undefined = undefined;
|
|
410
|
+
|
|
411
|
+
if (cell.runnable) {
|
|
412
|
+
try {
|
|
413
|
+
const rowLimit =
|
|
414
|
+
(await cell.runnable.getPreparedResult()).resultExplore.limit ||
|
|
415
|
+
ROW_LIMIT;
|
|
416
|
+
const result = await cell.runnable.run({ rowLimit });
|
|
417
|
+
const query = (await cell.runnable.getPreparedQuery())._query;
|
|
418
|
+
queryName = (query as NamedQuery).as || query.name;
|
|
419
|
+
queryResult =
|
|
420
|
+
result?._queryResult &&
|
|
421
|
+
this.modelInfo &&
|
|
422
|
+
JSON.stringify(API.util.wrapResult(result));
|
|
423
|
+
} catch (error) {
|
|
424
|
+
// Re-throw execution errors so the client knows about them
|
|
425
|
+
if (error instanceof MalloyError) {
|
|
426
|
+
throw error;
|
|
427
|
+
}
|
|
428
|
+
const errorMessage =
|
|
429
|
+
error instanceof Error ? error.message : String(error);
|
|
430
|
+
if (errorMessage.trim() === "Model has no queries.") {
|
|
431
|
+
return {
|
|
432
|
+
type: "code",
|
|
433
|
+
text: cell.text,
|
|
434
|
+
};
|
|
435
|
+
} else {
|
|
436
|
+
console.log("Error message: ", errorMessage);
|
|
437
|
+
}
|
|
438
|
+
console.log("Cell content: ", cellIndex, cell.type, cell.text);
|
|
439
|
+
throw new BadRequestError(`Cell execution failed: ${errorMessage}`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
type: cell.type,
|
|
445
|
+
text: cell.text,
|
|
446
|
+
queryName: queryName,
|
|
447
|
+
result: queryResult,
|
|
448
|
+
newSources: cell.newSources?.map((source) => JSON.stringify(source)),
|
|
449
|
+
};
|
|
393
450
|
}
|
|
394
451
|
|
|
395
452
|
static async getModelRuntime(
|