diff --git a/.eslintignore b/.eslintignore index 3346de4bf..4e91b9912 100644 --- a/.eslintignore +++ b/.eslintignore @@ -130,7 +130,6 @@ src/Explorer/Panes/LoadQueryPane.ts src/Explorer/Panes/NewVertexPane.ts src/Explorer/Panes/PaneComponents.ts src/Explorer/Panes/RenewAdHocAccessPane.ts -src/Explorer/Panes/SaveQueryPane.ts src/Explorer/Panes/SetupNotebooksPane.ts src/Explorer/Panes/StringInputPane.ts src/Explorer/Panes/SwitchDirectoryPane.ts diff --git a/src/Explorer/ComponentRegisterer.test.ts b/src/Explorer/ComponentRegisterer.test.ts index f2fa5ada3..361909eee 100644 --- a/src/Explorer/ComponentRegisterer.test.ts +++ b/src/Explorer/ComponentRegisterer.test.ts @@ -77,10 +77,6 @@ describe("Component Registerer", () => { expect(ko.components.isRegistered("delete-collection-confirmation-pane")).toBe(true); }); - it("should register save-query-pane component", () => { - expect(ko.components.isRegistered("save-query-pane")).toBe(true); - }); - it("should register browse-queries-pane component", () => { expect(ko.components.isRegistered("browse-queries-pane")).toBe(true); }); diff --git a/src/Explorer/ComponentRegisterer.ts b/src/Explorer/ComponentRegisterer.ts index f7b41607c..a391aba75 100644 --- a/src/Explorer/ComponentRegisterer.ts +++ b/src/Explorer/ComponentRegisterer.ts @@ -14,6 +14,7 @@ import DatabaseSettingsTab from "./Tabs/DatabaseSettingsTab"; import DocumentsTab from "./Tabs/DocumentsTab"; import GalleryTab from "./Tabs/GalleryTab"; import GraphTab from "./Tabs/GraphTab"; +import MongoDocumentsTab from "./Tabs/MongoDocumentsTab"; import MongoShellTab from "./Tabs/MongoShellTab"; import NotebookTabV2 from "./Tabs/NotebookV2Tab"; import NotebookViewerTab from "./Tabs/NotebookViewerTab"; @@ -73,7 +74,6 @@ ko.components.register("table-column-options-pane", new PaneComponents.TableColu ko.components.register("table-query-select-pane", new PaneComponents.TableQuerySelectPaneComponent()); ko.components.register("cassandra-add-collection-pane", new PaneComponents.CassandraAddCollectionPaneComponent()); ko.components.register("load-query-pane", new PaneComponents.LoadQueryPaneComponent()); -ko.components.register("save-query-pane", new PaneComponents.SaveQueryPaneComponent()); ko.components.register("browse-queries-pane", new PaneComponents.BrowseQueriesPaneComponent()); ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent()); ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent()); diff --git a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap index 266160cbc..2606dabdc 100644 --- a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap +++ b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap @@ -385,22 +385,6 @@ exports[`SettingsComponent renders 1`] = ` "title": [Function], "visible": [Function], }, - SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -899,22 +883,6 @@ exports[`SettingsComponent renders 1`] = ` "container": [Circular], "parameters": [Function], }, - "saveQueryPane": SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, @@ -1371,22 +1339,6 @@ exports[`SettingsComponent renders 1`] = ` "title": [Function], "visible": [Function], }, - SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -1885,22 +1837,6 @@ exports[`SettingsComponent renders 1`] = ` "container": [Circular], "parameters": [Function], }, - "saveQueryPane": SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, @@ -2370,22 +2306,6 @@ exports[`SettingsComponent renders 1`] = ` "title": [Function], "visible": [Function], }, - SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -2884,22 +2804,6 @@ exports[`SettingsComponent renders 1`] = ` "container": [Circular], "parameters": [Function], }, - "saveQueryPane": SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, @@ -3356,22 +3260,6 @@ exports[`SettingsComponent renders 1`] = ` "title": [Function], "visible": [Function], }, - SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -3870,22 +3758,6 @@ exports[`SettingsComponent renders 1`] = ` "container": [Circular], "parameters": [Function], }, - "saveQueryPane": SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx index 758c3f604..a5f2880ee 100644 --- a/src/Explorer/Explorer.tsx +++ b/src/Explorer/Explorer.tsx @@ -60,7 +60,7 @@ import { ExecuteSprocParamsPanel } from "./Panes/ExecuteSprocParamsPanel"; import GraphStylingPane from "./Panes/GraphStylingPane"; import { LoadQueryPane } from "./Panes/LoadQueryPane"; import NewVertexPane from "./Panes/NewVertexPane"; -import { SaveQueryPane } from "./Panes/SaveQueryPane"; +import { SaveQueryPanel } from "./Panes/SaveQueryPanel"; import { SettingsPane } from "./Panes/SettingsPane"; import { SetupNotebooksPane } from "./Panes/SetupNotebooksPane"; import { StringInputPane } from "./Panes/StringInputPane"; @@ -211,7 +211,6 @@ export default class Explorer { public newVertexPane: NewVertexPane; public cassandraAddCollectionPane: CassandraAddCollectionPane; public loadQueryPane: LoadQueryPane; - public saveQueryPane: ContextualPaneBase; public browseQueriesPane: BrowseQueriesPane; public stringInputPane: StringInputPane; public setupNotebooksPane: SetupNotebooksPane; @@ -618,13 +617,6 @@ export default class Explorer { container: this, }); - this.saveQueryPane = new SaveQueryPane({ - id: "savequerypane", - visible: ko.observable(false), - - container: this, - }); - this.browseQueriesPane = new BrowseQueriesPane({ id: "browsequeriespane", visible: ko.observable(false), @@ -660,7 +652,6 @@ export default class Explorer { this.newVertexPane, this.cassandraAddCollectionPane, this.loadQueryPane, - this.saveQueryPane, this.browseQueriesPane, this.stringInputPane, this.setupNotebooksPane, @@ -2423,6 +2414,11 @@ export default class Explorer { /> ); } + + public openSaveQueryPanel(): void { + this.openSidePanel("Save Query", this.closeSidePanel()} />); + } + public openUploadFilePanel(parent?: NotebookContentItem): void { parent = parent || this.resourceTree.myNotebooksContentRoot; this.openSidePanel( diff --git a/src/Explorer/Panes/PaneComponents.ts b/src/Explorer/Panes/PaneComponents.ts index cc4729447..64e862600 100644 --- a/src/Explorer/Panes/PaneComponents.ts +++ b/src/Explorer/Panes/PaneComponents.ts @@ -7,7 +7,6 @@ import GitHubReposPaneTemplate from "./GitHubReposPane.html"; import GraphNewVertexPaneTemplate from "./GraphNewVertexPane.html"; import GraphStylingPaneTemplate from "./GraphStylingPane.html"; import LoadQueryPaneTemplate from "./LoadQueryPane.html"; -import SaveQueryPaneTemplate from "./SaveQueryPane.html"; import SetupNotebooksPaneTemplate from "./SetupNotebooksPane.html"; import StringInputPaneTemplate from "./StringInputPane.html"; import TableAddEntityPaneTemplate from "./Tables/TableAddEntityPane.html"; @@ -120,15 +119,6 @@ export class LoadQueryPaneComponent { } } -export class SaveQueryPaneComponent { - constructor() { - return { - viewModel: PaneComponent, - template: SaveQueryPaneTemplate, - }; - } -} - export class BrowseQueriesPaneComponent { constructor() { return { diff --git a/src/Explorer/Panes/SaveQueryPane.html b/src/Explorer/Panes/SaveQueryPane.html deleted file mode 100644 index 6d8a8a587..000000000 --- a/src/Explorer/Panes/SaveQueryPane.html +++ /dev/null @@ -1,63 +0,0 @@ -
-
-
- -
-
- -
- -
- Close -
-
- - - -
-
- Error - - - More details - -
-
- - - -
-
-
- -
-
-

* Name

- -
-
-
-
-
- -
-
- - -
- -
- -
-
diff --git a/src/Explorer/Panes/SaveQueryPane.ts b/src/Explorer/Panes/SaveQueryPane.ts deleted file mode 100644 index 8bebbdfa7..000000000 --- a/src/Explorer/Panes/SaveQueryPane.ts +++ /dev/null @@ -1,153 +0,0 @@ -import * as ko from "knockout"; -import * as Constants from "../../Common/Constants"; -import * as DataModels from "../../Contracts/DataModels"; -import * as ViewModels from "../../Contracts/ViewModels"; -import { Action } from "../../Shared/Telemetry/TelemetryConstants"; -import { ContextualPaneBase } from "./ContextualPaneBase"; -import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent"; -import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"; -import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; -import QueryTab from "../Tabs/QueryTab"; -import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"; - -export class SaveQueryPane extends ContextualPaneBase { - public queryName: ko.Observable; - public canSaveQueries: ko.Computed; - public setupSaveQueriesText: string = `For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “${Constants.SavedQueries.DatabaseName}”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.`; - - constructor(options: ViewModels.PaneOptions) { - super(options); - this.title("Save Query"); - this.queryName = ko.observable(); - this.canSaveQueries = this.container && this.container.canSaveQueries; - this.resetData(); - } - - public submit = (): void => { - this.formErrors(""); - this.formErrorsDetails(""); - if (!this.canSaveQueries()) { - this.formErrors("Cannot save query"); - this.formErrorsDetails("Failed to save query: account not set up to save queries"); - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Error, - "Failed to save query: account not setup to save queries" - ); - } - - const queryName: string = this.queryName(); - const queryTab = this.container && (this.container.tabsManager.activeTab() as QueryTab); - const query: string = queryTab && queryTab.sqlQueryEditorContent(); - if (!queryName || queryName.length === 0) { - this.formErrors("No query name specified"); - this.formErrorsDetails("No query name specified. Please specify a query name."); - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Error, - "Could not save query -- No query name specified. Please specify a query name." - ); - return; - } else if (!query || query.length === 0) { - this.formErrors("Invalid query content specified"); - this.formErrorsDetails("Invalid query content specified. Please enter query content."); - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Error, - "Could not save query -- Invalid query content specified. Please enter query content." - ); - return; - } - - const queryParam: DataModels.Query = { - id: queryName, - resourceId: this.container.queriesClient.getResourceId(), - queryName: queryName, - query: query, - }; - const startKey: number = TelemetryProcessor.traceStart(Action.SaveQuery, { - dataExplorerArea: Constants.Areas.ContextualPane, - paneTitle: this.title(), - }); - this.isExecuting(true); - this.container.queriesClient.saveQuery(queryParam).then( - () => { - this.isExecuting(false); - queryTab.tabTitle(queryParam.queryName); - queryTab.tabPath(`${queryTab.collection.databaseId}>${queryTab.collection.id()}>${queryParam.queryName}`); - TelemetryProcessor.traceSuccess( - Action.SaveQuery, - { - dataExplorerArea: Constants.Areas.ContextualPane, - paneTitle: this.title(), - }, - startKey - ); - this.close(); - }, - (error: any) => { - this.isExecuting(false); - const errorMessage = getErrorMessage(error); - this.formErrors("Failed to save query"); - this.formErrorsDetails(`Failed to save query: ${errorMessage}`); - TelemetryProcessor.traceFailure( - Action.SaveQuery, - { - dataExplorerArea: Constants.Areas.ContextualPane, - paneTitle: this.title(), - error: errorMessage, - errorStack: getErrorStack(error), - }, - startKey - ); - } - ); - }; - - public setupQueries = async (src: any, event: MouseEvent): Promise => { - if (!this.container) { - return; - } - - const startKey: number = TelemetryProcessor.traceStart(Action.SetupSavedQueries, { - dataExplorerArea: Constants.Areas.ContextualPane, - paneTitle: this.title(), - }); - try { - this.isExecuting(true); - await this.container.queriesClient.setupQueriesCollection(); - this.container.refreshAllDatabases(); - TelemetryProcessor.traceSuccess( - Action.SetupSavedQueries, - { - dataExplorerArea: Constants.Areas.ContextualPane, - paneTitle: this.title(), - }, - startKey - ); - } catch (error) { - const errorMessage = getErrorMessage(error); - TelemetryProcessor.traceFailure( - Action.SetupSavedQueries, - { - dataExplorerArea: Constants.Areas.ContextualPane, - paneTitle: this.title(), - error: errorMessage, - errorStack: getErrorStack(error), - }, - startKey - ); - this.formErrors("Failed to setup a container for saved queries"); - this.formErrorsDetails(`Failed to setup a container for saved queries: ${errorMessage}`); - } finally { - this.isExecuting(false); - } - }; - - public close() { - super.close(); - this.resetData(); - } - - public resetData() { - super.resetData(); - this.queryName(""); - } -} diff --git a/src/Explorer/Panes/SaveQueryPanel/__snapshots__/index.test.tsx.snap b/src/Explorer/Panes/SaveQueryPanel/__snapshots__/index.test.tsx.snap new file mode 100644 index 000000000..b5ed7ed77 --- /dev/null +++ b/src/Explorer/Panes/SaveQueryPanel/__snapshots__/index.test.tsx.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Save Query Pane should render Default properly 1`] = ` + +
+
+ + For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily. + +
+
+
+`; diff --git a/src/Explorer/Panes/SaveQueryPanel/index.test.tsx b/src/Explorer/Panes/SaveQueryPanel/index.test.tsx new file mode 100644 index 000000000..85e928b54 --- /dev/null +++ b/src/Explorer/Panes/SaveQueryPanel/index.test.tsx @@ -0,0 +1,32 @@ +import { shallow } from "enzyme"; +import * as ko from "knockout"; +import React from "react"; +import Explorer from "../../Explorer"; +import { SaveQueryPanel } from "./index"; + +describe("Save Query Pane", () => { + const fakeExplorer = {} as Explorer; + fakeExplorer.canSaveQueries = ko.computed(() => true); + + const props = { + explorer: fakeExplorer, + closePanel: (): void => undefined, + }; + + const wrapper = shallow(); + + it("should return true if can save Queries else false", () => { + fakeExplorer.canSaveQueries = ko.computed(() => true); + wrapper.setProps(props); + expect(wrapper.exists("#saveQueryInput")).toBe(true); + + fakeExplorer.canSaveQueries = ko.computed(() => false); + wrapper.setProps(props); + expect(wrapper.exists("#saveQueryInput")).toBe(false); + }); + + it("should render Default properly", () => { + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/src/Explorer/Panes/SaveQueryPanel/index.tsx b/src/Explorer/Panes/SaveQueryPanel/index.tsx new file mode 100644 index 000000000..a4e5bc559 --- /dev/null +++ b/src/Explorer/Panes/SaveQueryPanel/index.tsx @@ -0,0 +1,168 @@ +import { useBoolean } from "@uifabric/react-hooks"; +import { Text, TextField } from "office-ui-fabric-react"; +import React, { FunctionComponent, useState } from "react"; +import { Areas, SavedQueries } from "../../../Common/Constants"; +import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils"; +import { Query } from "../../../Contracts/DataModels"; +import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; +import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor"; +import { logConsoleError } from "../../../Utils/NotificationConsoleUtils"; +import Explorer from "../../Explorer"; +import QueryTab from "../../Tabs/QueryTab"; +import { GenericRightPaneComponent, GenericRightPaneProps } from "../GenericRightPaneComponent"; + +interface SaveQueryPanelProps { + explorer: Explorer; + closePanel: () => void; +} + +export const SaveQueryPanel: FunctionComponent = ({ + explorer, + closePanel, +}: SaveQueryPanelProps): JSX.Element => { + const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false); + const [formError, setFormError] = useState(""); + const [formErrorsDetails, setFormErrorsDetails] = useState(""); + const [queryName, setQueryName] = useState(""); + + const setupSaveQueriesText = `For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “${SavedQueries.DatabaseName}”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.`; + const title = "Save Query"; + const { canSaveQueries } = explorer; + const genericPaneProps: GenericRightPaneProps = { + container: explorer, + formError: formError, + formErrorDetail: formErrorsDetails, + id: "saveQueryPane", + isExecuting: isLoading, + title, + submitButtonText: canSaveQueries() ? "Save" : "Complete setup", + onClose: () => closePanel(), + onSubmit: () => { + canSaveQueries() ? submit() : setupQueries(); + }, + }; + + const submit = async (): Promise => { + setFormError(""); + setFormErrorsDetails(""); + if (!canSaveQueries()) { + setFormError("Cannot save query"); + setFormErrorsDetails("Failed to save query: account not set up to save queries"); + logConsoleError("Failed to save query: account not setup to save queries"); + } + + const queryTab = explorer && (explorer.tabsManager.activeTab() as QueryTab); + const query: string = queryTab && queryTab.sqlQueryEditorContent(); + if (!queryName || queryName.length === 0) { + setFormError("No query name specified"); + setFormErrorsDetails("No query name specified. Please specify a query name."); + logConsoleError("Could not save query -- No query name specified. Please specify a query name."); + return; + } else if (!query || query.length === 0) { + setFormError("Invalid query content specified"); + setFormErrorsDetails("Invalid query content specified. Please enter query content."); + logConsoleError("Could not save query -- Invalid query content specified. Please enter query content."); + return; + } + + const queryParam: Query = { + id: queryName, + resourceId: explorer.queriesClient.getResourceId(), + queryName: queryName, + query: query, + }; + const startKey: number = traceStart(Action.SaveQuery, { + dataExplorerArea: Areas.ContextualPane, + paneTitle: title, + }); + setLoadingTrue(); + try { + await explorer.queriesClient.saveQuery(queryParam); + setLoadingFalse(); + queryTab.tabTitle(queryParam.queryName); + queryTab.tabPath(`${queryTab.collection.databaseId}>${queryTab.collection.id()}>${queryParam.queryName}`); + traceSuccess( + Action.SaveQuery, + { + dataExplorerArea: Areas.ContextualPane, + paneTitle: title, + }, + startKey + ); + closePanel(); + } catch (error) { + setLoadingFalse(); + const errorMessage = getErrorMessage(error); + setFormError("Failed to save query"); + setFormErrorsDetails(`Failed to save query: ${errorMessage}`); + traceFailure( + Action.SaveQuery, + { + dataExplorerArea: Areas.ContextualPane, + paneTitle: title, + error: errorMessage, + errorStack: getErrorStack(error), + }, + startKey + ); + } + }; + + const setupQueries = async (): Promise => { + const startKey: number = traceStart(Action.SetupSavedQueries, { + dataExplorerArea: Areas.ContextualPane, + paneTitle: title, + }); + + try { + setLoadingTrue(); + await explorer.queriesClient.setupQueriesCollection(); + explorer.refreshAllDatabases(); + traceSuccess( + Action.SetupSavedQueries, + { + dataExplorerArea: Areas.ContextualPane, + paneTitle: title, + }, + startKey + ); + } catch (error) { + const errorMessage = getErrorMessage(error); + traceFailure( + Action.SetupSavedQueries, + { + dataExplorerArea: Areas.ContextualPane, + paneTitle: title, + error: errorMessage, + errorStack: getErrorStack(error), + }, + startKey + ); + setFormError("Failed to setup a container for saved queries"); + setFormErrorsDetails(`Failed to setup a container for saved queries: ${errorMessage}`); + } finally { + setLoadingFalse(); + } + }; + + return ( + +
+
+ {!canSaveQueries() ? ( + {setupSaveQueriesText} + ) : ( + { + setQueryName(newInput); + }} + /> + )} +
+
+
+ ); +}; diff --git a/src/Explorer/Panes/SettingsPane/__snapshots__/index.test.tsx.snap b/src/Explorer/Panes/SettingsPane/__snapshots__/index.test.tsx.snap index 4bf68f239..ad0242558 100644 --- a/src/Explorer/Panes/SettingsPane/__snapshots__/index.test.tsx.snap +++ b/src/Explorer/Panes/SettingsPane/__snapshots__/index.test.tsx.snap @@ -361,22 +361,6 @@ exports[`Settings Pane should render Default properly 1`] = ` "title": [Function], "visible": [Function], }, - SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -875,22 +859,6 @@ exports[`Settings Pane should render Default properly 1`] = ` "container": [Circular], "parameters": [Function], }, - "saveQueryPane": SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, @@ -1435,22 +1403,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = ` "title": [Function], "visible": [Function], }, - SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -1949,22 +1901,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = ` "container": [Circular], "parameters": [Function], }, - "saveQueryPane": SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, diff --git a/src/Explorer/Panes/UploadItemsPane/__snapshots__/index.test.tsx.snap b/src/Explorer/Panes/UploadItemsPane/__snapshots__/index.test.tsx.snap index 58f219b7b..1662646db 100644 --- a/src/Explorer/Panes/UploadItemsPane/__snapshots__/index.test.tsx.snap +++ b/src/Explorer/Panes/UploadItemsPane/__snapshots__/index.test.tsx.snap @@ -361,22 +361,6 @@ exports[`Upload Items Pane should render Default properly 1`] = ` "title": [Function], "visible": [Function], }, - SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -875,22 +859,6 @@ exports[`Upload Items Pane should render Default properly 1`] = ` "container": [Circular], "parameters": [Function], }, - "saveQueryPane": SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, diff --git a/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap b/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap index 67fa05328..d4eca0772 100644 --- a/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap +++ b/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap @@ -362,22 +362,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database "title": [Function], "visible": [Function], }, - SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -880,22 +864,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database "container": [Circular], "parameters": [Function], }, - "saveQueryPane": SaveQueryPane { - "canSaveQueries": [Function], - "container": [Circular], - "firstFieldHasFocus": [Function], - "formErrors": [Function], - "formErrorsDetails": [Function], - "id": "savequerypane", - "isExecuting": [Function], - "isTemplateReady": [Function], - "queryName": [Function], - "setupQueries": [Function], - "setupSaveQueriesText": "For compliance reasons, we save queries in a container in your Azure Cosmos account, in a separate database called “___Cosmos”. To proceed, we need to create a container in your account, estimated additional cost is $0.77 daily.", - "submit": [Function], - "title": [Function], - "visible": [Function], - }, "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, diff --git a/src/Explorer/Tabs/QueryTab.ts b/src/Explorer/Tabs/QueryTab.ts index b91043440..2829f2fac 100644 --- a/src/Explorer/Tabs/QueryTab.ts +++ b/src/Explorer/Tabs/QueryTab.ts @@ -1,23 +1,22 @@ import * as ko from "knockout"; +import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg"; +import SaveQueryIcon from "../../../images/save-cosmos.svg"; import * as Constants from "../../Common/Constants"; +import { queryDocuments } from "../../Common/dataAccess/queryDocuments"; +import { queryDocumentsPage } from "../../Common/dataAccess/queryDocumentsPage"; +import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"; +import { HashMap } from "../../Common/HashMap"; +import * as HeadersUtility from "../../Common/HeadersUtility"; +import { MinimalQueryIterator } from "../../Common/IteratorUtilities"; +import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter"; import * as DataModels from "../../Contracts/DataModels"; import * as ViewModels from "../../Contracts/ViewModels"; import { Action } from "../../Shared/Telemetry/TelemetryConstants"; -import TabsBase from "./TabsBase"; -import { HashMap } from "../../Common/HashMap"; -import * as HeadersUtility from "../../Common/HeadersUtility"; -import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; -import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg"; import * as QueryUtils from "../../Utils/QueryUtils"; -import SaveQueryIcon from "../../../images/save-cosmos.svg"; - -import { MinimalQueryIterator } from "../../Common/IteratorUtilities"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; -import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"; -import { queryDocuments } from "../../Common/dataAccess/queryDocuments"; -import { queryDocumentsPage } from "../../Common/dataAccess/queryDocumentsPage"; import template from "./QueryTab.html"; +import TabsBase from "./TabsBase"; enum ToggleState { Result, @@ -190,7 +189,7 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem }; public onSaveQueryClick = (): void => { - this.collection && this.collection.container && this.collection.container.saveQueryPane.open(); + this.collection && this.collection.container && this.collection.container.openSaveQueryPanel(); }; public onSavedQueriesClick = (): void => { diff --git a/src/Main.tsx b/src/Main.tsx index 3b6f1ef20..654229252 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -240,7 +240,6 @@ const App: React.FunctionComponent = () => {
-