mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-29 00:47:01 +00:00
Fix incorrect usage of TelemetryProcessor (#221)
* Fix incorrect usage of TelemetryProcessor * Address feedback
This commit is contained in:
parent
3f2c67af23
commit
7c5c8ddb7a
@ -562,3 +562,11 @@ export class AnalyticalStorageTtl {
|
|||||||
public static readonly Infinite: number = -1;
|
public static readonly Infinite: number = -1;
|
||||||
public static readonly Disabled: number = 0;
|
public static readonly Disabled: number = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TerminalQueryParams {
|
||||||
|
public static readonly Terminal = "terminal";
|
||||||
|
public static readonly Server = "server";
|
||||||
|
public static readonly Token = "token";
|
||||||
|
public static readonly SubscriptionId = "subscriptionId";
|
||||||
|
public static readonly TerminalEndpoint = "terminalEndpoint";
|
||||||
|
}
|
||||||
|
@ -25,34 +25,4 @@ describe("Message Handler", () => {
|
|||||||
MessageHandler.runGarbageCollector();
|
MessageHandler.runGarbageCollector();
|
||||||
expect(MessageHandler.RequestMap["123"]).toBeUndefined();
|
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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -2,6 +2,7 @@ import { MessageTypes } from "../Contracts/ExplorerContracts";
|
|||||||
import Q from "q";
|
import Q from "q";
|
||||||
import * as _ from "underscore";
|
import * as _ from "underscore";
|
||||||
import * as Constants from "./Constants";
|
import * as Constants from "./Constants";
|
||||||
|
import { getDataExplorerWindow } from "../Utils/WindowUtils";
|
||||||
|
|
||||||
export interface CachedDataPromise<T> {
|
export interface CachedDataPromise<T> {
|
||||||
deferred: Q.Deferred<T>;
|
deferred: Q.Deferred<T>;
|
||||||
@ -61,25 +62,6 @@ export function sendMessage(data: any): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
export function canSendMessage(): boolean {
|
||||||
return window.parent !== window;
|
return window.parent !== window;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import * as Logger from "../../../Common/Logger";
|
|||||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { StringUtils } from "../../../Utils/StringUtils";
|
import { StringUtils } from "../../../Utils/StringUtils";
|
||||||
|
import { userContext } from "../../../UserContext";
|
||||||
|
import { TerminalQueryParams } from "../../../Common/Constants";
|
||||||
|
|
||||||
export interface NotebookTerminalComponentProps {
|
export interface NotebookTerminalComponentProps {
|
||||||
notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo;
|
notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo;
|
||||||
@ -32,11 +34,11 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
|||||||
|
|
||||||
public getTerminalParams(): Map<string, string> {
|
public getTerminalParams(): Map<string, string> {
|
||||||
let params: Map<string, string> = new Map<string, string>();
|
let params: Map<string, string> = new Map<string, string>();
|
||||||
params.set("terminal", "true");
|
params.set(TerminalQueryParams.Terminal, "true");
|
||||||
|
|
||||||
const terminalEndpoint: string = this.tryGetTerminalEndpoint();
|
const terminalEndpoint: string = this.tryGetTerminalEndpoint();
|
||||||
if (terminalEndpoint) {
|
if (terminalEndpoint) {
|
||||||
params.set("terminalEndpoint", terminalEndpoint);
|
params.set(TerminalQueryParams.TerminalEndpoint, terminalEndpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
@ -75,11 +77,13 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
params.set("server", serverInfo.notebookServerEndpoint);
|
params.set(TerminalQueryParams.Server, serverInfo.notebookServerEndpoint);
|
||||||
if (serverInfo.authToken && serverInfo.authToken.length > 0) {
|
if (serverInfo.authToken && serverInfo.authToken.length > 0) {
|
||||||
params.set("token", serverInfo.authToken);
|
params.set(TerminalQueryParams.Token, serverInfo.authToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params.set(TerminalQueryParams.SubscriptionId, userContext.subscriptionId);
|
||||||
|
|
||||||
let result: string = "terminal.html?";
|
let result: string = "terminal.html?";
|
||||||
for (let key of params.keys()) {
|
for (let key of params.keys()) {
|
||||||
result += `${key}=${encodeURIComponent(params.get(key))}&`;
|
result += `${key}=${encodeURIComponent(params.get(key))}&`;
|
||||||
|
@ -134,11 +134,9 @@ describe("Delete Collection Confirmation Pane", () => {
|
|||||||
expect(telemetryProcessorSpy.called).toBe(true);
|
expect(telemetryProcessorSpy.called).toBe(true);
|
||||||
let deleteFeedback = new DeleteFeedback(SubscriptionId, AccountName, DataModels.ApiKind.SQL, Feedback);
|
let deleteFeedback = new DeleteFeedback(SubscriptionId, AccountName, DataModels.ApiKind.SQL, Feedback);
|
||||||
expect(
|
expect(
|
||||||
telemetryProcessorSpy.calledWith(
|
telemetryProcessorSpy.calledWith(Action.DeleteCollection, ActionModifiers.Mark, {
|
||||||
Action.DeleteCollection,
|
message: JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback))
|
||||||
ActionModifiers.Mark,
|
})
|
||||||
JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback))
|
|
||||||
)
|
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -88,11 +88,9 @@ export default class DeleteCollectionConfirmationPane extends ContextualPaneBase
|
|||||||
this.containerDeleteFeedback()
|
this.containerDeleteFeedback()
|
||||||
);
|
);
|
||||||
|
|
||||||
TelemetryProcessor.trace(
|
TelemetryProcessor.trace(Action.DeleteCollection, ActionModifiers.Mark, {
|
||||||
Action.DeleteCollection,
|
message: JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback))
|
||||||
ActionModifiers.Mark,
|
});
|
||||||
JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.containerDeleteFeedback("");
|
this.containerDeleteFeedback("");
|
||||||
}
|
}
|
||||||
|
@ -120,11 +120,9 @@ describe("Delete Database Confirmation Pane", () => {
|
|||||||
|
|
||||||
return pane.submit().then(() => {
|
return pane.submit().then(() => {
|
||||||
let deleteFeedback = new DeleteFeedback(SubscriptionId, AccountName, DataModels.ApiKind.SQL, Feedback);
|
let deleteFeedback = new DeleteFeedback(SubscriptionId, AccountName, DataModels.ApiKind.SQL, Feedback);
|
||||||
expect(TelemetryProcessor.trace).toHaveBeenCalledWith(
|
expect(TelemetryProcessor.trace).toHaveBeenCalledWith(Action.DeleteDatabase, ActionModifiers.Mark, {
|
||||||
Action.DeleteDatabase,
|
message: JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback))
|
||||||
ActionModifiers.Mark,
|
});
|
||||||
JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback))
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -97,11 +97,9 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
|
|||||||
this.databaseDeleteFeedback()
|
this.databaseDeleteFeedback()
|
||||||
);
|
);
|
||||||
|
|
||||||
TelemetryProcessor.trace(
|
TelemetryProcessor.trace(Action.DeleteDatabase, ActionModifiers.Mark, {
|
||||||
Action.DeleteDatabase,
|
message: JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback))
|
||||||
ActionModifiers.Mark,
|
});
|
||||||
JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.databaseDeleteFeedback("");
|
this.databaseDeleteFeedback("");
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ export default class MongoShellTab extends TabsBase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataToLog: string = event.data.data.logData;
|
const dataToLog = { message: event.data.data.logData };
|
||||||
const logType: string = event.data.data.logType;
|
const logType: string = event.data.data.logType;
|
||||||
const shellTraceId: string = event.data.data.traceId || "none";
|
const shellTraceId: string = event.data.data.traceId || "none";
|
||||||
|
|
||||||
|
@ -4,12 +4,15 @@ import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
|||||||
import { appInsights } from "../appInsights";
|
import { appInsights } from "../appInsights";
|
||||||
import { configContext } from "../../ConfigContext";
|
import { configContext } from "../../ConfigContext";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
|
import { getDataExplorerWindow } from "../../Utils/WindowUtils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that persists telemetry data to the portal tables.
|
* Class that persists telemetry data to the portal tables.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function trace(action: Action, actionModifier: string = ActionModifiers.Mark, data?: unknown): void {
|
type TelemetryData = { [key: string]: unknown };
|
||||||
|
|
||||||
|
export function trace(action: Action, actionModifier: string = ActionModifiers.Mark, data?: TelemetryData): void {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: MessageTypes.TelemetryInfo,
|
type: MessageTypes.TelemetryInfo,
|
||||||
data: {
|
data: {
|
||||||
@ -22,7 +25,7 @@ export function trace(action: Action, actionModifier: string = ActionModifiers.M
|
|||||||
appInsights.trackEvent({ name: Action[action] }, getData(actionModifier, data));
|
appInsights.trackEvent({ name: Action[action] }, getData(actionModifier, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceStart(action: Action, data?: unknown): number {
|
export function traceStart(action: Action, data?: TelemetryData): number {
|
||||||
const timestamp: number = Date.now();
|
const timestamp: number = Date.now();
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: MessageTypes.TelemetryInfo,
|
type: MessageTypes.TelemetryInfo,
|
||||||
@ -38,7 +41,7 @@ export function traceStart(action: Action, data?: unknown): number {
|
|||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceSuccess(action: Action, data?: unknown, timestamp?: number): void {
|
export function traceSuccess(action: Action, data?: TelemetryData, timestamp?: number): void {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: MessageTypes.TelemetryInfo,
|
type: MessageTypes.TelemetryInfo,
|
||||||
data: {
|
data: {
|
||||||
@ -52,7 +55,7 @@ export function traceSuccess(action: Action, data?: unknown, timestamp?: number)
|
|||||||
appInsights.stopTrackEvent(Action[action], getData(ActionModifiers.Success, data));
|
appInsights.stopTrackEvent(Action[action], getData(ActionModifiers.Success, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceFailure(action: Action, data?: unknown, timestamp?: number): void {
|
export function traceFailure(action: Action, data?: TelemetryData, timestamp?: number): void {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: MessageTypes.TelemetryInfo,
|
type: MessageTypes.TelemetryInfo,
|
||||||
data: {
|
data: {
|
||||||
@ -66,7 +69,7 @@ export function traceFailure(action: Action, data?: unknown, timestamp?: number)
|
|||||||
appInsights.stopTrackEvent(Action[action], getData(ActionModifiers.Failed, data));
|
appInsights.stopTrackEvent(Action[action], getData(ActionModifiers.Failed, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceCancel(action: Action, data?: unknown, timestamp?: number): void {
|
export function traceCancel(action: Action, data?: TelemetryData, timestamp?: number): void {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: MessageTypes.TelemetryInfo,
|
type: MessageTypes.TelemetryInfo,
|
||||||
data: {
|
data: {
|
||||||
@ -80,7 +83,7 @@ export function traceCancel(action: Action, data?: unknown, timestamp?: number):
|
|||||||
appInsights.stopTrackEvent(Action[action], getData(ActionModifiers.Cancel, data));
|
appInsights.stopTrackEvent(Action[action], getData(ActionModifiers.Cancel, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceOpen(action: Action, data?: unknown, timestamp?: number): number {
|
export function traceOpen(action: Action, data?: TelemetryData, timestamp?: number): number {
|
||||||
const validTimestamp = timestamp || Date.now();
|
const validTimestamp = timestamp || Date.now();
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: MessageTypes.TelemetryInfo,
|
type: MessageTypes.TelemetryInfo,
|
||||||
@ -96,7 +99,7 @@ export function traceOpen(action: Action, data?: unknown, timestamp?: number): n
|
|||||||
return validTimestamp;
|
return validTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceMark(action: Action, data?: unknown, timestamp?: number): number {
|
export function traceMark(action: Action, data?: TelemetryData, timestamp?: number): number {
|
||||||
const validTimestamp = timestamp || Date.now();
|
const validTimestamp = timestamp || Date.now();
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: MessageTypes.TelemetryInfo,
|
type: MessageTypes.TelemetryInfo,
|
||||||
@ -112,15 +115,12 @@ export function traceMark(action: Action, data?: unknown, timestamp?: number): n
|
|||||||
return validTimestamp;
|
return validTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getData(actionModifier: string, data: unknown = {}): { [key: string]: string } | undefined {
|
function getData(actionModifier: string, data: TelemetryData = {}): { [key: string]: string } {
|
||||||
if (typeof data === "string") {
|
const dataExplorerWindow = getDataExplorerWindow(window);
|
||||||
data = { message: data };
|
|
||||||
}
|
|
||||||
if (typeof data === "object") {
|
|
||||||
return {
|
return {
|
||||||
// TODO: Need to `any` here since the window imports Explorer which can't be in strict mode yet
|
// 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
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
authType: (window as any).authType,
|
authType: dataExplorerWindow && (dataExplorerWindow as any).authType,
|
||||||
subscriptionId: userContext.subscriptionId as string,
|
subscriptionId: userContext.subscriptionId as string,
|
||||||
platform: configContext.platform,
|
platform: configContext.platform,
|
||||||
env: process.env.NODE_ENV as string,
|
env: process.env.NODE_ENV as string,
|
||||||
@ -128,5 +128,3 @@ function getData(actionModifier: string, data: unknown = {}): { [key: string]: s
|
|||||||
...data
|
...data
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
@ -6,6 +6,8 @@ import { ServerConnection } from "@jupyterlab/services";
|
|||||||
import { JupyterLabAppFactory } from "./JupyterLabAppFactory";
|
import { JupyterLabAppFactory } from "./JupyterLabAppFactory";
|
||||||
import { Action } from "../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { updateUserContext } from "../UserContext";
|
||||||
|
import { TerminalQueryParams } from "../Common/Constants";
|
||||||
|
|
||||||
const getUrlVars = (): { [key: string]: string } => {
|
const getUrlVars = (): { [key: string]: string } => {
|
||||||
const vars: { [key: string]: string } = {};
|
const vars: { [key: string]: string } = {};
|
||||||
@ -18,22 +20,22 @@ const getUrlVars = (): { [key: string]: string } => {
|
|||||||
|
|
||||||
const createServerSettings = (urlVars: { [key: string]: string }): ServerConnection.ISettings => {
|
const createServerSettings = (urlVars: { [key: string]: string }): ServerConnection.ISettings => {
|
||||||
let body: BodyInit;
|
let body: BodyInit;
|
||||||
if (urlVars.hasOwnProperty("terminalEndpoint")) {
|
if (urlVars.hasOwnProperty(TerminalQueryParams.TerminalEndpoint)) {
|
||||||
body = JSON.stringify({
|
body = JSON.stringify({
|
||||||
endpoint: urlVars["terminalEndpoint"]
|
endpoint: urlVars[TerminalQueryParams.TerminalEndpoint]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = urlVars["server"];
|
const server = urlVars[TerminalQueryParams.Server];
|
||||||
let options: Partial<ServerConnection.ISettings> = {
|
let options: Partial<ServerConnection.ISettings> = {
|
||||||
baseUrl: server,
|
baseUrl: server,
|
||||||
init: { body },
|
init: { body },
|
||||||
fetch: window.parent.fetch
|
fetch: window.parent.fetch
|
||||||
};
|
};
|
||||||
if (urlVars.hasOwnProperty("token")) {
|
if (urlVars.hasOwnProperty(TerminalQueryParams.Token)) {
|
||||||
options = {
|
options = {
|
||||||
baseUrl: server,
|
baseUrl: server,
|
||||||
token: urlVars["token"],
|
token: urlVars[TerminalQueryParams.Token],
|
||||||
init: { body },
|
init: { body },
|
||||||
fetch: window.parent.fetch
|
fetch: window.parent.fetch
|
||||||
};
|
};
|
||||||
@ -44,6 +46,12 @@ const createServerSettings = (urlVars: { [key: string]: string }): ServerConnect
|
|||||||
|
|
||||||
const main = async (): Promise<void> => {
|
const main = async (): Promise<void> => {
|
||||||
const urlVars = getUrlVars();
|
const urlVars = getUrlVars();
|
||||||
|
|
||||||
|
// Initialize userContext. Currently only subscrptionId is required by TelemetryProcessor
|
||||||
|
updateUserContext({
|
||||||
|
subscriptionId: urlVars[TerminalQueryParams.SubscriptionId]
|
||||||
|
});
|
||||||
|
|
||||||
const serverSettings = createServerSettings(urlVars);
|
const serverSettings = createServerSettings(urlVars);
|
||||||
|
|
||||||
const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, {
|
const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, {
|
||||||
@ -51,7 +59,7 @@ const main = async (): Promise<void> => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (urlVars.hasOwnProperty("terminal")) {
|
if (urlVars.hasOwnProperty(TerminalQueryParams.Terminal)) {
|
||||||
await JupyterLabAppFactory.createTerminalApp(serverSettings);
|
await JupyterLabAppFactory.createTerminalApp(serverSettings);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Only terminal is supported");
|
throw new Error("Only terminal is supported");
|
||||||
|
49
src/Utils/WindowUtils.test.ts
Normal file
49
src/Utils/WindowUtils.test.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { getDataExplorerWindow } from "./WindowUtils";
|
||||||
|
|
||||||
|
const createWindow = (dataExplorerPlatform: unknown, parent: Window): Window => {
|
||||||
|
// TODO: Need to `any` here since we're creating a mock window object
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const mockWindow: any = {};
|
||||||
|
if (dataExplorerPlatform !== undefined) {
|
||||||
|
mockWindow.dataExplorerPlatform = dataExplorerPlatform;
|
||||||
|
}
|
||||||
|
if (parent) {
|
||||||
|
mockWindow.parent = parent;
|
||||||
|
}
|
||||||
|
return mockWindow;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("WindowUtils", () => {
|
||||||
|
describe("getDataExplorerWindow", () => {
|
||||||
|
it("should return current window if current window has dataExplorerPlatform property", () => {
|
||||||
|
const currentWindow = createWindow(0, undefined);
|
||||||
|
|
||||||
|
expect(getDataExplorerWindow(currentWindow)).toEqual(currentWindow);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return current window's parent if current window's parent has dataExplorerPlatform property", () => {
|
||||||
|
const parentWindow = createWindow(0, undefined);
|
||||||
|
const currentWindow = createWindow(undefined, parentWindow);
|
||||||
|
|
||||||
|
expect(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 = createWindow(undefined, undefined);
|
||||||
|
|
||||||
|
// TODO: Need to `any` here since parent is a readonly property
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(parentWindow as any).parent = parentWindow; // If a window does not have a parent, its parent property is a reference to itself.
|
||||||
|
const currentWindow = createWindow(undefined, parentWindow);
|
||||||
|
|
||||||
|
expect(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 = createWindow(undefined, undefined);
|
||||||
|
const currentWindow = createWindow(undefined, parentWindow);
|
||||||
|
|
||||||
|
expect(getDataExplorerWindow(currentWindow)).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
17
src/Utils/WindowUtils.ts
Normal file
17
src/Utils/WindowUtils.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user