diff --git a/src/Common/MessageHandler.test.ts b/src/Common/MessageHandler.test.ts index 6f64b3fde..8baef1740 100644 --- a/src/Common/MessageHandler.test.ts +++ b/src/Common/MessageHandler.test.ts @@ -1,6 +1,5 @@ import Q from "q"; import * as MessageHandler from "./MessageHandler"; -import { MessageTypes } from "../Contracts/ExplorerContracts"; describe("Message Handler", () => { it("should handle cached message", async () => { @@ -26,4 +25,34 @@ describe("Message Handler", () => { MessageHandler.runGarbageCollector(); expect(MessageHandler.RequestMap["123"]).toBeUndefined(); }); + + describe("getDataExplorerWindow", () => { + it("should return current window if current window has dataExplorerPlatform property", () => { + const currentWindow: Window = { dataExplorerPlatform: 0 } as any; + + expect(MessageHandler.getDataExplorerWindow(currentWindow)).toEqual(currentWindow); + }); + + it("should return current window's parent if current window's parent has dataExplorerPlatform property", () => { + const parentWindow: Window = { dataExplorerPlatform: 0 } as any; + const currentWindow: Window = { parent: parentWindow } as any; + + expect(MessageHandler.getDataExplorerWindow(currentWindow)).toEqual(parentWindow); + }); + + it("should return undefined if none of the windows in the hierarchy have dataExplorerPlatform property and window's parent is reference to itself", () => { + const parentWindow: Window = {} as any; + (parentWindow as any).parent = parentWindow; // If a window does not have a parent, its parent property is a reference to itself. + const currentWindow: Window = { parent: parentWindow } as any; + + expect(MessageHandler.getDataExplorerWindow(currentWindow)).toBeUndefined(); + }); + + it("should return undefined if none of the windows in the hierarchy have dataExplorerPlatform property and window's parent is not defined", () => { + const parentWindow: Window = {} as any; + const currentWindow: Window = { parent: parentWindow } as any; + + expect(MessageHandler.getDataExplorerWindow(currentWindow)).toBeUndefined(); + }); + }); }); diff --git a/src/Common/MessageHandler.ts b/src/Common/MessageHandler.ts index d9bbaae8b..ce1af9b47 100644 --- a/src/Common/MessageHandler.ts +++ b/src/Common/MessageHandler.ts @@ -48,16 +48,38 @@ export function sendCachedDataMessage( export function sendMessage(data: any): void { if (canSendMessage()) { - window.parent.postMessage( - { - signature: "pcIframe", - data: data - }, - window.document.referrer - ); + const dataExplorerWindow = getDataExplorerWindow(window); + if (dataExplorerWindow) { + dataExplorerWindow.parent.postMessage( + { + signature: "pcIframe", + data: data + }, + dataExplorerWindow.document.referrer + ); + } } } +// Only exported for unit tests +export const getDataExplorerWindow = (currentWindow: Window): Window | undefined => { + // Start with the current window and traverse up the parent hierarchy to find a window + // with `dataExplorerPlatform` property + let dataExplorerWindow: Window | undefined = currentWindow; + // TODO: Need to `any` here since the window imports Explorer which can't be in strict mode yet + // eslint-disable-next-line @typescript-eslint/no-explicit-any + while (dataExplorerWindow && (dataExplorerWindow as any).dataExplorerPlatform == undefined) { + // If a window does not have a parent, its parent property is a reference to itself. + if (dataExplorerWindow.parent == dataExplorerWindow) { + dataExplorerWindow = undefined; + } else { + dataExplorerWindow = dataExplorerWindow.parent; + } + } + + return dataExplorerWindow; +}; + export function canSendMessage(): boolean { return window.parent !== window; } diff --git a/src/Shared/Telemetry/TelemetryConstants.ts b/src/Shared/Telemetry/TelemetryConstants.ts index 53a03cd75..97a4c303f 100644 --- a/src/Shared/Telemetry/TelemetryConstants.ts +++ b/src/Shared/Telemetry/TelemetryConstants.ts @@ -70,7 +70,8 @@ export enum Action { NotebooksGitHubManualRepoAdd, NotebooksGitHubManageRepo, NotebooksGitHubCommit, - NotebooksGitHubDisconnect + NotebooksGitHubDisconnect, + OpenTerminal } export const ActionModifiers = { diff --git a/src/Terminal/index.ts b/src/Terminal/index.ts index 6826ad0ff..605d290f7 100644 --- a/src/Terminal/index.ts +++ b/src/Terminal/index.ts @@ -4,6 +4,8 @@ import "@jupyterlab/terminal/style/index.css"; import "./index.css"; import { ServerConnection } from "@jupyterlab/services"; import { JupyterLabAppFactory } from "./JupyterLabAppFactory"; +import { Action } from "../Shared/Telemetry/TelemetryConstants"; +import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor"; const getUrlVars = (): { [key: string]: string } => { const vars: { [key: string]: string } = {}; @@ -14,10 +16,7 @@ const getUrlVars = (): { [key: string]: string } => { return vars; }; -const main = (): void => { - const urlVars = getUrlVars(); - console.log("URL parameters", urlVars); - +const createServerSettings = (urlVars: { [key: string]: string }): ServerConnection.ISettings => { let body: BodyInit; if (urlVars.hasOwnProperty("terminalEndpoint")) { body = JSON.stringify({ @@ -39,14 +38,29 @@ const main = (): void => { fetch: window.parent.fetch }; } - const serverSettings = ServerConnection.makeSettings(options); - if (urlVars.hasOwnProperty("terminal")) { - JupyterLabAppFactory.createTerminalApp(serverSettings); - return; + return ServerConnection.makeSettings(options); +}; + +const main = async (): Promise => { + const urlVars = getUrlVars(); + const serverSettings = createServerSettings(urlVars); + + const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, { + baseUrl: serverSettings.baseUrl + }); + + try { + if (urlVars.hasOwnProperty("terminal")) { + await JupyterLabAppFactory.createTerminalApp(serverSettings); + } else { + throw new Error("Only terminal is supported"); + } + + TelemetryProcessor.traceSuccess(Action.OpenTerminal, startTime); + } catch (error) { + TelemetryProcessor.traceFailure(Action.OpenTerminal, startTime); } - - throw new Error("Only terminal is supported"); }; window.addEventListener("load", main);