Skip to content

Commit 6d79312

Browse files
authored
fix(compass-crud): show error for bulk operations COMPASS-8529 (#7095)
1 parent 5f4dcf4 commit 6d79312

File tree

3 files changed

+86
-53
lines changed

3 files changed

+86
-53
lines changed

packages/compass-crud/src/components/bulk-actions-toasts.spec.tsx

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from './bulk-actions-toasts';
1818
import { expect } from 'chai';
1919
import sinon from 'sinon';
20+
import { MongoNetworkError } from 'mongodb';
2021

2122
function renderToastPortal() {
2223
return render(<ToastArea></ToastArea>);
@@ -67,30 +68,48 @@ describe('Bulk Action Toasts', function () {
6768
{
6869
modal: openBulkDeleteFailureToast,
6970
affected: undefined,
70-
expected: 'The delete operation failed.',
71+
error: new Error('Test error'),
72+
expected: ['The delete operation failed.', 'Test error'],
7173
},
7274
{
7375
modal: openBulkDeleteFailureToast,
7476
affected: 1,
75-
expected: '1 document could not been deleted.',
77+
error: new Error('Another test error'),
78+
expected: ['1 document could not be deleted.', 'Another test error'],
7679
},
7780
{
7881
modal: openBulkDeleteFailureToast,
7982
affected: 2,
80-
expected: '2 documents could not been deleted.',
83+
error: new Error('Another failure'),
84+
expected: ['2 documents could not be deleted.', 'Another failure'],
85+
},
86+
{
87+
modal: openBulkDeleteFailureToast,
88+
affected: 2,
89+
error: new MongoNetworkError('Connection lost'),
90+
expected: [
91+
'Delete operation - network error occurred.',
92+
'Connection lost',
93+
],
8194
},
8295
];
8396

8497
for (const useCase of USE_CASES) {
8598
it(`${useCase.modal.name} shows the text '${useCase.expected}' when affected document/s is/are '${useCase.affected}'`, async function () {
8699
useCase.modal({
87100
affectedDocuments: useCase.affected,
101+
error: useCase.error as Error,
88102
onRefresh: () => {},
89103
});
90104

91105
await waitFor(async function () {
92-
const node = await screen.findByText(useCase.expected);
93-
expect(node).to.exist;
106+
if (!Array.isArray(useCase.expected)) {
107+
expect(await screen.findByText(useCase.expected)).to.exist;
108+
} else {
109+
for (const expectedText of useCase.expected) {
110+
expect(await screen.findByText(expectedText)).to.exist;
111+
}
112+
}
94113
});
95114
});
96115
}
@@ -160,30 +179,49 @@ describe('Bulk Action Toasts', function () {
160179
{
161180
modal: openBulkUpdateFailureToast,
162181
affected: undefined,
163-
expected: 'The update operation failed.',
182+
error: new Error('Test error'),
183+
expected: ['The update operation failed.', 'Test error'],
164184
},
165185
{
166186
modal: openBulkUpdateFailureToast,
167187
affected: 1,
168-
expected: '1 document could not been updated.',
188+
error: new Error('Could not update'),
189+
expected: ['1 document could not be updated.', 'Could not update'],
169190
},
170191
{
171192
modal: openBulkUpdateFailureToast,
172193
affected: 2,
173-
expected: '2 documents could not been updated.',
194+
error: new Error('Update failed'),
195+
expected: ['2 documents could not be updated.', 'Update failed'],
196+
},
197+
198+
{
199+
modal: openBulkUpdateFailureToast,
200+
affected: 2,
201+
error: new MongoNetworkError('Connection lost'),
202+
expected: [
203+
'Update operation - network error occurred.',
204+
'Connection lost',
205+
],
174206
},
175207
];
176208

177209
for (const useCase of USE_CASES) {
178210
it(`${useCase.modal.name} shows the text '${useCase.expected}' when ${useCase.affected} document/s affected`, async function () {
179211
useCase.modal({
180212
affectedDocuments: useCase.affected,
213+
error: useCase.error,
181214
onRefresh: () => {},
182215
});
183216

184217
await waitFor(async function () {
185-
const node = await screen.findByText(useCase.expected);
186-
expect(node).to.exist;
218+
if (!Array.isArray(useCase.expected)) {
219+
expect(await screen.findByText(useCase.expected)).to.exist;
220+
} else {
221+
for (const expectedText of useCase.expected) {
222+
expect(await screen.findByText(expectedText)).to.exist;
223+
}
224+
}
187225
});
188226
});
189227
}

packages/compass-crud/src/components/bulk-actions-toasts.tsx

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
closeToast,
55
ToastBody,
66
} from '@mongodb-js/compass-components';
7+
import { MongoNetworkError } from 'mongodb';
78

89
type BulkDeleteSuccessToastProps = {
910
affectedDocuments?: number;
@@ -72,33 +73,48 @@ export function openBulkDeleteProgressToast({
7273
});
7374
}
7475

75-
type BulkDeleteFailureToastProps = {
76+
type BulkOperationFailureToastProps = {
7677
affectedDocuments?: number;
78+
error: Error;
79+
type: 'delete' | 'update';
7780
};
7881

79-
export function openBulkDeleteFailureToast({
82+
const isNetworkError = (error: Error) => error instanceof MongoNetworkError;
83+
84+
export function openBulkOperationFailureToast({
8085
affectedDocuments,
81-
}: BulkDeleteFailureToastProps): void {
82-
let text;
83-
switch (affectedDocuments) {
84-
case undefined:
85-
text = 'The delete operation failed.';
86-
break;
87-
case 1:
88-
text = `${affectedDocuments} document could not been deleted.`;
89-
break;
90-
default:
91-
text = `${affectedDocuments} documents could not been deleted.`;
86+
error,
87+
type,
88+
}: BulkOperationFailureToastProps): void {
89+
let title: string;
90+
if (isNetworkError(error)) {
91+
title = `${
92+
type === 'delete' ? 'Delete' : 'Update'
93+
} operation - network error occurred.`;
94+
} else if (affectedDocuments === undefined) {
95+
title = `The ${type} operation failed.`;
96+
} else if (affectedDocuments === 1) {
97+
title = `${affectedDocuments} document could not be ${
98+
type === 'delete' ? 'deleted' : 'updated'
99+
}.`;
100+
} else {
101+
title = `${affectedDocuments} documents could not be ${
102+
type === 'delete' ? 'deleted' : 'updated'
103+
}.`;
92104
}
93105

94-
openToast('bulk-delete-toast', {
95-
title: '',
106+
openToast(`bulk-${type}-toast`, {
107+
title,
96108
variant: 'warning',
97109
dismissible: true,
98-
description: <ToastBody statusMessage={text} />,
110+
description: <ToastBody statusMessage={error.message} />,
99111
});
100112
}
101113

114+
export const openBulkDeleteFailureToast = (
115+
props: Omit<BulkOperationFailureToastProps, 'type'>
116+
): void => openBulkOperationFailureToast({ ...props, type: 'delete' });
117+
102118
type BulkUpdateSuccessToastProps = {
103119
affectedDocuments?: number;
104120
onRefresh: () => void;
@@ -166,29 +182,6 @@ export function openBulkUpdateProgressToast({
166182
});
167183
}
168184

169-
type BulkUpdateFailureToastProps = {
170-
affectedDocuments?: number;
171-
};
172-
173-
export function openBulkUpdateFailureToast({
174-
affectedDocuments,
175-
}: BulkUpdateFailureToastProps): void {
176-
let text;
177-
switch (affectedDocuments) {
178-
case undefined:
179-
text = 'The update operation failed.';
180-
break;
181-
case 1:
182-
text = `${affectedDocuments} document could not been updated.`;
183-
break;
184-
default:
185-
text = `${affectedDocuments} documents could not been updated.`;
186-
}
187-
188-
openToast('bulk-update-toast', {
189-
title: '',
190-
variant: 'warning',
191-
dismissible: true,
192-
description: <ToastBody statusMessage={text} />,
193-
});
194-
}
185+
export const openBulkUpdateFailureToast = (
186+
props: Omit<BulkOperationFailureToastProps, 'type'>
187+
): void => openBulkOperationFailureToast({ ...props, type: 'update' });

packages/compass-crud/src/stores/crud-store.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { Listenable, Store } from 'reflux';
22
import Reflux from 'reflux';
33
import toNS from 'mongodb-ns';
44
import { findIndex, isEmpty, isEqual } from 'lodash';
5-
import type { MongoServerError } from 'mongodb';
65
import semver from 'semver';
76
import StateMixin from '@mongodb-js/reflux-state-mixin';
87
import type { Element } from 'hadron-document';
@@ -69,6 +68,7 @@ import type {
6968
} from '@mongodb-js/compass-connections/provider';
7069
import type { Query, QueryBarService } from '@mongodb-js/compass-query-bar';
7170
import type { TrackFunction } from '@mongodb-js/compass-telemetry';
71+
import type { MongoServerError } from 'mongodb';
7272

7373
export type BSONObject = TypeCastMap['Object'];
7474
export type BSONArray = TypeCastMap['Array'];
@@ -1243,6 +1243,7 @@ class CrudStoreImpl
12431243
} catch (err: any) {
12441244
openBulkUpdateFailureToast({
12451245
affectedDocuments: this.state.bulkUpdate.affected,
1246+
error: err as Error,
12461247
});
12471248

12481249
this.logger.log.error(
@@ -1899,6 +1900,7 @@ class CrudStoreImpl
18991900
bulkDeleteFailed(ex: Error) {
19001901
openBulkDeleteFailureToast({
19011902
affectedDocuments: this.state.bulkDelete.affected,
1903+
error: ex,
19021904
});
19031905

19041906
this.logger.log.error(

0 commit comments

Comments
 (0)