diff --git a/.eslintignore b/.eslintignore index ae73b21a2..c23885881 100644 --- a/.eslintignore +++ b/.eslintignore @@ -146,7 +146,7 @@ src/Explorer/Tabs/MongoDocumentsTab.ts # src/Explorer/Tabs/MongoShellTab.ts src/Explorer/Tabs/NotebookV2Tab.ts src/Explorer/Tabs/ScriptTabBase.ts -src/Explorer/Tabs/StoredProcedureTab.ts +# src/Explorer/Tabs/StoredProcedureTab.ts src/Explorer/Tabs/TabComponents.ts src/Explorer/Tabs/TabsBase.ts src/Explorer/Tabs/TriggerTab.ts diff --git a/less/documentDB.less b/less/documentDB.less index 1c8a7e8bb..271f53992 100644 --- a/less/documentDB.less +++ b/less/documentDB.less @@ -724,45 +724,24 @@ execute-sproc-params-pane { .results-container, .errors-container { - padding: @MediumSpace 0px 0px @MediumSpace; height: 100%; .flex-display(); .flex-direction(); overflow: hidden; - .toggles { - height: @ToggleHeight; - width: @ToggleWidth; - margin-left: @MediumSpace; - - &:focus { - .focus(); - } - - .tab { - margin-right: @MediumSpace; - } - - .toggleSwitch { - .toggleSwitch(); - } - - .selectedToggle { - .selectedToggle(); - } - - .unselectedToggle { - .unselectedToggle(); - } - } - .enterInputParameters { padding: @LargeSpace @MediumSpace; } + + div[role="tabpanel"] { + height: 100%; + padding-bottom: 50px; + } } .errors-container { padding-left: (2 * @MediumSpace); + padding: @MediumSpace 0px 0px @MediumSpace; .errors-header { font-weight: 700; font-size: @DefaultFontSize; diff --git a/src/Explorer/Tabs/StoredProcedureTab.html b/src/Explorer/Tabs/StoredProcedureTab.html deleted file mode 100644 index dc7db1bf7..000000000 --- a/src/Explorer/Tabs/StoredProcedureTab.html +++ /dev/null @@ -1,89 +0,0 @@ -
- -
-
Stored Procedure Id
- - - -
Stored Procedure Body
- - -
-
-
- - Result -
-
- - console.log -
-
- - - -
-
-
Errors:
-
- - - More details - -
-
- -
-
diff --git a/src/Explorer/Tabs/StoredProcedureTab.ts b/src/Explorer/Tabs/StoredProcedureTab.ts deleted file mode 100644 index 1b3de5502..000000000 --- a/src/Explorer/Tabs/StoredProcedureTab.ts +++ /dev/null @@ -1,287 +0,0 @@ -import { Resource, StoredProcedureDefinition } from "@azure/cosmos"; -import * as ko from "knockout"; -import Q from "q"; -import * as _ from "underscore"; -import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg"; -import * as Constants from "../../Common/Constants"; -import { createStoredProcedure } from "../../Common/dataAccess/createStoredProcedure"; -import { updateStoredProcedure } from "../../Common/dataAccess/updateStoredProcedure"; -import editable from "../../Common/EditableUtility"; -import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"; -import * as ViewModels from "../../Contracts/ViewModels"; -import { useNotificationConsole } from "../../hooks/useNotificationConsole"; -import { Action } from "../../Shared/Telemetry/TelemetryConstants"; -import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; -import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; -import StoredProcedure from "../Tree/StoredProcedure"; -import ScriptTabBase from "./ScriptTabBase"; -import template from "./StoredProcedureTab.html"; - -enum ToggleState { - Result = "result", - Logs = "logs", -} - -export default class StoredProcedureTab extends ScriptTabBase { - public readonly html = template; - public collection: ViewModels.Collection; - public node: StoredProcedure; - public executeResultsEditorId: string; - public executeLogsEditorId: string; - public toggleState: ko.Observable; - public originalSprocBody: ViewModels.Editable; - public resultsData: ko.Observable; - public logsData: ko.Observable; - public error: ko.Observable; - public hasResults: ko.Observable; - public hasErrors: ko.Observable; - - constructor(options: ViewModels.ScriptTabOption) { - super(options); - super.onActivate.bind(this); - - this.executeResultsEditorId = `executestoredprocedureresults${this.tabId}`; - this.executeLogsEditorId = `executestoredprocedurelogs${this.tabId}`; - this.toggleState = ko.observable(ToggleState.Result); - this.originalSprocBody = editable.observable(this.editorContent()); - this.resultsData = ko.observable(); - this.logsData = ko.observable(); - this.error = ko.observable(); - this.hasResults = ko.observable(false); - this.hasErrors = ko.observable(false); - this.error.subscribe((error: string) => { - this.hasErrors(error != null); - this.hasResults(error == null); - }); - - this.ariaLabel("Stored Procedure Body"); - this.buildCommandBarOptions(); - } - - public onSaveClick = (): Promise => { - return this._createStoredProcedure({ - id: this.id(), - body: this.editorContent(), - }); - }; - - public onDiscard = (): Q.Promise => { - this.setBaselines(); - const original = this.editorContent.getEditableOriginalValue(); - this.originalSprocBody(original); - this.originalSprocBody.valueHasMutated(); // trigger a re-render of the editor - - return Q(); - }; - - public onUpdateClick = (): Promise => { - const data = this._getResource(); - - this.isExecutionError(false); - this.isExecuting(true); - const startKey: number = TelemetryProcessor.traceStart(Action.UpdateStoredProcedure, { - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle(), - }); - return updateStoredProcedure(this.collection.databaseId, this.collection.id(), data) - .then( - (updatedResource) => { - this.resource(updatedResource); - this.tabTitle(updatedResource.id); - this.node.id(updatedResource.id); - this.node.body(updatedResource.body as string); - this.setBaselines(); - - const editorModel = this.editor() && this.editor().getModel(); - editorModel && editorModel.setValue(updatedResource.body as string); - this.editorContent.setBaseline(updatedResource.body as string); - TelemetryProcessor.traceSuccess( - Action.UpdateStoredProcedure, - { - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle(), - }, - startKey - ); - }, - (error: any) => { - this.isExecutionError(true); - TelemetryProcessor.traceFailure( - Action.UpdateStoredProcedure, - { - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle(), - error: getErrorMessage(error), - errorStack: getErrorStack(error), - }, - startKey - ); - } - ) - .finally(() => this.isExecuting(false)); - }; - - public onExecuteSprocsResult(result: any, logsData: any): void { - const resultData: string = this.renderObjectForEditor(_.omit(result, "scriptLogs").result, null, 4); - const scriptLogs: string = (result.scriptLogs && decodeURIComponent(result.scriptLogs)) || ""; - const logs: string = this.renderObjectForEditor(scriptLogs, null, 4); - this.error(null); - this.resultsData(resultData); - this.logsData(logs); - } - - public onExecuteSprocsError(error: string): void { - this.isExecutionError(true); - console.error(error); - this.error(error); - } - - public onErrorDetailsClick = (src: any, event: MouseEvent): boolean => { - useNotificationConsole.getState().expandConsole(); - - return false; - }; - - public onErrorDetailsKeyPress = (src: any, event: KeyboardEvent): boolean => { - if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) { - this.onErrorDetailsClick(src, null); - return false; - } - - return true; - }; - - public toggleResult(): void { - this.toggleState(ToggleState.Result); - this.resultsData.valueHasMutated(); // needed to refresh the json-editor component - } - - public toggleLogs(): void { - this.toggleState(ToggleState.Logs); - this.logsData.valueHasMutated(); // needed to refresh the json-editor component - } - - public onToggleKeyDown = (source: any, event: KeyboardEvent): boolean => { - if (event.keyCode === Constants.KeyCodes.LeftArrow) { - this.toggleResult(); - event.stopPropagation(); - return false; - } else if (event.keyCode === Constants.KeyCodes.RightArrow) { - this.toggleLogs(); - event.stopPropagation(); - return false; - } - - return true; - }; - - public isResultToggled(): boolean { - return this.toggleState() === ToggleState.Result; - } - - public isLogsToggled(): boolean { - return this.toggleState() === ToggleState.Logs; - } - - protected updateSelectedNode(): void { - if (this.collection == null) { - return; - } - - const database: ViewModels.Database = this.collection.getDatabase(); - if (!database.isDatabaseExpanded()) { - this.collection.container.selectedNode(database); - } else if (!this.collection.isCollectionExpanded() || !this.collection.isStoredProceduresExpanded()) { - this.collection.container.selectedNode(this.collection); - } else { - this.collection.container.selectedNode(this.node); - } - } - - protected buildCommandBarOptions(): void { - ko.computed(() => ko.toJSON([this.isNew, this.formIsDirty])).subscribe(() => this.updateNavbarWithTabsButtons()); - super.buildCommandBarOptions(); - } - - protected getTabsButtons(): CommandButtonComponentProps[] { - const label = "Execute"; - return super.getTabsButtons().concat({ - iconSrc: ExecuteQueryIcon, - iconAlt: label, - onCommandClick: () => { - this.collection && this.collection.container.openExecuteSprocParamsPanel(this.node); - }, - commandButtonLabel: label, - ariaLabel: label, - hasPopup: false, - disabled: this.isNew() || this.formIsDirty(), - }); - } - - private _getResource() { - return { - id: this.id(), - body: this.editorContent(), - }; - } - - private _createStoredProcedure(resource: StoredProcedureDefinition): Promise { - this.isExecutionError(false); - this.isExecuting(true); - const startKey: number = TelemetryProcessor.traceStart(Action.CreateStoredProcedure, { - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle(), - }); - - return createStoredProcedure(this.collection.databaseId, this.collection.id(), resource) - .then( - (createdResource) => { - this.tabTitle(createdResource.id); - this.isNew(false); - this.resource(createdResource); - this.hashLocation( - `${Constants.HashRoutePrefixes.collectionsWithIds( - this.collection.databaseId, - this.collection.id() - )}/sprocs/${createdResource.id}` - ); - this.setBaselines(); - - const editorModel = this.editor() && this.editor().getModel(); - editorModel && editorModel.setValue(createdResource.body as string); - this.editorContent.setBaseline(createdResource.body as string); - this.node = this.collection.createStoredProcedureNode(createdResource); - TelemetryProcessor.traceSuccess( - Action.CreateStoredProcedure, - { - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle(), - }, - startKey - ); - this.editorState(ViewModels.ScriptEditorState.exisitingNoEdits); - return createdResource; - }, - (createError) => { - this.isExecutionError(true); - TelemetryProcessor.traceFailure( - Action.CreateStoredProcedure, - { - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle(), - error: getErrorMessage(createError), - errorStack: getErrorStack(createError), - }, - startKey - ); - return Promise.reject(createError); - } - ) - .finally(() => this.isExecuting(false)); - } - - public onDelete(): Q.Promise { - // TODO - return Q(); - } -} diff --git a/src/Explorer/Tabs/StoredProcedureTab/StoredProcedureTab.tsx b/src/Explorer/Tabs/StoredProcedureTab/StoredProcedureTab.tsx new file mode 100644 index 000000000..3bf332956 --- /dev/null +++ b/src/Explorer/Tabs/StoredProcedureTab/StoredProcedureTab.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { ExecuteSprocResult } from "../../../Common/dataAccess/executeStoredProcedure"; +import * as DataModels from "../../../Contracts/DataModels"; +import * as ViewModels from "../../../Contracts/ViewModels"; +import Explorer from "../../Explorer"; +import StoredProcedure from "../../Tree/StoredProcedure"; +import ScriptTabBase from "../ScriptTabBase"; +import StoredProcedureTabComponent, { + IStoredProcTabComponentProps, + IStorProcTabComponentAccessor, +} from "./StoredProcedureTabComponent"; + +export interface IStoredProcTabProps { + container: Explorer; + collection: ViewModels.Collection; +} + +export class NewStoredProcedureTab extends ScriptTabBase { + public queryText: string; + public currentQuery: string; + public partitionKey: DataModels.PartitionKey; + public iStoredProcTabComponentProps: IStoredProcTabComponentProps; + public iStoreProcAccessor: IStorProcTabComponentAccessor; + public node: StoredProcedure; + public onSaveClick: () => void; + public onUpdateClick: () => Promise; + + constructor(options: ViewModels.ScriptTabOption, private props: IStoredProcTabProps) { + super(options); + this.partitionKey = options.partitionKey; + + this.iStoredProcTabComponentProps = { + resource: options.resource, + isNew: options.isNew, + tabKind: options.tabKind, + title: options.title, + tabPath: options.tabPath, + collectionBase: options.collection, + node: options.node, + hasLocation: options.hashLocation, + scriptTabBaseInstance: this, + collection: props.collection, + iStorProcTabComponentAccessor: (instance: IStorProcTabComponentAccessor) => { + this.iStoreProcAccessor = instance; + }, + container: props.container, + }; + } + + public render(): JSX.Element { + return ; + } + + public onTabClick(): void { + this.manager?.activateTab(this); + this.iStoreProcAccessor.onTabClickEvent(); + } + + public onCloseTabButtonClick(): void { + this.manager?.closeTab(this); + } + + public onExecuteSprocsResult(result: ExecuteSprocResult): void { + this.iStoreProcAccessor.onExecuteSprocsResultEvent(result); + } + + public onExecuteSprocsError(error: string): void { + this.iStoreProcAccessor.onExecuteSprocsErrorEvent(error); + } +} diff --git a/src/Explorer/Tabs/StoredProcedureTab/StoredProcedureTabComponent.tsx b/src/Explorer/Tabs/StoredProcedureTab/StoredProcedureTabComponent.tsx new file mode 100644 index 000000000..6d2b93be5 --- /dev/null +++ b/src/Explorer/Tabs/StoredProcedureTab/StoredProcedureTabComponent.tsx @@ -0,0 +1,604 @@ +import { Resource, StoredProcedureDefinition } from "@azure/cosmos"; +import { Pivot, PivotItem } from "@fluentui/react"; +import React from "react"; +import DiscardIcon from "../../../../images/discard.svg"; +import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg"; +import SaveIcon from "../../../../images/save-cosmos.svg"; +import * as Constants from "../../../Common/Constants"; +import { NormalizedEventKey } from "../../../Common/Constants"; +import { createStoredProcedure } from "../../../Common/dataAccess/createStoredProcedure"; +import { ExecuteSprocResult } from "../../../Common/dataAccess/executeStoredProcedure"; +import { updateStoredProcedure } from "../../../Common/dataAccess/updateStoredProcedure"; +import * as ViewModels from "../../../Contracts/ViewModels"; +import { useNotificationConsole } from "../../../hooks/useNotificationConsole"; +import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent"; +import { EditorReact } from "../../Controls/Editor/EditorReact"; +import Explorer from "../../Explorer"; +import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter"; +import StoredProcedure from "../../Tree/StoredProcedure"; +import ScriptTabBase from "../ScriptTabBase"; + +export interface IStorProcTabComponentAccessor { + onExecuteSprocsResultEvent: (result: ExecuteSprocResult) => void; + onExecuteSprocsErrorEvent: (error: string) => void; + onTabClickEvent: () => void; +} + +export interface Button { + visible: boolean; + enabled: boolean; + isSelected?: boolean; +} + +interface IStoredProcTabComponentStates { + hasResults: boolean; + hasErrors: boolean; + error: string; + resultData: string; + logsData: string; + originalSprocBody: string; + initialEditorContent: string; + sProcEditorContent: string; + id: string; + executeButton: Button; + saveButton: Button; + updateButton: Button; + discardButton: Button; +} + +export interface IStoredProcTabComponentProps { + resource: StoredProcedureDefinition; + isNew: boolean; + tabKind: ViewModels.CollectionTabKind; + title: string; + tabPath: string; + collectionBase: ViewModels.CollectionBase; + //eslint-disable-next-line + node?: any; + hasLocation: string; + scriptTabBaseInstance: ScriptTabBase; + collection: ViewModels.Collection; + iStorProcTabComponentAccessor: (instance: IStorProcTabComponentAccessor) => void; + container: Explorer; +} + +export default class StoredProcedureTabComponent extends React.Component< + IStoredProcTabComponentProps, + IStoredProcTabComponentStates +> { + public node: StoredProcedure; + public executeResultsEditorId: string; + public executeLogsEditorId: string; + public collection: ViewModels.Collection; + + constructor( + public storedProcTabCompProps: IStoredProcTabComponentProps, + private storedProcTabCompStates: IStoredProcTabComponentStates + ) { + super(storedProcTabCompProps); + this.state = { + error: "", + hasErrors: false, + hasResults: false, + resultData: "", + logsData: "", + originalSprocBody: this.props.resource.body.toString(), + initialEditorContent: this.props.resource.body.toString(), + sProcEditorContent: this.props.resource.body.toString(), + id: this.props.resource.id, + executeButton: { + enabled: !this.props.scriptTabBaseInstance.isNew(), + visible: true, + }, + saveButton: { + enabled: (() => { + if (!this.props.scriptTabBaseInstance.formIsValid()) { + return false; + } + if (!this.props.scriptTabBaseInstance.formIsDirty()) { + return false; + } + return true; + })(), + visible: this.props.scriptTabBaseInstance.isNew(), + }, + updateButton: { + enabled: (() => { + if (!this.props.scriptTabBaseInstance.formIsValid()) { + return false; + } + if (!this.props.scriptTabBaseInstance.formIsDirty()) { + return false; + } + return true; + })(), + visible: !this.props.scriptTabBaseInstance.isNew(), + }, + discardButton: { + enabled: (() => { + if (!this.props.scriptTabBaseInstance.formIsValid()) { + return false; + } + if (!this.props.scriptTabBaseInstance.formIsDirty()) { + return false; + } + return true; + })(), + visible: true, + }, + }; + + this.collection = this.props.collection; + this.executeResultsEditorId = `executestoredprocedureresults${this.props.scriptTabBaseInstance.tabId}`; + this.executeLogsEditorId = `executestoredprocedurelogs${this.props.scriptTabBaseInstance.tabId}`; + this.props.scriptTabBaseInstance.ariaLabel("Stored Procedure Body"); + + this.props.iStorProcTabComponentAccessor({ + onExecuteSprocsResultEvent: this.onExecuteSprocsResult.bind(this), + onExecuteSprocsErrorEvent: this.onExecuteSprocsError.bind(this), + onTabClickEvent: this.onTabClick.bind(this), + }); + + this.node = this.props.node; + + this.buildCommandBarOptions(); + } + + public onTabClick(): void { + if (this.props.container.tabsManager.openedTabs().length > 0) { + useCommandBar.getState().setContextButtons(this.getTabsButtons()); + } + } + + public onSaveClick = (): Promise => { + return this._createStoredProcedure({ + id: this.state.id, + body: this.state.sProcEditorContent, + }); + }; + + public onDiscard = (): Promise => { + const onDiscardPromise = new Promise(() => { + this.props.scriptTabBaseInstance.setBaselines(); + const original = this.props.scriptTabBaseInstance.editorContent.getEditableOriginalValue(); + if (this.state.updateButton.visible) { + this.setState({ + updateButton: { + enabled: false, + visible: true, + }, + sProcEditorContent: original, + discardButton: { + enabled: false, + visible: true, + }, + executeButton: { + enabled: true, + visible: true, + }, + }); + } else { + this.setState({ + saveButton: { + enabled: false, + visible: true, + }, + sProcEditorContent: original, + discardButton: { + enabled: false, + visible: true, + }, + executeButton: { + enabled: false, + visible: true, + }, + id: "", + }); + } + }); + + setTimeout(() => { + useCommandBar.getState().setContextButtons(this.getTabsButtons()); + }, 100); + + return onDiscardPromise; + }; + + public onUpdateClick = (): Promise => { + const data = this._getResource(); + + this.props.scriptTabBaseInstance.isExecutionError(false); + this.props.scriptTabBaseInstance.isExecuting(true); + + return updateStoredProcedure( + this.props.scriptTabBaseInstance.collection.databaseId, + this.props.scriptTabBaseInstance.collection.id(), + data + ) + .then( + (updatedResource) => { + this.props.scriptTabBaseInstance.resource(updatedResource); + this.props.scriptTabBaseInstance.tabTitle(updatedResource.id); + this.node.id(updatedResource.id); + this.node.body(updatedResource.body as string); + this.props.scriptTabBaseInstance.setBaselines(); + + const editorModel = + this.props.scriptTabBaseInstance.editor() && this.props.scriptTabBaseInstance.editor().getModel(); + editorModel && editorModel.setValue(updatedResource.body as string); + this.props.scriptTabBaseInstance.editorContent.setBaseline(updatedResource.body as string); + this.setState({ + discardButton: { + enabled: false, + visible: true, + }, + updateButton: { + enabled: false, + visible: true, + }, + executeButton: { + enabled: true, + visible: true, + }, + }); + useCommandBar.getState().setContextButtons(this.getTabsButtons()); + }, + () => { + this.props.scriptTabBaseInstance.isExecutionError(true); + } + ) + .finally(() => this.props.scriptTabBaseInstance.isExecuting(false)); + }; + + public onExecuteSprocsResult(result: ExecuteSprocResult): void { + const resultData: string = this.props.scriptTabBaseInstance.renderObjectForEditor(result.result, undefined, 4); + const scriptLogs: string = (result.scriptLogs && decodeURIComponent(result.scriptLogs)) || ""; + const logs: string = this.props.scriptTabBaseInstance.renderObjectForEditor(scriptLogs, undefined, 4); + + this.setState({ + hasResults: false, + }); + setTimeout(() => { + this.setState({ + error: undefined, + resultData: resultData, + logsData: logs, + hasResults: resultData ? true : false, + hasErrors: false, + }); + }, 100); + } + + public onExecuteSprocsError(error: string): void { + this.props.scriptTabBaseInstance.isExecutionError(true); + console.error(error); + this.setState({ + error: error, + hasErrors: true, + hasResults: false, + }); + } + + public onErrorDetailsClick = (): boolean => { + useNotificationConsole.getState().expandConsole(); + + return false; + }; + + public onErrorDetailsKeyPress = (event: React.KeyboardEvent): boolean => { + if (event.key === NormalizedEventKey.Space || event.key === NormalizedEventKey.Enter) { + this.onErrorDetailsClick(); + return false; + } + + return true; + }; + + protected updateSelectedNode(): void { + if (this.props.collectionBase === undefined) { + return; + } + + const database: ViewModels.Database = this.props.collectionBase.getDatabase(); + if (!database.isDatabaseExpanded()) { + this.props.collectionBase.container.selectedNode(database); + } else if (!this.props.collectionBase.isCollectionExpanded() || !this.collection.isStoredProceduresExpanded()) { + this.props.collectionBase.container.selectedNode(this.props.collectionBase); + } else { + this.props.collectionBase.container.selectedNode(this.node); + } + } + + protected buildCommandBarOptions(): void { + useCommandBar.getState().setContextButtons(this.getTabsButtons()); + } + + protected getTabsButtons(): CommandButtonComponentProps[] { + const buttons: CommandButtonComponentProps[] = []; + const label = "Save"; + if (this.state.saveButton.visible) { + buttons.push({ + iconSrc: SaveIcon, + iconAlt: label, + onCommandClick: this.onSaveClick, + commandButtonLabel: label, + ariaLabel: label, + hasPopup: false, + disabled: !this.state.saveButton.enabled, + }); + } + + if (this.state.updateButton.visible) { + const label = "Update"; + buttons.push({ + iconSrc: SaveIcon, + iconAlt: label, + onCommandClick: this.onUpdateClick, + commandButtonLabel: label, + ariaLabel: label, + hasPopup: false, + disabled: !this.state.updateButton.enabled, + }); + } + + if (this.state.discardButton.visible) { + const label = "Discard"; + buttons.push({ + iconSrc: DiscardIcon, + iconAlt: label, + onCommandClick: this.onDiscard, + commandButtonLabel: label, + ariaLabel: label, + hasPopup: false, + disabled: !this.state.discardButton.enabled, + }); + } + + if (this.state.executeButton.visible) { + const label = "Execute"; + buttons.push({ + iconSrc: ExecuteQueryIcon, + iconAlt: label, + onCommandClick: () => { + this.collection.container.openExecuteSprocParamsPanel(this.node); + }, + commandButtonLabel: label, + ariaLabel: label, + hasPopup: false, + disabled: !this.state.executeButton.enabled, + }); + } + + return buttons; + } + + private _getResource() { + return { + id: this.state.id, + body: this.state.sProcEditorContent, + }; + } + + private _createStoredProcedure(resource: StoredProcedureDefinition): Promise { + this.props.scriptTabBaseInstance.isExecutionError(false); + this.props.scriptTabBaseInstance.isExecuting(true); + + return createStoredProcedure(this.props.collectionBase.databaseId, this.props.collectionBase.id(), resource) + .then( + (createdResource) => { + this.props.scriptTabBaseInstance.tabTitle(createdResource.id); + this.props.scriptTabBaseInstance.isNew(false); + this.props.scriptTabBaseInstance.resource(createdResource); + this.props.scriptTabBaseInstance.hashLocation( + `${Constants.HashRoutePrefixes.collectionsWithIds( + this.props.collectionBase.databaseId, + this.props.collectionBase.id() + )}/sprocs/${createdResource.id}` + ); + this.props.scriptTabBaseInstance.setBaselines(); + + const editorModel = + this.props.scriptTabBaseInstance.editor() && this.props.scriptTabBaseInstance.editor().getModel(); + editorModel && editorModel.setValue(createdResource.body as string); + this.props.scriptTabBaseInstance.editorContent.setBaseline(createdResource.body as string); + this.node = this.collection.createStoredProcedureNode(createdResource); + this.props.container.tabsManager.openedTabs()[ + this.props.container.tabsManager.openedTabs().length - 1 + ].node = this.node; + + this.props.scriptTabBaseInstance.editorState(ViewModels.ScriptEditorState.exisitingNoEdits); + + this.setState({ + executeButton: { + enabled: false, + visible: true, + }, + }); + setTimeout(() => { + this.setState({ + executeButton: { + enabled: true, + visible: true, + }, + updateButton: { + enabled: false, + visible: true, + }, + saveButton: { + enabled: false, + visible: false, + }, + discardButton: { + enabled: false, + visible: true, + }, + sProcEditorContent: this.state.sProcEditorContent, + }); + useCommandBar.getState().setContextButtons(this.getTabsButtons()); + }, 100); + + return createdResource; + }, + (createError) => { + this.props.scriptTabBaseInstance.isExecutionError(true); + + return Promise.reject(createError); + } + ) + .finally(() => this.props.scriptTabBaseInstance.isExecuting(false)); + } + + public onDelete(): Promise { + const isDeleted = false; + const onDeletePromise = new Promise((resolve) => { + resolve(isDeleted); + }); + return onDeletePromise; + } + + public handleIdOnChange(event: React.ChangeEvent): void { + if (this.state.saveButton.visible) { + this.setState({ + id: event.target.value, + saveButton: { + enabled: true, + visible: this.props.scriptTabBaseInstance.isNew(), + }, + discardButton: { + enabled: true, + visible: true, + }, + }); + } + setTimeout(() => { + useCommandBar.getState().setContextButtons(this.getTabsButtons()); + }, 1000); + } + + public onChangeContent(newConent: string): void { + if (this.state.updateButton.visible) { + this.setState({ + updateButton: { + enabled: true, + visible: true, + }, + discardButton: { + enabled: true, + visible: true, + }, + executeButton: { + enabled: false, + visible: true, + }, + sProcEditorContent: newConent, + }); + } else { + this.setState({ + saveButton: { + enabled: false, + visible: this.props.scriptTabBaseInstance.isNew(), + }, + executeButton: { + enabled: false, + visible: true, + }, + discardButton: { + enabled: true, + visible: true, + }, + sProcEditorContent: newConent, + }); + } + + setTimeout(() => { + useCommandBar.getState().setContextButtons(this.getTabsButtons()); + }, 100); + } + + render(): JSX.Element { + return ( +
+
+
Stored Procedure Id
+ + ) => this.handleIdOnChange(event)} + /> + +
Stored Procedure Body
+ this.onChangeContent(newContent)} + /> + {this.state.hasResults && ( +
+ + + + + + + + +
+ )} + {this.state.hasErrors && ( + + )} +
+
+ ); + } +} diff --git a/src/Explorer/Tree/StoredProcedure.ts b/src/Explorer/Tree/StoredProcedure.ts index 9f491287c..2fa9d6eb7 100644 --- a/src/Explorer/Tree/StoredProcedure.ts +++ b/src/Explorer/Tree/StoredProcedure.ts @@ -3,13 +3,13 @@ import * as ko from "knockout"; import * as Constants from "../../Common/Constants"; import { deleteStoredProcedure } from "../../Common/dataAccess/deleteStoredProcedure"; import { executeStoredProcedure } from "../../Common/dataAccess/executeStoredProcedure"; -import { getErrorMessage } from "../../Common/ErrorHandlingUtils"; 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 Explorer from "../Explorer"; -import StoredProcedureTab from "../Tabs/StoredProcedureTab"; +import { getErrorMessage } from "../Tables/Utilities"; +import { NewStoredProcedureTab } from "../Tabs/StoredProcedureTab/StoredProcedureTab"; import TabsBase from "../Tabs/TabsBase"; const sampleStoredProcedureBody: string = `// SAMPLE STORED PROCEDURE @@ -67,16 +67,22 @@ export default class StoredProcedure { body: sampleStoredProcedureBody, }; - const storedProcedureTab: StoredProcedureTab = new StoredProcedureTab({ - resource: storedProcedure, - isNew: true, - tabKind: ViewModels.CollectionTabKind.StoredProcedures, - title: `New Stored Procedure ${id}`, - tabPath: `${source.databaseId}>${source.id()}>New Stored Procedure ${id}`, - collection: source, - node: source, - hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/sproc`, - }); + const storedProcedureTab: NewStoredProcedureTab = new NewStoredProcedureTab( + { + resource: storedProcedure, + isNew: true, + tabKind: ViewModels.CollectionTabKind.StoredProcedures, + title: `New Stored Procedure ${id}`, + tabPath: `${source.databaseId}>${source.id()}>New Stored Procedure ${id}`, + collection: source, + node: source, + hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/sproc`, + }, + { + collection: source, + container: source.container, + } + ); source.container.tabsManager.activateNewTab(storedProcedureTab); } @@ -93,11 +99,11 @@ export default class StoredProcedure { public open = () => { this.select(); - const storedProcedureTabs: StoredProcedureTab[] = this.container.tabsManager.getTabs( + const storedProcedureTabs: NewStoredProcedureTab[] = this.container.tabsManager.getTabs( ViewModels.CollectionTabKind.StoredProcedures, (tab: TabsBase) => tab.node && tab.node.rid === this.rid - ) as StoredProcedureTab[]; - let storedProcedureTab: StoredProcedureTab = storedProcedureTabs && storedProcedureTabs[0]; + ) as NewStoredProcedureTab[]; + let storedProcedureTab: NewStoredProcedureTab = storedProcedureTabs && storedProcedureTabs[0]; if (storedProcedureTab) { this.container.tabsManager.activateTab(storedProcedureTab); @@ -109,24 +115,29 @@ export default class StoredProcedure { body: this.body(), }; - storedProcedureTab = new StoredProcedureTab({ - resource: storedProcedureData, - isNew: false, - tabKind: ViewModels.CollectionTabKind.StoredProcedures, - title: storedProcedureData.id, - tabPath: `${this.collection.databaseId}>${this.collection.id()}>${storedProcedureData.id}`, - collection: this.collection, - node: this, - hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds( - this.collection.databaseId, - this.collection.id() - )}/sprocs/${this.id()}`, - }); + storedProcedureTab = new NewStoredProcedureTab( + { + resource: storedProcedureData, + isNew: false, + tabKind: ViewModels.CollectionTabKind.StoredProcedures, + title: storedProcedureData.id, + tabPath: `${this.collection.databaseId}>${this.collection.id()}>${storedProcedureData.id}`, + collection: this.collection, + node: this, + hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds( + this.collection.databaseId, + this.collection.id() + )}/sprocs/${this.id()}`, + }, + { + collection: this.collection, + container: this.container, + } + ); this.container.tabsManager.activateNewTab(storedProcedureTab); } }; - public delete() { if (!window.confirm("Are you sure you want to delete the stored procedure?")) { return; @@ -142,19 +153,19 @@ export default class StoredProcedure { } public execute(params: string[], partitionKeyValue?: string): void { - const sprocTabs = this.container.tabsManager.getTabs( + const sprocTabs: NewStoredProcedureTab[] = this.container.tabsManager.getTabs( ViewModels.CollectionTabKind.StoredProcedures, (tab: TabsBase) => tab.node && tab.node.rid === this.rid - ) as StoredProcedureTab[]; - const sprocTab = sprocTabs && sprocTabs.length > 0 && sprocTabs[0]; + ) as NewStoredProcedureTab[]; + const sprocTab: NewStoredProcedureTab = sprocTabs && sprocTabs.length > 0 && sprocTabs[0]; sprocTab.isExecuting(true); this.container && executeStoredProcedure(this.collection, this, partitionKeyValue, params) .then( - (result: any) => { - sprocTab.onExecuteSprocsResult(result, result.scriptLogs); + (result) => { + sprocTab.onExecuteSprocsResult(result); }, - (error: any) => { + (error) => { sprocTab.onExecuteSprocsError(getErrorMessage(error)); } )