Skip to content

Commit 948c73f

Browse files
authored
fix(compass-global-writes): handle loading error COMPASS-8446 (#6451)
1 parent efcc6ab commit 948c73f

File tree

5 files changed

+235
-20
lines changed

5 files changed

+235
-20
lines changed

packages/compass-global-writes/src/components/index.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import ShardKeyInvalid from './states/shard-key-invalid';
1616
import ShardKeyMismatch from './states/shard-key-mismatch';
1717
import ShardingError from './states/sharding-error';
1818
import IncompleteShardingSetup from './states/incomplete-sharding-setup';
19+
import LoadingError from './states/loading-error';
1920

2021
const containerStyles = css({
2122
display: 'flex',
@@ -90,6 +91,10 @@ function ShardingStateView({
9091
return <IncompleteShardingSetup />;
9192
}
9293

94+
if (shardingStatus === ShardingStatuses.LOADING_ERROR) {
95+
return <LoadingError />;
96+
}
97+
9398
return null;
9499
}
95100

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
import { expect } from 'chai';
3+
import { screen } from '@mongodb-js/testing-library-compass';
4+
import { LoadingError } from './loading-error';
5+
import { renderWithStore } from '../../../tests/create-store';
6+
7+
const error = 'Test failure';
8+
9+
function renderWithProps(
10+
props?: Partial<React.ComponentProps<typeof LoadingError>>
11+
) {
12+
return renderWithStore(<LoadingError error={error} {...props} />);
13+
}
14+
15+
describe('LoadingError', function () {
16+
it('renders the error', async function () {
17+
await renderWithProps();
18+
expect(screen.getByText(error)).to.exist;
19+
});
20+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react';
2+
import { ErrorSummary } from '@mongodb-js/compass-components';
3+
import { connect } from 'react-redux';
4+
import { type RootState, ShardingStatuses } from '../../store/reducer';
5+
import { containerStyles } from '../common-styles';
6+
7+
interface LoadingErrorProps {
8+
error: string;
9+
}
10+
11+
export function LoadingError({ error }: LoadingErrorProps) {
12+
return (
13+
<div className={containerStyles}>
14+
<ErrorSummary errors={error} />
15+
</div>
16+
);
17+
}
18+
19+
export default connect((state: RootState) => {
20+
if (state.status !== ShardingStatuses.LOADING_ERROR) {
21+
throw new Error('Error not found in LoadingError');
22+
}
23+
return {
24+
error: state.loadingError,
25+
};
26+
})(LoadingError);

packages/compass-global-writes/src/store/index.spec.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ function createStore({
7373
hasShardKey = () => false,
7474
failsOnShardingRequest = () => false,
7575
failsOnShardZoneRequest = () => false,
76+
failsToFetchClusterDetails = () => false,
77+
failsToFetchDeploymentStatus = () => false,
78+
failsToFetchShardKey = () => false,
7679
authenticatedFetchStub,
7780
}:
7881
| {
@@ -81,6 +84,9 @@ function createStore({
8184
hasShardKey?: () => boolean | AtlasShardKey;
8285
failsOnShardingRequest?: () => boolean;
8386
failsOnShardZoneRequest?: () => boolean;
87+
failsToFetchClusterDetails?: () => boolean;
88+
failsToFetchDeploymentStatus?: () => boolean;
89+
failsToFetchShardKey?: () => boolean;
8490
authenticatedFetchStub?: never;
8591
}
8692
| {
@@ -89,6 +95,9 @@ function createStore({
8995
hasShardKey?: () => boolean | ShardKey;
9096
failsOnShardingRequest?: never;
9197
failsOnShardZoneRequest?: () => never;
98+
failsToFetchClusterDetails?: never;
99+
failsToFetchDeploymentStatus?: never;
100+
failsToFetchShardKey?: () => boolean;
92101
authenticatedFetchStub?: () => void;
93102
} = {}): GlobalWritesStore {
94103
const atlasService = {
@@ -98,6 +107,9 @@ function createStore({
98107
}
99108

100109
if (uri.includes('/clusters/')) {
110+
if (failsToFetchClusterDetails()) {
111+
return Promise.reject(new Error('Failed to fetch cluster details'));
112+
}
101113
return createAuthFetchResponse({
102114
...clusterDetails,
103115
geoSharding: {
@@ -108,6 +120,9 @@ function createStore({
108120
}
109121

110122
if (uri.includes('/deploymentStatus/')) {
123+
if (failsToFetchDeploymentStatus()) {
124+
return Promise.reject(new Error('Failed to fetch deployment status'));
125+
}
111126
return createAuthFetchResponse({
112127
automationStatus: {
113128
processes: hasShardingError() ? [failedShardingProcess] : [],
@@ -130,6 +145,10 @@ function createStore({
130145
}),
131146
automationAgentAwait: (_meta: unknown, type: string) => {
132147
if (type === 'getShardKey') {
148+
if (failsToFetchShardKey()) {
149+
return Promise.reject(new Error('Failed to fetch shardKey'));
150+
}
151+
133152
const shardKey = hasShardKey();
134153
return {
135154
response:
@@ -188,6 +207,35 @@ describe('GlobalWritesStore Store', function () {
188207
});
189208

190209
context('scenarios', function () {
210+
context('initial load fail', function () {
211+
it('fails to fetch cluster details', async function () {
212+
const store = createStore({
213+
failsToFetchClusterDetails: () => true,
214+
});
215+
await waitFor(() => {
216+
expect(store.getState().status).to.equal('LOADING_ERROR');
217+
});
218+
});
219+
220+
it('fails to fetch shard key', async function () {
221+
const store = createStore({
222+
failsToFetchShardKey: () => true,
223+
});
224+
await waitFor(() => {
225+
expect(store.getState().status).to.equal('LOADING_ERROR');
226+
});
227+
});
228+
229+
it('fails to fetch deployment status', async function () {
230+
const store = createStore({
231+
failsToFetchDeploymentStatus: () => true,
232+
});
233+
await waitFor(() => {
234+
expect(store.getState().status).to.equal('LOADING_ERROR');
235+
});
236+
});
237+
});
238+
191239
it('not managed -> sharding -> valid shard key', async function () {
192240
let mockShardKey = false;
193241
let mockManagedNamespace = false;
@@ -291,6 +339,52 @@ describe('GlobalWritesStore Store', function () {
291339
});
292340
});
293341

342+
context('pulling fail', function () {
343+
it('sharding -> error (failed to fetch shard key)', async function () {
344+
let mockFailure = false;
345+
// initial state === sharding
346+
clock = sinon.useFakeTimers({
347+
shouldAdvanceTime: true,
348+
});
349+
const store = createStore({
350+
isNamespaceManaged: () => true,
351+
failsToFetchShardKey: Sinon.fake(() => mockFailure),
352+
});
353+
await waitFor(() => {
354+
expect(store.getState().status).to.equal('SHARDING');
355+
});
356+
357+
// sharding ends with a request failure
358+
mockFailure = true;
359+
clock.tick(POLLING_INTERVAL);
360+
await waitFor(() => {
361+
expect(store.getState().status).to.equal('LOADING_ERROR');
362+
});
363+
});
364+
365+
it('sharding -> error (failed to fetch deployment status)', async function () {
366+
let mockFailure = false;
367+
// initial state === sharding
368+
clock = sinon.useFakeTimers({
369+
shouldAdvanceTime: true,
370+
});
371+
const store = createStore({
372+
isNamespaceManaged: () => true,
373+
failsToFetchDeploymentStatus: Sinon.fake(() => mockFailure),
374+
});
375+
await waitFor(() => {
376+
expect(store.getState().status).to.equal('SHARDING');
377+
});
378+
379+
// sharding ends with a request failure
380+
mockFailure = true;
381+
clock.tick(POLLING_INTERVAL);
382+
await waitFor(() => {
383+
expect(store.getState().status).to.equal('LOADING_ERROR');
384+
});
385+
});
386+
});
387+
294388
it('sharding -> cancelling request -> not managed', async function () {
295389
let mockManagedNamespace = true;
296390
confirmationStub.resolves(true);

0 commit comments

Comments
 (0)