Migrate Mongo Shell tab to React (#900)

This commit is contained in:
vaidankarswapnil 2021-06-23 10:09:58 +05:30 committed by GitHub
parent f9bd12eaa6
commit f255387ccd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 154 additions and 89 deletions

View File

@ -143,7 +143,7 @@ src/Explorer/Tabs/DocumentsTab.ts
src/Explorer/Tabs/GraphTab.ts src/Explorer/Tabs/GraphTab.ts
src/Explorer/Tabs/MongoDocumentsTab.ts src/Explorer/Tabs/MongoDocumentsTab.ts
# src/Explorer/Tabs/MongoQueryTab.ts # src/Explorer/Tabs/MongoQueryTab.ts
src/Explorer/Tabs/MongoShellTab.ts # src/Explorer/Tabs/MongoShellTab.ts
src/Explorer/Tabs/NotebookV2Tab.ts src/Explorer/Tabs/NotebookV2Tab.ts
src/Explorer/Tabs/ScriptTabBase.ts src/Explorer/Tabs/ScriptTabBase.ts
src/Explorer/Tabs/StoredProcedureTab.ts src/Explorer/Tabs/StoredProcedureTab.ts

View File

@ -1,15 +0,0 @@
<iframe
name="explorer"
class="iframe"
style="width: 100%; height: 100%; border: 0px; padding: 0px; margin: 0px; overflow: hidden"
data-bind="
attr: {
src: url,
id: tabId
},
event:{
load: setContentFocus(event)
}"
title="Mongo Shell"
role="tabpanel"
></iframe>

View File

@ -0,0 +1,39 @@
import React from "react";
import * as DataModels from "../../../Contracts/DataModels";
import type { TabOptions } from "../../../Contracts/ViewModels";
import Explorer from "../../Explorer";
import TabsBase from "../TabsBase";
import MongoShellTabComponent, { IMongoShellTabAccessor, IMongoShellTabComponentProps } from "./MongoShellTabComponent";
export interface IMongoShellTabProps {
container: Explorer;
}
export class NewMongoShellTab extends TabsBase {
public queryText: string;
public currentQuery: string;
public partitionKey: DataModels.PartitionKey;
public iMongoShellTabComponentProps: IMongoShellTabComponentProps;
public iMongoShellTabAccessor: IMongoShellTabAccessor;
constructor(options: TabOptions, private props: IMongoShellTabProps) {
super(options);
this.iMongoShellTabComponentProps = {
collection: this.collection,
tabsBaseInstance: this,
container: this.props.container,
onMongoShellTabAccessor: (instance: IMongoShellTabAccessor) => {
this.iMongoShellTabAccessor = instance;
},
};
}
public render(): JSX.Element {
return <MongoShellTabComponent {...this.iMongoShellTabComponentProps} />;
}
public onTabClick(): void {
this.manager?.activateTab(this);
this.iMongoShellTabAccessor.onTabClickEvent();
}
}

View File

@ -1,67 +1,101 @@
import * as ko from "knockout"; import React, { Component } from "react";
import * as Constants from "../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import { configContext, Platform } from "../../ConfigContext"; import { configContext, Platform } from "../../../ConfigContext";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext"; import { userContext } from "../../../UserContext";
import { isInvalidParentFrameOrigin, isReadyMessage } from "../../Utils/MessageValidation"; import { isInvalidParentFrameOrigin, isReadyMessage } from "../../../Utils/MessageValidation";
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils";
import Explorer from "../Explorer"; import Explorer from "../../Explorer";
import template from "./MongoShellTab.html"; import TabsBase from "../TabsBase";
import TabsBase from "./TabsBase";
export default class MongoShellTab extends TabsBase { //eslint-disable-next-line
public readonly html = template; class MessageType {
public url: ko.Computed<string>; static IframeReady = "iframeready";
private _container: Explorer; static Notification = "notification";
static Log = "log";
}
//eslint-disable-next-line
class LogType {
static Information = "information";
static Warning = "warning";
static Verbose = "verbose";
static InProgress = "inprogress";
static StartTrace = "start";
static SuccessTrace = "success";
static FailureTrace = "failure";
}
export interface IMongoShellTabAccessor {
onTabClickEvent: () => void;
}
export interface IMongoShellTabComponentStates {
url: string;
}
export interface IMongoShellTabComponentProps {
collection: ViewModels.CollectionBase;
tabsBaseInstance: TabsBase;
container: Explorer;
onMongoShellTabAccessor: (instance: IMongoShellTabAccessor) => void;
}
export default class MongoShellTabComponent extends Component<
IMongoShellTabComponentProps,
IMongoShellTabComponentStates
> {
private _runtimeEndpoint: string; private _runtimeEndpoint: string;
private _logTraces: Map<string, number>; private _logTraces: Map<string, number>;
constructor(options: ViewModels.TabOptions) { constructor(props: IMongoShellTabComponentProps) {
super(options); super(props);
this._logTraces = new Map(); this._logTraces = new Map();
this._container = options.collection.container;
this.url = ko.computed<string>(() => {
const { databaseAccount: account } = userContext;
const resourceId = account?.id;
const accountName = account?.name;
const mongoEndpoint = account?.properties?.mongoEndpoint || account?.properties?.documentEndpoint;
this._runtimeEndpoint = configContext.platform === Platform.Hosted ? configContext.BACKEND_ENDPOINT : ""; this.state = {
const extensionEndpoint: string = configContext.BACKEND_ENDPOINT || this._runtimeEndpoint || ""; url: this.getURL(),
let baseUrl = "/content/mongoshell/dist/"; };
if (userContext.portalEnv === "localhost") {
baseUrl = "/content/mongoshell/";
}
return `${extensionEndpoint}${baseUrl}index.html?resourceId=${resourceId}&accountName=${accountName}&mongoEndpoint=${mongoEndpoint}`; props.onMongoShellTabAccessor({
onTabClickEvent: this.onTabClick.bind(this),
}); });
window.addEventListener("message", this.handleMessage.bind(this), false); window.addEventListener("message", this.handleMessage.bind(this), false);
} }
public setContentFocus(event: any): any { public getURL(): string {
// TODO: Work around cross origin security issue in Hosted Data Explorer by using Shell <-> Data Explorer messaging (253527) const { databaseAccount: account } = userContext;
// if(event.type === "load" && window.dataExplorerPlatform != PlatformType.Hosted) { const resourceId = account?.id;
// let activeShell = event.target.contentWindow && event.target.contentWindow.mongo && event.target.contentWindow.mongo.shells && event.target.contentWindow.mongo.shells[0]; const accountName = account?.name;
// activeShell && setTimeout(function(){ const mongoEndpoint = account?.properties?.mongoEndpoint || account?.properties?.documentEndpoint;
// activeShell.focus();
// },2000); this._runtimeEndpoint = configContext.platform === Platform.Hosted ? configContext.BACKEND_ENDPOINT : "";
// } const extensionEndpoint: string = configContext.BACKEND_ENDPOINT || this._runtimeEndpoint || "";
let baseUrl = "/content/mongoshell/dist/";
if (userContext.portalEnv === "localhost") {
baseUrl = "/content/mongoshell/";
}
return `${extensionEndpoint}${baseUrl}index.html?resourceId=${resourceId}&accountName=${accountName}&mongoEndpoint=${mongoEndpoint}`;
} }
//eslint-disable-next-line
public setContentFocus(event: React.SyntheticEvent<HTMLIFrameElement, Event>): void {}
public onTabClick(): void { public onTabClick(): void {
super.onTabClick(); this.props.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
this.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
} }
public handleMessage(event: MessageEvent) { public handleMessage(event: MessageEvent): void {
if (isInvalidParentFrameOrigin(event)) { if (isInvalidParentFrameOrigin(event)) {
return; return;
} }
const shellIframe: HTMLIFrameElement = <HTMLIFrameElement>document.getElementById(this.tabId); const shellIframe: HTMLIFrameElement = document.getElementById(
this.props.tabsBaseInstance.tabId
) as HTMLIFrameElement;
if (!shellIframe) { if (!shellIframe) {
return; return;
@ -73,9 +107,9 @@ export default class MongoShellTab extends TabsBase {
return; return;
} }
if (event.data.eventType == MessageType.IframeReady) { if (event.data.eventType === MessageType.IframeReady) {
this.handleReadyMessage(event, shellIframe); this.handleReadyMessage(event, shellIframe);
} else if (event.data.eventType == MessageType.Notification) { } else if (event.data.eventType === MessageType.Notification) {
this.handleNotificationMessage(event, shellIframe); this.handleNotificationMessage(event, shellIframe);
} else { } else {
this.handleLogMessage(event, shellIframe); this.handleLogMessage(event, shellIframe);
@ -98,8 +132,8 @@ export default class MongoShellTab extends TabsBase {
documentEndpoint.length - documentEndpoint.length -
(Constants.MongoDBAccounts.protocol.length + 2 + Constants.MongoDBAccounts.defaultPort.length) (Constants.MongoDBAccounts.protocol.length + 2 + Constants.MongoDBAccounts.defaultPort.length)
) + Constants.MongoDBAccounts.defaultPort.toString(); ) + Constants.MongoDBAccounts.defaultPort.toString();
const databaseId = this.collection.databaseId; const databaseId = this.props.collection.databaseId;
const collectionId = this.collection.id(); const collectionId = this.props.collection.id();
const apiEndpoint = configContext.BACKEND_ENDPOINT; const apiEndpoint = configContext.BACKEND_ENDPOINT;
const encryptedAuthToken: string = userContext.accessToken; const encryptedAuthToken: string = userContext.accessToken;
@ -121,6 +155,7 @@ export default class MongoShellTab extends TabsBase {
); );
} }
//eslint-disable-next-line
private handleLogMessage(event: MessageEvent, shellIframe: HTMLIFrameElement) { private handleLogMessage(event: MessageEvent, shellIframe: HTMLIFrameElement) {
if (!("logType" in event.data.data) || typeof event.data.data["logType"] !== "string") { if (!("logType" in event.data.data) || typeof event.data.data["logType"] !== "string") {
return; return;
@ -144,6 +179,7 @@ export default class MongoShellTab extends TabsBase {
TelemetryProcessor.trace(Action.MongoShell, ActionModifiers.Mark, dataToLog); TelemetryProcessor.trace(Action.MongoShell, ActionModifiers.Mark, dataToLog);
break; break;
case LogType.StartTrace: case LogType.StartTrace:
//eslint-disable-next-line
const telemetryTraceId: number = TelemetryProcessor.traceStart(Action.MongoShell, dataToLog); const telemetryTraceId: number = TelemetryProcessor.traceStart(Action.MongoShell, dataToLog);
this._logTraces.set(shellTraceId, telemetryTraceId); this._logTraces.set(shellTraceId, telemetryTraceId);
break; break;
@ -168,6 +204,7 @@ export default class MongoShellTab extends TabsBase {
} }
} }
//eslint-disable-next-line
private handleNotificationMessage(event: MessageEvent, shellIframe: HTMLIFrameElement) { private handleNotificationMessage(event: MessageEvent, shellIframe: HTMLIFrameElement) {
if (!("logType" in event.data.data) || typeof event.data.data["logType"] !== "string") { if (!("logType" in event.data.data) || typeof event.data.data["logType"] !== "string") {
return; return;
@ -188,20 +225,19 @@ export default class MongoShellTab extends TabsBase {
return logConsoleProgress(dataToLog); return logConsoleProgress(dataToLog);
} }
} }
}
class MessageType { render(): JSX.Element {
static IframeReady: string = "iframeready"; return (
static Notification: string = "notification"; <iframe
static Log: string = "log"; name="explorer"
} className="iframe"
style={{ width: "100%", height: "100%", border: 0, padding: 0, margin: 0, overflow: "hidden" }}
class LogType { src={this.state.url}
static Information: string = "information"; id={this.props.tabsBaseInstance.tabId}
static Warning: string = "warning"; onLoad={(event) => this.setContentFocus(event)}
static Verbose: string = "verbose"; title="Mongo Shell"
static InProgress: string = "inprogress"; role="tabpanel"
static StartTrace: string = "start"; ></iframe>
static SuccessTrace: string = "success"; );
static FailureTrace: string = "failure"; }
} }

View File

@ -29,7 +29,7 @@ import DocumentsTab from "../Tabs/DocumentsTab";
import GraphTab from "../Tabs/GraphTab"; import GraphTab from "../Tabs/GraphTab";
import MongoDocumentsTab from "../Tabs/MongoDocumentsTab"; import MongoDocumentsTab from "../Tabs/MongoDocumentsTab";
import { NewMongoQueryTab } from "../Tabs/MongoQueryTab/MongoQueryTab"; import { NewMongoQueryTab } from "../Tabs/MongoQueryTab/MongoQueryTab";
import MongoShellTab from "../Tabs/MongoShellTab"; import { NewMongoShellTab } from "../Tabs/MongoShellTab/MongoShellTab";
import { NewQueryTab } from "../Tabs/QueryTab/QueryTab"; import { NewQueryTab } from "../Tabs/QueryTab/QueryTab";
import QueryTablesTab from "../Tabs/QueryTablesTab"; import QueryTablesTab from "../Tabs/QueryTablesTab";
import { CollectionSettingsTabV2 } from "../Tabs/SettingsTabV2"; import { CollectionSettingsTabV2 } from "../Tabs/SettingsTabV2";
@ -703,22 +703,27 @@ export default class Collection implements ViewModels.Collection {
public onNewMongoShellClick() { public onNewMongoShellClick() {
const mongoShellTabs = this.container.tabsManager.getTabs( const mongoShellTabs = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.MongoShell ViewModels.CollectionTabKind.MongoShell
) as MongoShellTab[]; ) as NewMongoShellTab[];
let index = 1; let index = 1;
if (mongoShellTabs.length > 0) { if (mongoShellTabs.length > 0) {
index = mongoShellTabs[mongoShellTabs.length - 1].index + 1; index = mongoShellTabs[mongoShellTabs.length - 1].index + 1;
} }
const mongoShellTab: MongoShellTab = new MongoShellTab({ const mongoShellTab: NewMongoShellTab = new NewMongoShellTab(
tabKind: ViewModels.CollectionTabKind.MongoShell, {
title: "Shell " + index, tabKind: ViewModels.CollectionTabKind.MongoShell,
tabPath: "", title: "Shell " + index,
collection: this, tabPath: "",
node: this, collection: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoShell`, node: this,
index: index, hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoShell`,
}); index: index,
},
{
container: this.container,
}
);
this.container.tabsManager.activateNewTab(mongoShellTab); this.container.tabsManager.activateNewTab(mongoShellTab);
} }