From b1d47fbaed05f2ad582e616835886dc4a1e1035d Mon Sep 17 00:00:00 2001 From: Tomasz Naumowicz Date: Wed, 20 Nov 2024 15:18:05 +0100 Subject: [PATCH] vCore: feat: added operation summary messages (#2430) Added confirmation messages for basic operations. Refactored confirmations.ts to a more appropriate name and location. Propagated success feedback for confirmation messages and future use. --- package.json | 5 +++++ src/extensionVariables.ts | 1 + src/mongoClusters/MongoClustersClient.ts | 5 +++-- .../commands/addWorkspaceConnection.ts | 5 +++++ .../commands/createCollection.ts | 9 ++++++++- src/mongoClusters/commands/createDatabase.ts | 9 ++++++++- src/mongoClusters/commands/dropCollection.ts | 16 +++++++++++++-- src/mongoClusters/commands/dropDatabase.ts | 16 +++++++++++++-- .../commands/removeWorkspaceConnection.ts | 9 +++++++++ src/mongoClusters/tree/CollectionItem.ts | 5 +++-- src/mongoClusters/tree/DatabaseItem.ts | 5 +++-- .../getConfirmation.ts} | 2 +- src/utils/dialogs/showConfirmation.ts | 18 +++++++++++++++++ .../collectionView/CollectionView.tsx | 2 +- .../collectionView/collectionViewRouter.ts | 20 ++++++++++++++++++- .../components/toolbarDocuments.tsx | 6 +++--- .../documentView/documentView.tsx | 9 +++++++-- .../documentView/documentsViewRouter.ts | 10 ++++++++++ 18 files changed, 132 insertions(+), 20 deletions(-) rename src/utils/{confirmations.ts => dialogs/getConfirmation.ts} (99%) create mode 100644 src/utils/dialogs/showConfirmation.ts diff --git a/package.json b/package.json index e7b35f75..c4673d76 100644 --- a/package.json +++ b/package.json @@ -1282,6 +1282,11 @@ ], "default": "wordConfirmation" }, + "azureDatabases.showOperationSummaries": { + "type": "boolean", + "default": true, + "description": "Show detailed operation summaries, displaying messages for actions such as database drops, document additions, deletions, or similar events." + }, "cosmosDB.documentLabelFields": { "type": "array", "default": [ diff --git a/src/extensionVariables.ts b/src/extensionVariables.ts index 347f4f2b..2ad559b6 100644 --- a/src/extensionVariables.ts +++ b/src/extensionVariables.ts @@ -67,6 +67,7 @@ export namespace ext { export const mongoShellTimeout = 'mongo.shell.timeout'; export const batchSize = 'azureDatabases.batchSize'; export const confirmationStyle = 'azureDatabases.confirmationStyle'; + export const showOperationSummaries = 'azureDatabases.showOperationSummaries'; export namespace vsCode { export const proxyStrictSSL = 'http.proxyStrictSSL'; diff --git a/src/mongoClusters/MongoClustersClient.ts b/src/mongoClusters/MongoClustersClient.ts index 8b55f424..727fbaf2 100644 --- a/src/mongoClusters/MongoClustersClient.ts +++ b/src/mongoClusters/MongoClustersClient.ts @@ -320,14 +320,15 @@ export class MongoClustersClient { } async createCollection(databaseName: string, collectionName: string): Promise { + let newCollection; try { - await this._mongoClient.db(databaseName).createCollection(collectionName); + newCollection = await this._mongoClient.db(databaseName).createCollection(collectionName); } catch (_e) { console.log(_e); //todo: add to telemetry return false; } - return true; + return newCollection !== undefined; } async createDatabase(databaseName: string): Promise { diff --git a/src/mongoClusters/commands/addWorkspaceConnection.ts b/src/mongoClusters/commands/addWorkspaceConnection.ts index ba14aeb0..31ac2ce5 100644 --- a/src/mongoClusters/commands/addWorkspaceConnection.ts +++ b/src/mongoClusters/commands/addWorkspaceConnection.ts @@ -10,6 +10,7 @@ import { API } from '../../AzureDBExperiences'; import { ext } from '../../extensionVariables'; import { WorkspaceResourceType } from '../../tree/workspace/sharedWorkspaceResourceProvider'; import { SharedWorkspaceStorage } from '../../tree/workspace/sharedWorkspaceStorage'; +import { showConfirmationAsInSettings } from '../../utils/dialogs/showConfirmation'; import { localize } from '../../utils/localize'; import { type AddWorkspaceConnectionContext } from '../wizards/addWorkspaceConnection/AddWorkspaceConnectionContext'; import { ConnectionStringStep } from '../wizards/addWorkspaceConnection/ConnectionStringStep'; @@ -98,6 +99,10 @@ export async function addWorkspaceConnection(context: IActionContext): Promise { @@ -23,5 +25,15 @@ export async function dropCollection(context: IActionContext, node?: CollectionI return; } - await node.delete(context); + const success = await node.delete(context); + + if (success) { + showConfirmationAsInSettings( + localize( + 'showConfirmation.droppedCollection', + 'The "{0}" collection has been dropped.', + node.collectionInfo.name, + ), + ); + } } diff --git a/src/mongoClusters/commands/dropDatabase.ts b/src/mongoClusters/commands/dropDatabase.ts index cb20d404..52f3b860 100644 --- a/src/mongoClusters/commands/dropDatabase.ts +++ b/src/mongoClusters/commands/dropDatabase.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { type IActionContext } from '@microsoft/vscode-azext-utils'; -import { getConfirmationAsInSettings } from '../../utils/confirmations'; +import { getConfirmationAsInSettings } from '../../utils/dialogs/getConfirmation'; +import { showConfirmationAsInSettings } from '../../utils/dialogs/showConfirmation'; +import { localize } from '../../utils/localize'; import { type DatabaseItem } from '../tree/DatabaseItem'; export async function dropDatabase(context: IActionContext, node?: DatabaseItem): Promise { @@ -23,5 +25,15 @@ export async function dropDatabase(context: IActionContext, node?: DatabaseItem) return; } - await node.delete(context); + const success = await node.delete(context); + + if (success) { + showConfirmationAsInSettings( + localize( + 'showConfirmation.droppedDatabase', + 'The "{0}" database has been dropped.', + node.databaseInfo.name, + ), + ); + } } diff --git a/src/mongoClusters/commands/removeWorkspaceConnection.ts b/src/mongoClusters/commands/removeWorkspaceConnection.ts index 1fec28cc..123299ed 100644 --- a/src/mongoClusters/commands/removeWorkspaceConnection.ts +++ b/src/mongoClusters/commands/removeWorkspaceConnection.ts @@ -7,6 +7,8 @@ import { type IActionContext } from '@microsoft/vscode-azext-utils'; import { ext } from '../../extensionVariables'; import { WorkspaceResourceType } from '../../tree/workspace/sharedWorkspaceResourceProvider'; import { SharedWorkspaceStorage } from '../../tree/workspace/sharedWorkspaceStorage'; +import { showConfirmationAsInSettings } from '../../utils/dialogs/showConfirmation'; +import { localize } from '../../utils/localize'; import { type MongoClusterWorkspaceItem } from '../tree/workspace/MongoClusterWorkspaceItem'; export async function removeWorkspaceConnection( @@ -18,4 +20,11 @@ export async function removeWorkspaceConnection( }); ext.mongoClustersWorkspaceBranchDataProvider.refresh(); + + showConfirmationAsInSettings( + localize( + 'showConfirmation.removedWorkspaceConnecdtion', + 'The selected connection has been removed from your workspace.', + ), + ); } diff --git a/src/mongoClusters/tree/CollectionItem.ts b/src/mongoClusters/tree/CollectionItem.ts index c059dd0a..672ed39d 100644 --- a/src/mongoClusters/tree/CollectionItem.ts +++ b/src/mongoClusters/tree/CollectionItem.ts @@ -55,13 +55,14 @@ export class CollectionItem { async delete(_context: IActionContext): Promise { const client = await MongoClustersClient.getClient(this.mongoCluster.id); + let success = false; await ext.state.showDeleting(this.id, async () => { - await client.dropCollection(this.databaseInfo.name, this.collectionInfo.name); + success = await client.dropCollection(this.databaseInfo.name, this.collectionInfo.name); }); ext.state.notifyChildrenChanged(`${this.mongoCluster.id}/${this.databaseInfo.name}`); - return true; + return success; } async insertDocuments(_context: IActionContext, documents: Document[]): Promise { diff --git a/src/mongoClusters/tree/DatabaseItem.ts b/src/mongoClusters/tree/DatabaseItem.ts index 4d391f5c..12c85180 100644 --- a/src/mongoClusters/tree/DatabaseItem.ts +++ b/src/mongoClusters/tree/DatabaseItem.ts @@ -48,13 +48,14 @@ export class DatabaseItem { async delete(_context: IActionContext): Promise { const client = await MongoClustersClient.getClient(this.mongoCluster.id); + let success = false; await ext.state.showDeleting(this.id, async () => { - await client.dropDatabase(this.databaseInfo.name); + success = await client.dropDatabase(this.databaseInfo.name); }); ext.state.notifyChildrenChanged(this.mongoCluster.id); - return true; + return success; } async createCollection(_context: IActionContext, collectionName: string): Promise { diff --git a/src/utils/confirmations.ts b/src/utils/dialogs/getConfirmation.ts similarity index 99% rename from src/utils/confirmations.ts rename to src/utils/dialogs/getConfirmation.ts index 24faaead..7a384e71 100644 --- a/src/utils/confirmations.ts +++ b/src/utils/dialogs/getConfirmation.ts @@ -5,7 +5,7 @@ import { DialogResponses, UserCancelledError } from '@microsoft/vscode-azext-utils'; import vscode from 'vscode'; -import { ext } from '../extensionVariables'; +import { ext } from '../../extensionVariables'; enum ConfirmationStyle { wordConfirmation = 'wordConfirmation', diff --git a/src/utils/dialogs/showConfirmation.ts b/src/utils/dialogs/showConfirmation.ts new file mode 100644 index 00000000..fa3afd61 --- /dev/null +++ b/src/utils/dialogs/showConfirmation.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import vscode from 'vscode'; +import { ext } from '../../extensionVariables'; +import { localize } from '../localize'; + +export function showConfirmationAsInSettings(message: string) { + const showSummary: boolean | undefined = vscode.workspace + .getConfiguration() + .get(ext.settingsKeys.showOperationSummaries); + + if (showSummary) { + vscode.window.showInformationMessage(message, localize('showConfirmation.ok', 'OK')); + } +} diff --git a/src/webviews/mongoClusters/collectionView/CollectionView.tsx b/src/webviews/mongoClusters/collectionView/CollectionView.tsx index 4e4fccc4..b5c96814 100644 --- a/src/webviews/mongoClusters/collectionView/CollectionView.tsx +++ b/src/webviews/mongoClusters/collectionView/CollectionView.tsx @@ -318,7 +318,7 @@ export const CollectionView = (): JSX.Element => { return (
- {currentContext.isLoading && } + {currentContext.isLoading && }
diff --git a/src/webviews/mongoClusters/collectionView/collectionViewRouter.ts b/src/webviews/mongoClusters/collectionView/collectionViewRouter.ts index 3caf48f2..950de065 100644 --- a/src/webviews/mongoClusters/collectionView/collectionViewRouter.ts +++ b/src/webviews/mongoClusters/collectionView/collectionViewRouter.ts @@ -8,14 +8,16 @@ import { type JSONSchema } from 'vscode-json-languageservice'; import { z } from 'zod'; import { type MongoClustersClient } from '../../../mongoClusters/MongoClustersClient'; import { MongoClustersSession } from '../../../mongoClusters/MongoClusterSession'; -import { getConfirmationAsInSettings } from '../../../utils/confirmations'; +import { getConfirmationAsInSettings } from '../../../utils/dialogs/getConfirmation'; import { getKnownFields, type FieldEntry } from '../../../utils/json/mongo/autocomplete/getKnownFields'; import { publicProcedure, router } from '../../api/extension-server/trpc'; import { type CollectionItem } from '../../../mongoClusters/tree/CollectionItem'; +import { showConfirmationAsInSettings } from '../../../utils/dialogs/showConfirmation'; // eslint-disable-next-line import/no-internal-modules import basicFindQuerySchema from '../../../utils/json/mongo/autocomplete/basicMongoFindFilterSchema.json'; import { generateMongoFindJsonSchema } from '../../../utils/json/mongo/autocomplete/generateMongoFindJsonSchema'; +import { localize } from '../../../utils/localize'; export type RouterContext = { sessionId: string; @@ -171,6 +173,22 @@ export const collectionsViewRouter = router({ const acknowledged = await client.deleteDocuments(myCtx.databaseName, myCtx.collectionName, input); + if (acknowledged) { + showConfirmationAsInSettings( + input.length > 1 + ? localize( + 'showConfirmation.deletedNdocuments', + '{0} documents have been deleted.', + input.length, + ) + : localize( + 'showConfirmation.deletedNdocuments', + '{0} document has been deleted.', + input.length, + ), + ); + } + if (!acknowledged) { void vscode.window.showErrorMessage('Failed to delete documents. Unknown error.', { modal: true, diff --git a/src/webviews/mongoClusters/documentView/components/toolbarDocuments.tsx b/src/webviews/mongoClusters/documentView/components/toolbarDocuments.tsx index b05d946c..f956f64d 100644 --- a/src/webviews/mongoClusters/documentView/components/toolbarDocuments.tsx +++ b/src/webviews/mongoClusters/documentView/components/toolbarDocuments.tsx @@ -9,14 +9,14 @@ import { type JSX } from 'react'; import { ToolbarDividerTransparent } from '../../collectionView/components/toolbar/ToolbarDividerTransparent'; interface ToolbarDocumentsProps { - viewerMode: string; + disableSaveButton: boolean; onValidateRequest: () => void; onRefreshRequest: () => void; onSaveRequest: () => void; } export const ToolbarDocuments = ({ - viewerMode, + disableSaveButton, onValidateRequest, onRefreshRequest, onSaveRequest, @@ -29,7 +29,7 @@ export const ToolbarDocuments = ({ aria-label="Save to the database" icon={} appearance={'primary'} - disabled={viewerMode === 'view'} + disabled={disableSaveButton} > Save diff --git a/src/webviews/mongoClusters/documentView/documentView.tsx b/src/webviews/mongoClusters/documentView/documentView.tsx index 776d3633..3df958f1 100644 --- a/src/webviews/mongoClusters/documentView/documentView.tsx +++ b/src/webviews/mongoClusters/documentView/documentView.tsx @@ -45,6 +45,7 @@ export const DocumentView = (): JSX.Element => { const [editorContent] = configuration.mode === 'add' ? useState('{ }') : useState('{ "loading...": true }'); const [isLoading, setIsLoading] = useState(configuration.mode !== 'add'); + const [isDirty, setIsDirty] = useState(true); // a useEffect without a dependency runs only once after the first render only useEffect(() => { @@ -194,6 +195,7 @@ export const DocumentView = (): JSX.Element => { configuration.documentId = response.documentId; setContent(response.documentStringified); setIsLoading(false); + setIsDirty(false); }) .catch((error) => { console.error('Error saving document:', error); @@ -208,9 +210,9 @@ export const DocumentView = (): JSX.Element => { return (
- {isLoading && } + {isLoading && } { options={monacoOptions} value={editorContent} onMount={handleMonacoEditorMount} + onChange={() => { + setIsDirty(true); + }} />
diff --git a/src/webviews/mongoClusters/documentView/documentsViewRouter.ts b/src/webviews/mongoClusters/documentView/documentsViewRouter.ts index 5e26977b..1e46c45b 100644 --- a/src/webviews/mongoClusters/documentView/documentsViewRouter.ts +++ b/src/webviews/mongoClusters/documentView/documentsViewRouter.ts @@ -8,6 +8,8 @@ import { type Document } from 'mongodb'; import { z } from 'zod'; import { type MongoClustersClient } from '../../../mongoClusters/MongoClustersClient'; import { MongoClustersSession } from '../../../mongoClusters/MongoClusterSession'; +import { showConfirmationAsInSettings } from '../../../utils/dialogs/showConfirmation'; +import { localize } from '../../../utils/localize'; import { publicProcedure, router } from '../../api/extension-server/trpc'; export type RouterContext = { @@ -89,6 +91,14 @@ export const documentsViewRouter = router({ myCtx.viewPanelTitleSetter(`${myCtx.databaseName}/${myCtx.collectionName}/${newDocumentId}`); + showConfirmationAsInSettings( + localize( + 'showConfirmation.mongoClusters.documentView.saveDocument', + 'The document with the _id "{0}" has been saved.', + newDocumentId, + ), + ); + return { documentStringified: newDocumentStringified, documentId: newDocumentId }; }), });