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.
This commit is contained in:
Tomasz Naumowicz 2024-11-20 15:18:05 +01:00 коммит произвёл GitHub
Родитель a6c79f90d6
Коммит b1d47fbaed
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
18 изменённых файлов: 132 добавлений и 20 удалений

Просмотреть файл

@ -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": [

Просмотреть файл

@ -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';

Просмотреть файл

@ -320,14 +320,15 @@ export class MongoClustersClient {
}
async createCollection(databaseName: string, collectionName: string): Promise<boolean> {
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<boolean> {

Просмотреть файл

@ -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<v
// refresh the workspace tree view
ext.mongoClustersWorkspaceBranchDataProvider.refresh();
showConfirmationAsInSettings(
localize('showConfirmation.addedWorkspaceConnecdtion', 'New connection has been added to your workspace.'),
);
}
function isMongoDBRU(host: string): boolean {

Просмотреть файл

@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { AzureWizard, nonNullValue, type IActionContext } from '@microsoft/vscode-azext-utils';
import { showConfirmationAsInSettings } from '../../utils/dialogs/showConfirmation';
import { localize } from '../../utils/localize';
import { type DatabaseItem } from '../tree/DatabaseItem';
import { type CreateCollectionWizardContext } from '../wizards/create/createWizardContexts';
@ -31,5 +32,11 @@ export async function createCollection(context: IActionContext, databaseNode?: D
const newCollectionName = nonNullValue(wizardContext.newCollectionName);
await databaseNode.createCollection(context, newCollectionName);
const success = await databaseNode.createCollection(context, newCollectionName);
if (success) {
showConfirmationAsInSettings(
localize('showConfirmation.createdDatabase', 'The "{0}" collection has been created.', newCollectionName),
);
}
}

Просмотреть файл

@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { AzureWizard, nonNullValue, type IActionContext } from '@microsoft/vscode-azext-utils';
import { showConfirmationAsInSettings } from '../../utils/dialogs/showConfirmation';
import { localize } from '../../utils/localize';
import { CredentialCache } from '../CredentialCache';
import { type MongoClusterResourceItem } from '../tree/MongoClusterResourceItem';
@ -45,5 +46,11 @@ export async function createDatabase(context: IActionContext, clusterNode?: Mong
const newDatabaseName = nonNullValue(wizardContext.newDatabaseName);
await clusterNode.createDatabase(context, newDatabaseName);
const success = await clusterNode.createDatabase(context, newDatabaseName);
if (success) {
showConfirmationAsInSettings(
localize('showConfirmation.createdDatabase', 'The "{0}" database has been created.', newDatabaseName),
);
}
}

Просмотреть файл

@ -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 CollectionItem } from '../tree/CollectionItem';
export async function dropCollection(context: IActionContext, node?: CollectionItem): Promise<void> {
@ -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,
),
);
}
}

Просмотреть файл

@ -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<void> {
@ -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,
),
);
}
}

Просмотреть файл

@ -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.',
),
);
}

Просмотреть файл

@ -55,13 +55,14 @@ export class CollectionItem {
async delete(_context: IActionContext): Promise<boolean> {
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<InsertDocumentsResult> {

Просмотреть файл

@ -48,13 +48,14 @@ export class DatabaseItem {
async delete(_context: IActionContext): Promise<boolean> {
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<boolean> {

Просмотреть файл

@ -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',

Просмотреть файл

@ -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<boolean>(ext.settingsKeys.showOperationSummaries);
if (showSummary) {
vscode.window.showInformationMessage(message, localize('showConfirmation.ok', 'OK'));
}
}

Просмотреть файл

@ -318,7 +318,7 @@ export const CollectionView = (): JSX.Element => {
return (
<CollectionViewContext.Provider value={[currentContext, setCurrentContext]}>
<div className="collectionView">
{currentContext.isLoading && <ProgressBar thickness="large" className="progressBar" />}
{currentContext.isLoading && <ProgressBar thickness="large" shape="square" className="progressBar" />}
<div className="toolbarMainView">
<ToolbarMainView />

Просмотреть файл

@ -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,

Просмотреть файл

@ -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={<SaveRegular />}
appearance={'primary'}
disabled={viewerMode === 'view'}
disabled={disableSaveButton}
>
Save
</ToolbarButton>

Просмотреть файл

@ -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 (
<div className="documentView">
<div className="toolbarContainer">
{isLoading && <ProgressBar thickness="large" className="progressBar" />}
{isLoading && <ProgressBar thickness="large" shape="square" className="progressBar" />}
<ToolbarDocuments
viewerMode={configuration.mode}
disableSaveButton={configuration.mode === 'view' || !isDirty}
onSaveRequest={handleOnSaveRequest}
onValidateRequest={handleOnValidateRequest}
onRefreshRequest={handleOnRefreshRequest}
@ -224,6 +226,9 @@ export const DocumentView = (): JSX.Element => {
options={monacoOptions}
value={editorContent}
onMount={handleMonacoEditorMount}
onChange={() => {
setIsDirty(true);
}}
/>
</div>
</div>

Просмотреть файл

@ -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 };
}),
});