From 0c80c45e22053136e5c108d80b9ed283e042b096 Mon Sep 17 00:00:00 2001 From: Laurent Nguyen Date: Wed, 2 Dec 2020 14:22:02 +0100 Subject: [PATCH] New MongoQueryTab and component running nteract --- src/Explorer/ComponentRegisterer.ts | 2 +- src/Explorer/Explorer.ts | 2 - .../MongoQueryComponent.tsx | 143 ++++++++++++++++++ .../MongoQueryComponentAdapter.tsx | 43 ++++++ src/Explorer/Tabs/MongoDocumentsTabV2.html | 1 + src/Explorer/Tabs/MongoDocumentsTabV2.ts | 45 ++++++ src/Explorer/Tabs/NotebookTabBase.ts | 50 ++++++ src/Explorer/Tabs/NotebookV2Tab.ts | 44 +----- src/Explorer/Tabs/TabComponents.ts | 10 ++ src/Explorer/Tree/Collection.ts | 12 +- 10 files changed, 306 insertions(+), 46 deletions(-) create mode 100644 src/Explorer/Notebook/MongoQueryComponent/MongoQueryComponent.tsx create mode 100644 src/Explorer/Notebook/MongoQueryComponent/MongoQueryComponentAdapter.tsx create mode 100644 src/Explorer/Tabs/MongoDocumentsTabV2.html create mode 100644 src/Explorer/Tabs/MongoDocumentsTabV2.ts create mode 100644 src/Explorer/Tabs/NotebookTabBase.ts diff --git a/src/Explorer/ComponentRegisterer.ts b/src/Explorer/ComponentRegisterer.ts index 1e28c2d8d..a1b903331 100644 --- a/src/Explorer/ComponentRegisterer.ts +++ b/src/Explorer/ComponentRegisterer.ts @@ -26,7 +26,7 @@ ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponent ko.components.register("tabs-manager", TabsManagerKOComponent()); // Collection Tabs -ko.components.register("documents-tab", new TabComponents.DocumentsTab()); +ko.components.register("documents-tab", new TabComponents.MongoDocumentsTabV2()); ko.components.register("mongo-documents-tab", new TabComponents.MongoDocumentsTab()); ko.components.register("stored-procedure-tab", new TabComponents.StoredProcedureTab()); ko.components.register("trigger-tab", new TabComponents.TriggerTab()); diff --git a/src/Explorer/Explorer.ts b/src/Explorer/Explorer.ts index bef261953..9dd16e39f 100644 --- a/src/Explorer/Explorer.ts +++ b/src/Explorer/Explorer.ts @@ -2379,13 +2379,11 @@ export default class Explorer { this.tabsManager.activateTab(notebookTab); } else { const options: NotebookTabOptions = { - account: userContext.databaseAccount, tabKind: ViewModels.CollectionTabKind.NotebookV2, node: null, title: notebookContentItem.name, tabPath: notebookContentItem.path, collection: null, - masterKey: userContext.masterKey || "", hashLocation: "notebooks", isActive: ko.observable(false), isTabsContentExpanded: ko.observable(true), diff --git a/src/Explorer/Notebook/MongoQueryComponent/MongoQueryComponent.tsx b/src/Explorer/Notebook/MongoQueryComponent/MongoQueryComponent.tsx new file mode 100644 index 000000000..902b3bbc6 --- /dev/null +++ b/src/Explorer/Notebook/MongoQueryComponent/MongoQueryComponent.tsx @@ -0,0 +1,143 @@ +import * as React from "react"; +import { Dispatch } from "redux"; +import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor"; +import { PrimaryButton } from "office-ui-fabric-react"; +import { ChoiceGroup, IChoiceGroupOption } from "office-ui-fabric-react/lib/ChoiceGroup"; +import Input from "@nteract/stateful-components/lib/inputs/input"; +import Editor from "@nteract/stateful-components/lib/inputs/editor"; +import { Source } from "@nteract/presentational-components"; +import Outputs from "@nteract/stateful-components/lib/outputs"; +import { KernelOutputError, StreamText } from "@nteract/outputs"; +import TransformMedia from "@nteract/stateful-components/lib/outputs/transform-media"; +import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor"; +import { actions, selectors, AppState, ContentRef, KernelRef } from "@nteract/core"; +import { connect } from "react-redux"; + +interface MongoQueryComponentPureProps { + contentRef: ContentRef; + kernelRef: KernelRef; +} + +interface MongoQueryComponentDispatchProps { + runCell: (contentRef: ContentRef, cellId: string) => void; +} + +type OutputType = "rich" | "json"; + +interface MongoQueryComponentState { + outputType: OutputType; +} + +const options: IChoiceGroupOption[] = [ + { key: "rich", text: "Rich Output" }, + { key: "json", text: "Json Output" } +]; + +type MongoQueryComponentProps = MongoQueryComponentPureProps & StateProps & MongoQueryComponentDispatchProps; +export class MongoQueryComponent extends React.Component { + constructor(props: MongoQueryComponentProps) { + super(props); + this.state = { + outputType: "rich" + }; + } + + private onExecute = () => { + this.props.runCell(this.props.contentRef, this.props.firstCellId); + }; + + private onOutputTypeChange = (e: React.FormEvent): void => { + const outputType = e.target.value as OutputType; + this.setState({ outputType }); + }; + + render(): JSX.Element { + const editor = { + monaco: (props: PassedEditorProps) => + }; + + const { firstCellId: id, contentRef } = this.props; + + if (!id) { + return <>NO FIRST CELL; + } + + return ( +
+
+ + + + {editor} + + + + + +
+ + + + + + +
+
+ ); + } +} + +interface StateProps { + firstCellId: string; +} +interface InitialProps { + contentRef: string; +} + +// Redux +const makeMapStateToProps = (state: AppState, initialProps: InitialProps) => { + const { contentRef } = initialProps; + console.log("makeMapStateToProps"); + + const mapStateToProps = (state: AppState) => { + let firstCellId; + + const content = selectors.content(state, { contentRef }); + + console.log("Looking for first cell", content?.type, state); + if (content?.type === "notebook") { + const cellOrder = selectors.notebook.cellOrder(content.model); + if (cellOrder.size > 0) { + firstCellId = cellOrder.first() as string; + } + } + + return { + firstCellId + }; + }; + return mapStateToProps; +}; + +const makeMapDispatchToProps = (/* initialDispatch: Dispatch, initialProps: MongoQueryComponentProps */) => { + const mapDispatchToProps = (dispatch: Dispatch) => { + return { + runCell: (contentRef: ContentRef, cellId: string) => { + return dispatch( + actions.executeCell({ + contentRef, + id: cellId + }) + ); + } + }; + }; + return mapDispatchToProps; +}; + +export default connect(makeMapStateToProps, makeMapDispatchToProps)(MongoQueryComponent); diff --git a/src/Explorer/Notebook/MongoQueryComponent/MongoQueryComponentAdapter.tsx b/src/Explorer/Notebook/MongoQueryComponent/MongoQueryComponentAdapter.tsx new file mode 100644 index 000000000..10e0bb6f2 --- /dev/null +++ b/src/Explorer/Notebook/MongoQueryComponent/MongoQueryComponentAdapter.tsx @@ -0,0 +1,43 @@ +import * as React from "react"; + +import { ReactAdapter } from "../../../Bindings/ReactBindingHandler"; +import { + NotebookComponentBootstrapper, + NotebookComponentBootstrapperOptions +} from "../NotebookComponent/NotebookComponentBootstrapper"; +import MongoQueryComponent from "../MongoQueryComponent/MongoQueryComponent"; +import { actions, createContentRef, createKernelRef, KernelRef } from "@nteract/core"; +import { Provider } from "react-redux"; + +export class MongoQueryComponentAdapter extends NotebookComponentBootstrapper implements ReactAdapter { + public parameters: unknown; + private kernelRef: KernelRef; + + constructor(options: NotebookComponentBootstrapperOptions) { + super(options); + + if (!this.contentRef) { + this.contentRef = createContentRef(); + this.kernelRef = createKernelRef(); + + // Request fetching notebook content + this.getStore().dispatch( + actions.fetchContent({ + filepath: "mongo.ipynb", + params: {}, + kernelRef: this.kernelRef, + contentRef: this.contentRef + }) + ); + } + } + + public renderComponent(): JSX.Element { + console.log("Rendering from adapter"); + return ( + + ; + + ); + } +} diff --git a/src/Explorer/Tabs/MongoDocumentsTabV2.html b/src/Explorer/Tabs/MongoDocumentsTabV2.html new file mode 100644 index 000000000..acb4fa878 --- /dev/null +++ b/src/Explorer/Tabs/MongoDocumentsTabV2.html @@ -0,0 +1 @@ +
diff --git a/src/Explorer/Tabs/MongoDocumentsTabV2.ts b/src/Explorer/Tabs/MongoDocumentsTabV2.ts new file mode 100644 index 000000000..8ebe595a5 --- /dev/null +++ b/src/Explorer/Tabs/MongoDocumentsTabV2.ts @@ -0,0 +1,45 @@ +import * as Q from "q"; +import NotebookTabBase, { NotebookTabBaseOptions } from "./NotebookTabBase"; +import { MongoQueryComponentAdapter } from "../Notebook/MongoQueryComponent/MongoQueryComponentAdapter"; + +export default class MongoDocumentsTabV2 extends NotebookTabBase { + private mongoQueryComponentAdapter: MongoQueryComponentAdapter; + + constructor(options: NotebookTabBaseOptions) { + super(options); + this.mongoQueryComponentAdapter = new MongoQueryComponentAdapter({ + contentRef: undefined, + notebookClient: NotebookTabBase.clientManager + }); + } + + public onCloseTabButtonClick(): Q.Promise { + super.onCloseTabButtonClick(); + + // const cleanup = () => { + // this.notebookComponentAdapter.notebookShutdown(); + // this.isActive(false); + // super.onCloseTabButtonClick(); + // }; + + // if (this.notebookComponentAdapter.isContentDirty()) { + // this.container.showOkCancelModalDialog( + // "Close without saving?", + // `File has unsaved changes, close without saving?`, + // "Close", + // cleanup, + // "Cancel", + // undefined + // ); + // return Q.resolve(null); + // } else { + // cleanup(); + // return Q.resolve(null); + // } + return undefined; + } + + protected buildCommandBarOptions(): void { + this.updateNavbarWithTabsButtons(); + } +} diff --git a/src/Explorer/Tabs/NotebookTabBase.ts b/src/Explorer/Tabs/NotebookTabBase.ts new file mode 100644 index 000000000..1ed0830cf --- /dev/null +++ b/src/Explorer/Tabs/NotebookTabBase.ts @@ -0,0 +1,50 @@ +import * as ViewModels from "../../Contracts/ViewModels"; +import TabsBase from "./TabsBase"; + +import { NotebookClientV2 } from "../Notebook/NotebookClientV2"; +import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; +import Explorer from "../Explorer"; +import { ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; +import { Areas } from "../../Common/Constants"; + +export interface NotebookTabBaseOptions extends ViewModels.TabOptions { + container: Explorer; +} + +/** + * Every notebook-based tab inherits from this class. It holds the static reference to a notebook client (singleton) + */ +export default class NotebookTabBase extends TabsBase { + protected static clientManager: NotebookClientV2; + protected container: Explorer; + + constructor(options: NotebookTabBaseOptions) { + super(options); + + this.container = options.container; + + if (!NotebookTabBase.clientManager) { + NotebookTabBase.clientManager = new NotebookClientV2({ + connectionInfo: this.container.notebookServerInfo(), + databaseAccountName: this.container.databaseAccount().name, + defaultExperience: this.container.defaultExperience(), + contentProvider: this.container.notebookManager?.notebookContentProvider + }); + } + } + + /** + * Override base behavior + */ + protected getContainer(): Explorer { + return this.container; + } + + protected traceTelemetry(actionType: number): void { + TelemetryProcessor.trace(actionType, ActionModifiers.Mark, { + databaseAccountName: this.container.databaseAccount() && this.container.databaseAccount().name, + defaultExperience: this.container.defaultExperience && this.container.defaultExperience(), + dataExplorerArea: Areas.Notebook + }); + } +} diff --git a/src/Explorer/Tabs/NotebookV2Tab.ts b/src/Explorer/Tabs/NotebookV2Tab.ts index 88a692ec9..7b9f087f0 100644 --- a/src/Explorer/Tabs/NotebookV2Tab.ts +++ b/src/Explorer/Tabs/NotebookV2Tab.ts @@ -2,9 +2,7 @@ import * as _ from "underscore"; import * as Q from "q"; import * as ko from "knockout"; -import * as ViewModels from "../../Contracts/ViewModels"; import * as DataModels from "../../Contracts/DataModels"; -import TabsBase from "./TabsBase"; import NewCellIcon from "../../../images/notebook/Notebook-insert-cell.svg"; import CutIcon from "../../../images/notebook/Notebook-cut.svg"; @@ -17,31 +15,25 @@ import SaveIcon from "../../../images/save-cosmos.svg"; import ClearAllOutputsIcon from "../../../images/notebook/Notebook-clear-all-outputs.svg"; import InterruptKernelIcon from "../../../images/notebook/Notebook-stop.svg"; import KillKernelIcon from "../../../images/notebook/Notebook-stop.svg"; -import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; -import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; -import { Areas, ArmApiVersions } from "../../Common/Constants"; +import { Action } from "../../Shared/Telemetry/TelemetryConstants"; +import { ArmApiVersions } from "../../Common/Constants"; import { CommandBarComponentButtonFactory } from "../Menus/CommandBar/CommandBarComponentButtonFactory"; import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent"; import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"; import { NotebookComponentAdapter } from "../Notebook/NotebookComponent/NotebookComponentAdapter"; import { NotebookConfigurationUtils } from "../../Utils/NotebookConfigurationUtils"; -import { KernelSpecsDisplay, NotebookClientV2 } from "../Notebook/NotebookClientV2"; +import { KernelSpecsDisplay } from "../Notebook/NotebookClientV2"; import { configContext } from "../../ConfigContext"; -import Explorer from "../Explorer"; import { NotebookContentItem } from "../Notebook/NotebookContentItem"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; import { toJS, stringifyNotebook } from "@nteract/commutable"; +import NotebookTabBase, { NotebookTabBaseOptions } from "./NotebookTabBase"; -export interface NotebookTabOptions extends ViewModels.TabOptions { - account: DataModels.DatabaseAccount; - masterKey: string; - container: Explorer; +export interface NotebookTabOptions extends NotebookTabBaseOptions { notebookContentItem: NotebookContentItem; } -export default class NotebookTabV2 extends TabsBase { - private static clientManager: NotebookClientV2; - private container: Explorer; +export default class NotebookTabV2 extends NotebookTabBase { public notebookPath: ko.Observable; private selectedSparkPool: ko.Observable; private notebookComponentAdapter: NotebookComponentAdapter; @@ -50,16 +42,6 @@ export default class NotebookTabV2 extends TabsBase { super(options); this.container = options.container; - - if (!NotebookTabV2.clientManager) { - NotebookTabV2.clientManager = new NotebookClientV2({ - connectionInfo: this.container.notebookServerInfo(), - databaseAccountName: this.container.databaseAccount().name, - defaultExperience: this.container.defaultExperience(), - contentProvider: this.container.notebookManager?.notebookContentProvider - }); - } - this.notebookPath = ko.observable(options.notebookContentItem.path); this.container.notebookServerInfo.subscribe((newValue: DataModels.NotebookWorkspaceConnectionInfo) => { @@ -69,7 +51,7 @@ export default class NotebookTabV2 extends TabsBase { this.notebookComponentAdapter = new NotebookComponentAdapter({ contentItem: options.notebookContentItem, notebooksBasePath: this.container.getNotebookBasePath(), - notebookClient: NotebookTabV2.clientManager, + notebookClient: NotebookTabBase.clientManager, onUpdateKernelInfo: this.onKernelUpdate }); @@ -115,10 +97,6 @@ export default class NotebookTabV2 extends TabsBase { return await this.configureServiceEndpoints(this.notebookComponentAdapter.getCurrentKernelName()); } - protected getContainer(): Explorer { - return this.container; - } - protected getTabsButtons(): CommandButtonComponentProps[] { const availableKernels = NotebookTabV2.clientManager.getAvailableKernelSpecs(); @@ -493,12 +471,4 @@ export default class NotebookTabV2 extends TabsBase { this.container.copyNotebook(notebookContent.name, content); }; - - private traceTelemetry(actionType: number) { - TelemetryProcessor.trace(actionType, ActionModifiers.Mark, { - databaseAccountName: this.container.databaseAccount() && this.container.databaseAccount().name, - defaultExperience: this.container.defaultExperience && this.container.defaultExperience(), - dataExplorerArea: Areas.Notebook - }); - } } diff --git a/src/Explorer/Tabs/TabComponents.ts b/src/Explorer/Tabs/TabComponents.ts index 2700bce07..3f4b18120 100644 --- a/src/Explorer/Tabs/TabComponents.ts +++ b/src/Explorer/Tabs/TabComponents.ts @@ -5,6 +5,7 @@ import SparkMasterTabTemplate from "./SparkMasterTab.html"; import NotebookV2TabTemplate from "./NotebookV2Tab.html"; import TerminalTabTemplate from "./TerminalTab.html"; import MongoDocumentsTabTemplate from "./MongoDocumentsTab.html"; +import MongoDocumentsTabV2Template from "./MongoDocumentsTabV2.html"; import MongoQueryTabTemplate from "./MongoQueryTab.html"; import MongoShellTabTemplate from "./MongoShellTab.html"; import QueryTabTemplate from "./QueryTab.html"; @@ -106,6 +107,15 @@ export class MongoQueryTab { } } +export class MongoDocumentsTabV2 { + constructor() { + return { + viewModel: TabComponent, + template: MongoDocumentsTabV2Template + }; + } +} + export class MongoShellTab { constructor() { return { diff --git a/src/Explorer/Tree/Collection.ts b/src/Explorer/Tree/Collection.ts index 897752b92..c1dbfc309 100644 --- a/src/Explorer/Tree/Collection.ts +++ b/src/Explorer/Tree/Collection.ts @@ -24,6 +24,7 @@ import ConflictsTab from "../Tabs/ConflictsTab"; import DocumentsTab from "../Tabs/DocumentsTab"; import GraphTab from "../Tabs/GraphTab"; import MongoDocumentsTab from "../Tabs/MongoDocumentsTab"; +import MongoDocumentsTabV2 from "../Tabs/MongoDocumentsTabV2"; import MongoQueryTab from "../Tabs/MongoQueryTab"; import MongoShellTab from "../Tabs/MongoShellTab"; import QueryTab from "../Tabs/QueryTab"; @@ -506,11 +507,11 @@ export default class Collection implements ViewModels.Collection { dataExplorerArea: Constants.Areas.ResourceTree }); - const mongoDocumentsTabs: MongoDocumentsTab[] = this.container.tabsManager.getTabs( + const mongoDocumentsTabs: MongoDocumentsTabV2[] = this.container.tabsManager.getTabs( ViewModels.CollectionTabKind.Documents, tab => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id() - ) as MongoDocumentsTab[]; - let mongoDocumentsTab: MongoDocumentsTab = mongoDocumentsTabs && mongoDocumentsTabs[0]; + ) as MongoDocumentsTabV2[]; + let mongoDocumentsTab: MongoDocumentsTabV2 = mongoDocumentsTabs && mongoDocumentsTabs[0]; if (mongoDocumentsTab) { this.container.tabsManager.activateTab(mongoDocumentsTab); @@ -525,9 +526,8 @@ export default class Collection implements ViewModels.Collection { }); this.documentIds([]); - mongoDocumentsTab = new MongoDocumentsTab({ - partitionKey: this.partitionKey, - documentIds: this.documentIds, + mongoDocumentsTab = new MongoDocumentsTabV2({ + container: this.container, tabKind: ViewModels.CollectionTabKind.Documents, title: "Documents", tabPath: "",