diff --git a/packages/compass-crud/src/components/bulk-actions-toasts.spec.tsx b/packages/compass-crud/src/components/bulk-actions-toasts.spec.tsx
index 6d9f53d989c..9f821da670d 100644
--- a/packages/compass-crud/src/components/bulk-actions-toasts.spec.tsx
+++ b/packages/compass-crud/src/components/bulk-actions-toasts.spec.tsx
@@ -17,6 +17,7 @@ import {
} from './bulk-actions-toasts';
import { expect } from 'chai';
import sinon from 'sinon';
+import { MongoNetworkError } from 'mongodb';
function renderToastPortal() {
return render();
@@ -67,17 +68,29 @@ describe('Bulk Action Toasts', function () {
{
modal: openBulkDeleteFailureToast,
affected: undefined,
- expected: 'The delete operation failed.',
+ error: new Error('Test error'),
+ expected: ['The delete operation failed.', 'Test error'],
},
{
modal: openBulkDeleteFailureToast,
affected: 1,
- expected: '1 document could not been deleted.',
+ error: new Error('Another test error'),
+ expected: ['1 document could not be deleted.', 'Another test error'],
},
{
modal: openBulkDeleteFailureToast,
affected: 2,
- expected: '2 documents could not been deleted.',
+ error: new Error('Another failure'),
+ expected: ['2 documents could not be deleted.', 'Another failure'],
+ },
+ {
+ modal: openBulkDeleteFailureToast,
+ affected: 2,
+ error: new MongoNetworkError('Connection lost'),
+ expected: [
+ 'Delete operation - network error occurred.',
+ 'Connection lost',
+ ],
},
];
@@ -85,12 +98,18 @@ describe('Bulk Action Toasts', function () {
it(`${useCase.modal.name} shows the text '${useCase.expected}' when affected document/s is/are '${useCase.affected}'`, async function () {
useCase.modal({
affectedDocuments: useCase.affected,
+ error: useCase.error as Error,
onRefresh: () => {},
});
await waitFor(async function () {
- const node = await screen.findByText(useCase.expected);
- expect(node).to.exist;
+ if (!Array.isArray(useCase.expected)) {
+ expect(await screen.findByText(useCase.expected)).to.exist;
+ } else {
+ for (const expectedText of useCase.expected) {
+ expect(await screen.findByText(expectedText)).to.exist;
+ }
+ }
});
});
}
@@ -160,17 +179,30 @@ describe('Bulk Action Toasts', function () {
{
modal: openBulkUpdateFailureToast,
affected: undefined,
- expected: 'The update operation failed.',
+ error: new Error('Test error'),
+ expected: ['The update operation failed.', 'Test error'],
},
{
modal: openBulkUpdateFailureToast,
affected: 1,
- expected: '1 document could not been updated.',
+ error: new Error('Could not update'),
+ expected: ['1 document could not be updated.', 'Could not update'],
},
{
modal: openBulkUpdateFailureToast,
affected: 2,
- expected: '2 documents could not been updated.',
+ error: new Error('Update failed'),
+ expected: ['2 documents could not be updated.', 'Update failed'],
+ },
+
+ {
+ modal: openBulkUpdateFailureToast,
+ affected: 2,
+ error: new MongoNetworkError('Connection lost'),
+ expected: [
+ 'Update operation - network error occurred.',
+ 'Connection lost',
+ ],
},
];
@@ -178,12 +210,18 @@ describe('Bulk Action Toasts', function () {
it(`${useCase.modal.name} shows the text '${useCase.expected}' when ${useCase.affected} document/s affected`, async function () {
useCase.modal({
affectedDocuments: useCase.affected,
+ error: useCase.error,
onRefresh: () => {},
});
await waitFor(async function () {
- const node = await screen.findByText(useCase.expected);
- expect(node).to.exist;
+ if (!Array.isArray(useCase.expected)) {
+ expect(await screen.findByText(useCase.expected)).to.exist;
+ } else {
+ for (const expectedText of useCase.expected) {
+ expect(await screen.findByText(expectedText)).to.exist;
+ }
+ }
});
});
}
diff --git a/packages/compass-crud/src/components/bulk-actions-toasts.tsx b/packages/compass-crud/src/components/bulk-actions-toasts.tsx
index 474470ba8a3..be1c308072f 100644
--- a/packages/compass-crud/src/components/bulk-actions-toasts.tsx
+++ b/packages/compass-crud/src/components/bulk-actions-toasts.tsx
@@ -4,6 +4,7 @@ import {
closeToast,
ToastBody,
} from '@mongodb-js/compass-components';
+import { MongoNetworkError } from 'mongodb';
type BulkDeleteSuccessToastProps = {
affectedDocuments?: number;
@@ -72,33 +73,48 @@ export function openBulkDeleteProgressToast({
});
}
-type BulkDeleteFailureToastProps = {
+type BulkOperationFailureToastProps = {
affectedDocuments?: number;
+ error: Error;
+ type: 'delete' | 'update';
};
-export function openBulkDeleteFailureToast({
+const isNetworkError = (error: Error) => error instanceof MongoNetworkError;
+
+export function openBulkOperationFailureToast({
affectedDocuments,
-}: BulkDeleteFailureToastProps): void {
- let text;
- switch (affectedDocuments) {
- case undefined:
- text = 'The delete operation failed.';
- break;
- case 1:
- text = `${affectedDocuments} document could not been deleted.`;
- break;
- default:
- text = `${affectedDocuments} documents could not been deleted.`;
+ error,
+ type,
+}: BulkOperationFailureToastProps): void {
+ let title: string;
+ if (isNetworkError(error)) {
+ title = `${
+ type === 'delete' ? 'Delete' : 'Update'
+ } operation - network error occurred.`;
+ } else if (affectedDocuments === undefined) {
+ title = `The ${type} operation failed.`;
+ } else if (affectedDocuments === 1) {
+ title = `${affectedDocuments} document could not be ${
+ type === 'delete' ? 'deleted' : 'updated'
+ }.`;
+ } else {
+ title = `${affectedDocuments} documents could not be ${
+ type === 'delete' ? 'deleted' : 'updated'
+ }.`;
}
- openToast('bulk-delete-toast', {
- title: '',
+ openToast(`bulk-${type}-toast`, {
+ title,
variant: 'warning',
dismissible: true,
- description: ,
+ description: ,
});
}
+export const openBulkDeleteFailureToast = (
+ props: Omit
+): void => openBulkOperationFailureToast({ ...props, type: 'delete' });
+
type BulkUpdateSuccessToastProps = {
affectedDocuments?: number;
onRefresh: () => void;
@@ -166,29 +182,6 @@ export function openBulkUpdateProgressToast({
});
}
-type BulkUpdateFailureToastProps = {
- affectedDocuments?: number;
-};
-
-export function openBulkUpdateFailureToast({
- affectedDocuments,
-}: BulkUpdateFailureToastProps): void {
- let text;
- switch (affectedDocuments) {
- case undefined:
- text = 'The update operation failed.';
- break;
- case 1:
- text = `${affectedDocuments} document could not been updated.`;
- break;
- default:
- text = `${affectedDocuments} documents could not been updated.`;
- }
-
- openToast('bulk-update-toast', {
- title: '',
- variant: 'warning',
- dismissible: true,
- description: ,
- });
-}
+export const openBulkUpdateFailureToast = (
+ props: Omit
+): void => openBulkOperationFailureToast({ ...props, type: 'update' });
diff --git a/packages/compass-crud/src/stores/crud-store.ts b/packages/compass-crud/src/stores/crud-store.ts
index 81bc2087c3d..94266d6a916 100644
--- a/packages/compass-crud/src/stores/crud-store.ts
+++ b/packages/compass-crud/src/stores/crud-store.ts
@@ -2,7 +2,6 @@ import type { Listenable, Store } from 'reflux';
import Reflux from 'reflux';
import toNS from 'mongodb-ns';
import { findIndex, isEmpty, isEqual } from 'lodash';
-import type { MongoServerError } from 'mongodb';
import semver from 'semver';
import StateMixin from '@mongodb-js/reflux-state-mixin';
import type { Element } from 'hadron-document';
@@ -69,6 +68,7 @@ import type {
} from '@mongodb-js/compass-connections/provider';
import type { Query, QueryBarService } from '@mongodb-js/compass-query-bar';
import type { TrackFunction } from '@mongodb-js/compass-telemetry';
+import type { MongoServerError } from 'mongodb';
export type BSONObject = TypeCastMap['Object'];
export type BSONArray = TypeCastMap['Array'];
@@ -1243,6 +1243,7 @@ class CrudStoreImpl
} catch (err: any) {
openBulkUpdateFailureToast({
affectedDocuments: this.state.bulkUpdate.affected,
+ error: err as Error,
});
this.logger.log.error(
@@ -1899,6 +1900,7 @@ class CrudStoreImpl
bulkDeleteFailed(ex: Error) {
openBulkDeleteFailureToast({
affectedDocuments: this.state.bulkDelete.affected,
+ error: ex,
});
this.logger.log.error(