-
Notifications
You must be signed in to change notification settings - Fork 39
VSCode: nicer job panel #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a049006
aa250e9
355113e
66f34b4
b731d32
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,8 @@ import { | |
| jobs, | ||
| ApiClientResponseError, | ||
| } from "@databricks/databricks-sdk"; | ||
| import {basename} from "path"; | ||
| import {basename} from "node:path"; | ||
| import * as fs from "node:fs/promises"; | ||
| import { | ||
| CancellationToken, | ||
| CancellationTokenSource, | ||
|
|
@@ -80,12 +81,7 @@ export async function runAsWorkflow({ | |
| token: cancellation.token, | ||
| }); | ||
| let htmlContent = response.views![0].content; | ||
| // window.parent doesn't exist in a Webview | ||
| htmlContent = htmlContent?.replace( | ||
| "<script>window.__STATIC_SETTINGS__", | ||
| "<script>window.parent = { postMessage: function() {}}; window.__STATIC_SETTINGS__" | ||
| ); | ||
| panel.html = htmlContent || ""; | ||
| panel.showHtmlResult(htmlContent || ""); | ||
| } else { | ||
| let response = await cluster.runPythonAndWait({ | ||
| path: syncDestination.localToRemoteNotebook(program) + ".py", | ||
|
|
@@ -120,7 +116,9 @@ export async function runAsWorkflow({ | |
| export class WorkflowOutputPanel { | ||
| private run?: WorkflowRun; | ||
| constructor(private panel: WebviewPanel, private extensionUri: Uri) { | ||
| panel.webview.html = this.getWebviewContent("Starting ..."); | ||
| this.getWebviewContent().then((html) => { | ||
| panel.webview.html = html; | ||
| }); | ||
| } | ||
|
|
||
| onDidDispose(listener: () => void): Disposable { | ||
|
|
@@ -135,47 +133,26 @@ export class WorkflowOutputPanel { | |
| this.panel.webview.html = htmlContent; | ||
| } | ||
|
|
||
| showHtmlResult(htmlContent: string) { | ||
| this.panel.webview.postMessage({ | ||
| fn: "setOutputHtml", | ||
| args: [htmlContent], | ||
| }); | ||
| } | ||
|
|
||
| showStdoutResult(output: string) { | ||
| /* html */ | ||
| this.html = `<html> | ||
| <head> | ||
| <script type="module" src="${this.getToolkitUri()}"></script> | ||
| <body> | ||
| <h1>Output</h1> | ||
| <hr> | ||
| <pre>${output}</pre> | ||
| </body> | ||
| </html>`; | ||
| this.panel.webview.postMessage({ | ||
| fn: "setStdout", | ||
| args: [output], | ||
| }); | ||
| } | ||
|
|
||
| // TODO: use new webview to render errors | ||
| showError({message, stack}: {message?: string; stack?: string}) { | ||
| /* html */ | ||
| this.html = [ | ||
| `<html> | ||
| <head> | ||
| <script type="module" src="${this.getToolkitUri()}"></script> | ||
| <style> | ||
| .alert-error { | ||
| padding: 8px; | ||
| color: rgb(200, 45, 76); | ||
| border-color: rgb(251, 208, 216); | ||
| background-color: #FFF5F7; | ||
| border: 1px solid #FBD0D8; | ||
| border-radius: 4px; | ||
| overflow: scroll; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <h1>Error</h1><hr>`, | ||
| message ? `<pre class="alert-error">${message}</pre>` : "", | ||
| stack ? `<pre class="alert-error">${stack}</pre>` : "", | ||
| this.run?.runPageUrl | ||
| ? `<vscode-link href="${this.run?.runPageUrl}">View job on Databricks</vscode-link>` | ||
| : "", | ||
| `</body> | ||
| </html>`, | ||
| ].join("\n"); | ||
| this.panel.webview.postMessage({ | ||
| fn: "setError", | ||
| args: [message, stack], | ||
| }); | ||
| } | ||
|
|
||
| updateState(state: jobs.RunLifeCycleState, run: WorkflowRun) { | ||
|
|
@@ -185,6 +162,43 @@ export class WorkflowOutputPanel { | |
| state, | ||
| pageUrl: run.runPageUrl, | ||
| }); | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can remove the function before this line since we don't use posted status anymore. |
||
| const task = run.tasks![0]; | ||
| const cluster = task.cluster_instance; | ||
|
|
||
| let clusterUrl = "#"; | ||
| if (cluster) { | ||
| clusterUrl = `https://${ | ||
| new URL(run.runPageUrl).hostname | ||
| }/#setting/sparkui/${cluster.cluster_id}/driver-${ | ||
| cluster.spark_context_id | ||
| }`; | ||
|
Comment on lines
+171
to
+175
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Maybe extract this into a function within Cluster object? It can be useful in other places to create similar links.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem is that we need the spark_context_id, which is only available on the Workflow run object.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But maybe we can pass that in. |
||
| } | ||
|
|
||
| this.panel.webview.postMessage({ | ||
| fn: "updateDetails", | ||
| args: [ | ||
| { | ||
| runUrl: run.runPageUrl, | ||
| runId: task.run_id, | ||
| clusterUrl, | ||
| clusterId: cluster?.cluster_id || "-", | ||
| started: task.start_time | ||
| ? new Date(task.start_time).toLocaleString() | ||
| : "-", | ||
| ended: task.end_time | ||
| ? new Date(task.end_time).toLocaleString() | ||
| : "-", | ||
| status: state, | ||
| }, | ||
| ], | ||
| }); | ||
| if (task.end_time) { | ||
| this.panel.webview.postMessage({ | ||
| fn: "stop", | ||
| args: [], | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| getToolkitUri(): Uri { | ||
|
|
@@ -197,39 +211,20 @@ export class WorkflowOutputPanel { | |
| ); | ||
| } | ||
|
|
||
| private getWebviewContent(message: string): string { | ||
| /* html */ | ||
| return `<html> | ||
| <head> | ||
| <script type="module" src="${this.getToolkitUri()}"></script> | ||
| </head> | ||
| <body> | ||
| <div style="margin:20px; display: flex; justify-content: center; width: 100%"><vscode-progress-ring></vscode-progress-ring></div> | ||
| <div style="display: flex; justify-content: center; width: 100%"><span id="message">${message}</span> <span id="duration"></span></div> | ||
|
|
||
| <script> | ||
| window.addEventListener('message', event => { | ||
| const messageEl = document.getElementById("message") | ||
| messageEl.innerHTML = ""; | ||
|
|
||
| switch(event.data.type) { | ||
| case "status": | ||
| const message = 'State: ' + event.data.state + ' - <vscode-link href="' + event.data.pageUrl + '">View job on Databricks</vscode-link>'; | ||
| messageEl.innerHTML = message; | ||
| break; | ||
|
|
||
| default: | ||
| messageEl.innerText = event.data.message; | ||
| break; | ||
| } | ||
| }); | ||
|
|
||
| let start = Date.now(); | ||
| let interval = setInterval(function() { | ||
| document.getElementById("duration").innerText = "(" + Math.floor((Date.now()-start) / 1000) + "s)"; | ||
| }, 300); | ||
| </script> | ||
| </body> | ||
| </html>`; | ||
| private async getWebviewContent(): Promise<string> { | ||
| const htmlFile = Uri.joinPath( | ||
| this.extensionUri, | ||
| "webview-ui", | ||
| "job.html" | ||
| ); | ||
| let html = await fs.readFile(htmlFile.fsPath, "utf8"); | ||
| html = html | ||
| .replace(/\/\*\* STRIP -> \*\*\/(.*?)\/\*\* <- STRIP \*\*\//gs, "") | ||
| .replace( | ||
| /src="[^"].*?\/toolkit.js"/g, | ||
| `src="${this.getToolkitUri()}"` | ||
| ); | ||
|
|
||
| return html; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the
node:prefix but let's make sure to be consistent. I can address this offline