-
Notifications
You must be signed in to change notification settings - Fork 8
Feat: Allow users to remove one than 1 connection #398
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Changes from all commits
40af04c
3f7eaee
6a08a2c
dba5df0
9bbfea0
6464580
c004935
edaebd4
e2a5372
1c3d328
58d5bfb
962c762
c6c2fed
2570556
52517aa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,42 +12,121 @@ import { type DocumentDBClusterItem } from '../../tree/connections-view/Document | |
| import { getConfirmationAsInSettings } from '../../utils/dialogs/getConfirmation'; | ||
| import { showConfirmationAsInSettings } from '../../utils/dialogs/showConfirmation'; | ||
|
|
||
| export async function removeAzureConnection(context: IActionContext, node: DocumentDBClusterItem): Promise<void> { | ||
| if (!node) { | ||
| throw new Error(l10n.t('No node selected.')); | ||
| export async function removeConnection( | ||
| context: IActionContext, | ||
| node?: DocumentDBClusterItem, | ||
| nodes?: DocumentDBClusterItem[], | ||
| ): Promise<void> { | ||
| // Determine the list of connections to delete | ||
| let connectionsToDelete: DocumentDBClusterItem[]; | ||
| if (nodes && nodes.length > 0) { | ||
| connectionsToDelete = nodes; | ||
| } else if (node) { | ||
| connectionsToDelete = [node]; | ||
| } else { | ||
| connectionsToDelete = []; | ||
| } | ||
|
|
||
| await removeConnection(context, node); | ||
| } | ||
| if (connectionsToDelete.length === 0) { | ||
| ext.outputChannel.warn(l10n.t('No connections selected to remove.')); | ||
| return; | ||
| } | ||
|
|
||
| export async function removeConnection(context: IActionContext, node: DocumentDBClusterItem): Promise<void> { | ||
| context.telemetry.properties.experience = node.experience.api; | ||
| // Set telemetry for the first node | ||
| context.telemetry.properties.experience = connectionsToDelete[0].experience.api; | ||
| context.telemetry.measurements.connectionsToDelete = connectionsToDelete.length; | ||
|
|
||
| // Confirmation logic - different messages for single vs. multiple deletions | ||
| const expectedConfirmationWord = | ||
| connectionsToDelete.length === 1 ? 'delete' : connectionsToDelete.length.toString(); | ||
| const confirmed = await getConfirmationAsInSettings( | ||
| l10n.t('Are you sure?'), | ||
| l10n.t('Delete "{connectionName}"?', { connectionName: node.cluster.name }) + | ||
| '\n' + | ||
| l10n.t('This cannot be undone.'), | ||
| 'delete', | ||
| connectionsToDelete.length === 1 | ||
| ? l10n.t('Delete "{connectionName}"?', { connectionName: connectionsToDelete[0].cluster.name }) + | ||
| '\n' + | ||
| l10n.t('This cannot be undone.') | ||
| : l10n.t('Delete {count} connections?', { count: connectionsToDelete.length }) + | ||
| '\n' + | ||
| l10n.t('This cannot be undone.'), | ||
| expectedConfirmationWord, | ||
| ); | ||
|
|
||
| if (!confirmed) { | ||
| throw new UserCancelledError(); | ||
| } | ||
|
|
||
| // continue with deletion | ||
| // Resilient deletion loop - continue on failure | ||
| let successCount = 0; | ||
| let failureCount = 0; | ||
|
|
||
| await ext.state.showDeleting(node.id, async () => { | ||
| if ((node as DocumentDBClusterItem).cluster.emulatorConfiguration?.isEmulator) { | ||
| await ConnectionStorageService.delete(ConnectionType.Emulators, node.storageId); | ||
| } else { | ||
| await ConnectionStorageService.delete(ConnectionType.Clusters, node.storageId); | ||
| } | ||
| }); | ||
| for (const connection of connectionsToDelete) { | ||
| try { | ||
| await ext.state.showDeleting(connection.id, async () => { | ||
| if (connection.cluster.emulatorConfiguration?.isEmulator) { | ||
| await ConnectionStorageService.delete(ConnectionType.Emulators, connection.storageId); | ||
| } else { | ||
| await ConnectionStorageService.delete(ConnectionType.Clusters, connection.storageId); | ||
| } | ||
| }); | ||
|
|
||
| // delete cached credentials from memory | ||
| CredentialCache.deleteCredentials(connection.id); | ||
|
|
||
| // Log success | ||
| ext.outputChannel.info( | ||
| l10n.t('Successfully removed connection "{connectionName}".', { | ||
| connectionName: connection.cluster.name, | ||
| }), | ||
| ); | ||
| successCount++; | ||
| } catch (error) { | ||
| // Log error and continue with next connection | ||
| ext.outputChannel.error( | ||
| l10n.t('Failed to remove connection "{connectionName}": {error}', { | ||
| connectionName: connection.cluster.name, | ||
| error: error instanceof Error ? error.message : String(error), | ||
| }), | ||
| ); | ||
|
|
||
| // delete cached credentials from memory | ||
| CredentialCache.deleteCredentials(node.id); | ||
| // Note: When multiple deletions fail, we intentionally capture only the last error's | ||
| // details in telemetry. The errorCount metric below provides context on total failures. | ||
| context.telemetry.properties.error = 'RemoveConnectionError'; | ||
| context.telemetry.properties.errorMessage = error instanceof Error ? error.message : String(error); | ||
|
Comment on lines
+93
to
+94
|
||
|
|
||
| failureCount++; | ||
| } | ||
| } | ||
|
|
||
| // Refresh the tree view | ||
| ext.connectionsBranchDataProvider.refresh(); | ||
|
|
||
| showConfirmationAsInSettings(l10n.t('The selected connection has been removed.')); | ||
| // Set telemetry for successfully deleted connections | ||
| context.telemetry.measurements.connectionsDeleted = successCount; | ||
| context.telemetry.measurements.errorCount = failureCount; | ||
|
|
||
| // Show summary message | ||
| if (connectionsToDelete.length === 1) { | ||
| // For single connection, show success or error message | ||
| if (successCount === 1) { | ||
| showConfirmationAsInSettings(l10n.t('The selected connection has been removed.')); | ||
| } else { | ||
| // Error is already logged to outputChannel, so throw error to show notification | ||
| throw new Error( | ||
| l10n.t('Failed to remove connection "{connectionName}".', { | ||
| connectionName: connectionsToDelete[0].cluster.name, | ||
| }), | ||
| ); | ||
| } | ||
| } else { | ||
| // Show summary for multiple deletions | ||
| const summaryMessage = | ||
| failureCount === 0 | ||
| ? l10n.t('Successfully removed {count} connections.', { count: successCount }) | ||
| : l10n.t('Removed {successCount} of {total} connections. {failureCount} failed.', { | ||
| successCount, | ||
| total: connectionsToDelete.length, | ||
| failureCount, | ||
| }); | ||
| showConfirmationAsInSettings(summaryMessage); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the function is called with no connections to delete (both
nodeandnodesare undefined or empty), it returns silently without any user feedback. This could leave users confused if the command is invoked unexpectedly without a selection.Consider logging a warning or showing a message to inform the user:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot agreed, please add this line, remember about running 'npm run prettier-fix' and 'npm run l10n' after making the edits.