mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-20 09:20:16 +00:00
Migrate Mongo Shell tab to React (#900)
This commit is contained in:
39
src/Explorer/Tabs/MongoShellTab/MongoShellTab.tsx
Normal file
39
src/Explorer/Tabs/MongoShellTab/MongoShellTab.tsx
Normal 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();
|
||||
}
|
||||
}
|
||||
243
src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx
Normal file
243
src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx
Normal file
@@ -0,0 +1,243 @@
|
||||
import React, { Component } from "react";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import { configContext, Platform } from "../../../ConfigContext";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { isInvalidParentFrameOrigin, isReadyMessage } from "../../../Utils/MessageValidation";
|
||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import TabsBase from "../TabsBase";
|
||||
|
||||
//eslint-disable-next-line
|
||||
class MessageType {
|
||||
static IframeReady = "iframeready";
|
||||
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 _logTraces: Map<string, number>;
|
||||
|
||||
constructor(props: IMongoShellTabComponentProps) {
|
||||
super(props);
|
||||
this._logTraces = new Map();
|
||||
|
||||
this.state = {
|
||||
url: this.getURL(),
|
||||
};
|
||||
|
||||
props.onMongoShellTabAccessor({
|
||||
onTabClickEvent: this.onTabClick.bind(this),
|
||||
});
|
||||
|
||||
window.addEventListener("message", this.handleMessage.bind(this), false);
|
||||
}
|
||||
|
||||
public getURL(): 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 : "";
|
||||
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 {
|
||||
this.props.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
|
||||
}
|
||||
|
||||
public handleMessage(event: MessageEvent): void {
|
||||
if (isInvalidParentFrameOrigin(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shellIframe: HTMLIFrameElement = document.getElementById(
|
||||
this.props.tabsBaseInstance.tabId
|
||||
) as HTMLIFrameElement;
|
||||
|
||||
if (!shellIframe) {
|
||||
return;
|
||||
}
|
||||
if (typeof event.data !== "object" || event.data["signature"] !== "mongoshell") {
|
||||
return;
|
||||
}
|
||||
if (!("data" in event.data) || !("eventType" in event.data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data.eventType === MessageType.IframeReady) {
|
||||
this.handleReadyMessage(event, shellIframe);
|
||||
} else if (event.data.eventType === MessageType.Notification) {
|
||||
this.handleNotificationMessage(event, shellIframe);
|
||||
} else {
|
||||
this.handleLogMessage(event, shellIframe);
|
||||
}
|
||||
}
|
||||
|
||||
private handleReadyMessage(event: MessageEvent, shellIframe: HTMLIFrameElement) {
|
||||
if (!isReadyMessage(event)) {
|
||||
return;
|
||||
}
|
||||
const { databaseAccount } = userContext;
|
||||
|
||||
const authorization: string = userContext.authorizationToken || "";
|
||||
const resourceId = databaseAccount?.id;
|
||||
const accountName = databaseAccount?.name;
|
||||
const documentEndpoint = databaseAccount?.properties.mongoEndpoint || databaseAccount?.properties.documentEndpoint;
|
||||
const mongoEndpoint =
|
||||
documentEndpoint.substr(
|
||||
Constants.MongoDBAccounts.protocol.length + 3,
|
||||
documentEndpoint.length -
|
||||
(Constants.MongoDBAccounts.protocol.length + 2 + Constants.MongoDBAccounts.defaultPort.length)
|
||||
) + Constants.MongoDBAccounts.defaultPort.toString();
|
||||
const databaseId = this.props.collection.databaseId;
|
||||
const collectionId = this.props.collection.id();
|
||||
const apiEndpoint = configContext.BACKEND_ENDPOINT;
|
||||
const encryptedAuthToken: string = userContext.accessToken;
|
||||
|
||||
shellIframe.contentWindow.postMessage(
|
||||
{
|
||||
signature: "dataexplorer",
|
||||
data: {
|
||||
resourceId: resourceId,
|
||||
accountName: accountName,
|
||||
mongoEndpoint: mongoEndpoint,
|
||||
authorization: authorization,
|
||||
databaseId: databaseId,
|
||||
collectionId: collectionId,
|
||||
encryptedAuthToken: encryptedAuthToken,
|
||||
apiEndpoint: apiEndpoint,
|
||||
},
|
||||
},
|
||||
configContext.BACKEND_ENDPOINT
|
||||
);
|
||||
}
|
||||
|
||||
//eslint-disable-next-line
|
||||
private handleLogMessage(event: MessageEvent, shellIframe: HTMLIFrameElement) {
|
||||
if (!("logType" in event.data.data) || typeof event.data.data["logType"] !== "string") {
|
||||
return;
|
||||
}
|
||||
if (!("logData" in event.data.data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataToLog = { message: event.data.data.logData };
|
||||
const logType: string = event.data.data.logType;
|
||||
const shellTraceId: string = event.data.data.traceId || "none";
|
||||
|
||||
switch (logType) {
|
||||
case LogType.Information:
|
||||
TelemetryProcessor.trace(Action.MongoShell, ActionModifiers.Success, dataToLog);
|
||||
break;
|
||||
case LogType.Warning:
|
||||
TelemetryProcessor.trace(Action.MongoShell, ActionModifiers.Failed, dataToLog);
|
||||
break;
|
||||
case LogType.Verbose:
|
||||
TelemetryProcessor.trace(Action.MongoShell, ActionModifiers.Mark, dataToLog);
|
||||
break;
|
||||
case LogType.StartTrace:
|
||||
//eslint-disable-next-line
|
||||
const telemetryTraceId: number = TelemetryProcessor.traceStart(Action.MongoShell, dataToLog);
|
||||
this._logTraces.set(shellTraceId, telemetryTraceId);
|
||||
break;
|
||||
case LogType.SuccessTrace:
|
||||
if (this._logTraces.has(shellTraceId)) {
|
||||
const originalTelemetryTraceId: number = this._logTraces.get(shellTraceId);
|
||||
TelemetryProcessor.traceSuccess(Action.MongoShell, dataToLog, originalTelemetryTraceId);
|
||||
this._logTraces.delete(shellTraceId);
|
||||
} else {
|
||||
TelemetryProcessor.trace(Action.MongoShell, ActionModifiers.Success, dataToLog);
|
||||
}
|
||||
break;
|
||||
case LogType.FailureTrace:
|
||||
if (this._logTraces.has(shellTraceId)) {
|
||||
const originalTelemetryTraceId: number = this._logTraces.get(shellTraceId);
|
||||
TelemetryProcessor.traceFailure(Action.MongoShell, dataToLog, originalTelemetryTraceId);
|
||||
this._logTraces.delete(shellTraceId);
|
||||
} else {
|
||||
TelemetryProcessor.trace(Action.MongoShell, ActionModifiers.Failed, dataToLog);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//eslint-disable-next-line
|
||||
private handleNotificationMessage(event: MessageEvent, shellIframe: HTMLIFrameElement) {
|
||||
if (!("logType" in event.data.data) || typeof event.data.data["logType"] !== "string") {
|
||||
return;
|
||||
}
|
||||
if (!("logData" in event.data.data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataToLog: string = event.data.data.logData;
|
||||
const logType: string = event.data.data.logType;
|
||||
|
||||
switch (logType) {
|
||||
case LogType.Information:
|
||||
return logConsoleInfo(dataToLog);
|
||||
case LogType.Warning:
|
||||
return logConsoleError(dataToLog);
|
||||
case LogType.InProgress:
|
||||
return logConsoleProgress(dataToLog);
|
||||
}
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<iframe
|
||||
name="explorer"
|
||||
className="iframe"
|
||||
style={{ width: "100%", height: "100%", border: 0, padding: 0, margin: 0, overflow: "hidden" }}
|
||||
src={this.state.url}
|
||||
id={this.props.tabsBaseInstance.tabId}
|
||||
onLoad={(event) => this.setContentFocus(event)}
|
||||
title="Mongo Shell"
|
||||
role="tabpanel"
|
||||
></iframe>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user