Skip to content

Commit 00c4b29

Browse files
committed
feat(devtools-connect): fail fast on specific error and codes from compass-web COMPASS-9793
1 parent 184e1d3 commit 00c4b29

File tree

3 files changed

+102
-8
lines changed

3 files changed

+102
-8
lines changed

packages/devtools-connect/src/fast-failure-connect.spec.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,71 @@ describe('isFastFailureConnectionError', function () {
4646
isFastFailureConnectionError(new Error('could not connect')),
4747
).to.equal(false);
4848
});
49+
50+
describe('isCompassSocketServiceError', function () {
51+
class CompassSocketServiceError extends Error {
52+
constructor(
53+
msg: string,
54+
public code: number,
55+
) {
56+
super(msg);
57+
this.name = 'CompassSocketServiceError';
58+
}
59+
}
60+
61+
it('returns true for UNAUTHORIZED (3000)', function () {
62+
const error = new CompassSocketServiceError('Unauthorized', 3000);
63+
expect(isFastFailureConnectionError(error)).to.equal(true);
64+
});
65+
66+
it('returns true for FORBIDDEN (3003)', function () {
67+
const error = new CompassSocketServiceError('Forbidden', 3003);
68+
expect(isFastFailureConnectionError(error)).to.equal(true);
69+
});
70+
71+
it('returns true for NOT_FOUND (4004)', function () {
72+
const error = new CompassSocketServiceError('Not found', 4004);
73+
expect(isFastFailureConnectionError(error)).to.equal(true);
74+
});
75+
76+
it('returns true for VIOLATED_POLICY (1008)', function () {
77+
const error = new CompassSocketServiceError('Violated policy', 1008);
78+
expect(isFastFailureConnectionError(error)).to.equal(true);
79+
});
80+
81+
it('returns true for DO_NOT_TRY_AGAIN (4101)', function () {
82+
const error = new CompassSocketServiceError('Do not try again', 4101);
83+
expect(isFastFailureConnectionError(error)).to.equal(true);
84+
});
85+
86+
it('returns false for CompassSocketServiceError with non-fail-fast code', function () {
87+
const error = new CompassSocketServiceError('Some other error', 9999);
88+
expect(isFastFailureConnectionError(error)).to.equal(false);
89+
});
90+
91+
it('returns true when CompassSocketServiceError is the cause of MongoNetworkError', function () {
92+
const cause = new CompassSocketServiceError('Unauthorized', 3000);
93+
const error = new MongoNetworkError('Connection failed');
94+
(error as any).cause = cause;
95+
expect(isFastFailureConnectionError(error)).to.equal(true);
96+
});
97+
98+
it('returns true when CompassSocketServiceError is nested deeply', function () {
99+
const cause = new CompassSocketServiceError('Forbidden', 3003);
100+
const wrappedError = new Error('Wrapped error');
101+
(wrappedError as any).cause = cause;
102+
const error = new MongoNetworkError('Connection failed');
103+
(error as any).cause = wrappedError;
104+
expect(isFastFailureConnectionError(error)).to.equal(true);
105+
});
106+
107+
it('returns true when CompassSocketServiceError is in an AggregateError', function () {
108+
const cause = new CompassSocketServiceError('Not found', 4004);
109+
const aggregateError = new AggregateError(
110+
[new Error('Other error'), cause],
111+
'Multiple errors',
112+
);
113+
expect(isFastFailureConnectionError(aggregateError)).to.equal(true);
114+
});
115+
});
49116
});

packages/devtools-connect/src/fast-failure-connect.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,48 @@
11
// It probably makes sense to put this into its own package/repository once
22
// other tools start using it.
33

4+
const NODE_SOCKET_NON_RETRY_CODES = [
5+
'ECONNREFUSED',
6+
'ENOTFOUND',
7+
'ENETUNREACH',
8+
'EINVAL',
9+
];
10+
const COMPASS_SOCKET_SERVICE_NON_RETRY_CODES = [
11+
3000, // UNAUTHORIZED
12+
3003, // FORBIDDEN
13+
4004, // NOT_FOUND
14+
1008, // VIOLATED_POLICY
15+
4101, // DO_NOT_TRY_AGAIN
16+
];
17+
18+
const isCompassSocketServiceError = handleNestedErrors(
19+
(error: Error & { code?: string | number }): boolean => {
20+
if (error.name === 'CompassSocketServiceError') {
21+
return (
22+
typeof error.code === 'number' &&
23+
COMPASS_SOCKET_SERVICE_NON_RETRY_CODES.includes(error.code)
24+
);
25+
}
26+
return false;
27+
},
28+
);
29+
430
function isFastFailureConnectionSingleError(
5-
error: Error & { code?: string },
31+
error: Error & { code?: string | number },
632
): boolean {
733
switch (error.name) {
834
case 'MongoNetworkError':
9-
return /\b(ECONNREFUSED|ENOTFOUND|ENETUNREACH|EINVAL)\b/.test(
10-
error.message,
11-
);
35+
return new RegExp(
36+
String.raw`\b(${NODE_SOCKET_NON_RETRY_CODES.join('|')})\b`,
37+
).test(error.message);
1238
case 'MongoError':
1339
return /The apiVersion parameter is required/.test(error.message);
1440
default:
1541
return (
16-
['ECONNREFUSED', 'ENOTFOUND', 'ENETUNREACH', 'EINVAL'].includes(
17-
error.code ?? '',
18-
) || isPotentialTLSCertificateError(error)
42+
(typeof error.code === 'string' &&
43+
NODE_SOCKET_NON_RETRY_CODES.includes(error.code)) ||
44+
isPotentialTLSCertificateError(error) ||
45+
isCompassSocketServiceError(error)
1946
);
2047
}
2148
}

packages/devtools-connect/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"extends": "@mongodb-js/tsconfig-devtools/tsconfig.common.json",
33
"compilerOptions": {
44
"target": "es2020",
5-
"lib": ["es2020", "DOM"],
5+
"lib": ["es2021", "DOM"],
66
"module": "commonjs",
77
"moduleResolution": "node"
88
}

0 commit comments

Comments
 (0)