Skip to content

Commit 6f8a725

Browse files
authored
feat: add codeName to error output MONGOSH-1198 (#1809)
* feat: add codeName to error output MONGOSH-1198 * fix imports * test: add tests * refactor: remove interface * revert: enzyme types * test: update e2e test * revert: package changes * fix: add dependency * fix: add dependency * type
1 parent b6ccbbc commit 6f8a725

File tree

7 files changed

+139
-8
lines changed

7 files changed

+139
-8
lines changed

packages/browser-repl/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"@babel/plugin-proposal-class-properties": "^7.8.3",
7070
"@babel/preset-react": "^7.18.6",
7171
"@babel/preset-typescript": "^7.18.6",
72+
"mongodb": "^6.3.0",
7273
"@mongodb-js/compass-components": "*",
7374
"@mongodb-js/compass-editor": "*",
7475
"@mongodb-js/eslint-config-mongosh": "^1.0.0",
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React from 'react';
2+
import { expect } from '../../../testing/chai';
3+
import { render, mount } from '../../../testing/enzyme';
4+
5+
import { ErrorOutput } from './error-output';
6+
7+
describe('ErrorOutput', function () {
8+
class MongoError extends Error {
9+
code: number;
10+
codeName: string;
11+
errorInfo: string;
12+
constructor({
13+
message,
14+
code,
15+
codeName,
16+
name,
17+
errorInfo,
18+
}: {
19+
message: string;
20+
code: number;
21+
codeName: string;
22+
name: string;
23+
errorInfo: string;
24+
}) {
25+
super(message);
26+
this.code = code;
27+
this.codeName = codeName;
28+
this.name = name;
29+
this.errorInfo = errorInfo;
30+
}
31+
}
32+
33+
const mongoError = new MongoError({
34+
message: 'Something went wrong.',
35+
code: 123,
36+
codeName: 'ErrorCode',
37+
name: 'MongoError',
38+
errorInfo: 'More details about the error',
39+
});
40+
41+
describe('collapsed', function () {
42+
it('renders basic info - MongoError', function () {
43+
const wrapper = render(<ErrorOutput value={mongoError} />);
44+
45+
expect(wrapper.text()).to.contain(
46+
'MongoError[ErrorCode]: Something went wrong'
47+
);
48+
expect(wrapper.text()).not.to.contain('More details about the error');
49+
});
50+
51+
it('renders basic info - generic Error', function () {
52+
const error = new Error('Something went wrong.');
53+
const wrapper = render(<ErrorOutput value={error} />);
54+
55+
expect(wrapper.text()).to.contain('Something went wrong.');
56+
});
57+
});
58+
59+
describe('expanded', function () {
60+
it('renders basic info - generic Error', function () {
61+
const wrapper = mount(<ErrorOutput value={mongoError} />);
62+
63+
expect(wrapper.text()).to.contain('Something went wrong.');
64+
// wrapper.findWhere((node) => node.text().includes('Something went wrong')).simulate('click');
65+
wrapper.find('svg').simulate('click');
66+
67+
expect(wrapper.text()).to.contain(
68+
'MongoError[ErrorCode]: Something went wrong'
69+
);
70+
expect(wrapper.text()).not.to.contain('More details about the error');
71+
});
72+
});
73+
});

packages/browser-repl/src/components/types/error-output.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { isShouldReportAsBugError } from '@mongosh/errors';
44

55
import { SimpleTypeOutput } from './simple-type-output';
66
import { Expandable } from '../utils/expandable';
7+
import type { MongoServerError } from 'mongodb';
78

89
interface ErrorOutputProps {
910
value: any;
@@ -15,6 +16,8 @@ export class ErrorOutput extends Component<ErrorOutputProps> {
1516
};
1617

1718
renderCollapsed(toggle: () => void): JSX.Element {
19+
const { name, message, codeName } = this.props.value as MongoServerError;
20+
const formattedName = name + (codeName ? `[${codeName}]` : '');
1821
return (
1922
<div>
2023
<pre>
@@ -25,9 +28,9 @@ export class ErrorOutput extends Component<ErrorOutputProps> {
2528
toggle();
2629
}}
2730
>
28-
{this.props.value.name || 'Error'}:
31+
{formattedName || 'Error'}:
2932
</a>{' '}
30-
{this.props.value.message}
33+
{message}
3134
</pre>
3235
</div>
3336
);

packages/cli-repl/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"yargs-parser": "^20.2.4"
8888
},
8989
"devDependencies": {
90+
"mongodb": "^6.3.0",
9091
"@mongodb-js/eslint-config-mongosh": "^1.0.0",
9192
"@mongodb-js/prettier-config-devtools": "^1.0.1",
9293
"@mongodb-js/sbom-tools": "^0.5.2",

packages/cli-repl/src/format-output.spec.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ for (const colors of [false, true]) {
144144
});
145145

146146
context('when the result is an Error', function () {
147-
it('returns only name and message', function () {
147+
it('returns only name and message - generic Error', function () {
148148
const output = stripAnsiColors(
149149
format({
150150
value: new Error('Something went wrong.'),
@@ -155,6 +155,40 @@ for (const colors of [false, true]) {
155155
expect(output).to.equal('\rError: Something went wrong.');
156156
});
157157

158+
it('returns name, codeName and message - MongoError', function () {
159+
class MongoError extends Error {
160+
code: number;
161+
codeName: string;
162+
constructor(
163+
message: string,
164+
code: number,
165+
codeName: string,
166+
name: string
167+
) {
168+
super(message);
169+
this.code = code;
170+
this.codeName = codeName;
171+
this.name = name;
172+
}
173+
}
174+
175+
const output = stripAnsiColors(
176+
format({
177+
value: new MongoError(
178+
'Something went wrong.',
179+
123,
180+
'ErrorCode',
181+
'MongoError'
182+
),
183+
type: 'Error',
184+
})
185+
);
186+
187+
expect(output).to.equal(
188+
'\rMongoError[ErrorCode]: Something went wrong.'
189+
);
190+
});
191+
158192
it('provides errInfo information if present', function () {
159193
const err = Object.assign(new Error('Something went wrong.'), {
160194
errInfo: { commandThatFailed: 'doSomething' },

packages/cli-repl/src/format-output.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
CollectionNamesWithTypes,
1010
} from '@mongosh/shell-api';
1111
import { isShouldReportAsBugError } from '@mongosh/errors';
12+
import type { MongoServerError } from 'mongodb';
1213

1314
type EvaluationResult = {
1415
value: any;
@@ -249,7 +250,17 @@ function formatListCommands(
249250

250251
export function formatError(error: Error, options: FormatOptions): string {
251252
let result = '';
252-
if (error.name) result += `\r${clr(error.name, 'mongosh:error', options)}: `;
253+
if (error.name) {
254+
result += `\r${clr(error.name, 'mongosh:error', options)}`;
255+
const codeName = (error as MongoServerError).codeName;
256+
if (codeName)
257+
result += `${clr(
258+
`[` + (codeName as string) + `]`,
259+
'mongosh:error',
260+
options
261+
)}`;
262+
result += ': ';
263+
}
253264
if (error.message) result += error.message;
254265
if (isShouldReportAsBugError(error)) {
255266
result +=

packages/e2e-tests/test/e2e-direct.spec.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ describe('e2e direct connection', function () {
116116
await shell.waitForPrompt();
117117
await shell.executeLine('use admin');
118118
await shell.executeLine('db.runCommand({ listCollections: 1 })');
119-
shell.assertContainsError('MongoServerError: not primary');
119+
shell.assertContainsError(
120+
'MongoServerError[NotPrimaryNoSecondaryOk]: not primary'
121+
);
120122
});
121123

122124
it('lists collections when readPreference is in the connection string', async function () {
@@ -130,7 +132,9 @@ describe('e2e direct connection', function () {
130132
await shell.waitForPrompt();
131133
await shell.executeLine('use admin');
132134
await shell.executeLine('db.runCommand({ listCollections: 1 })');
133-
shell.assertContainsOutput('MongoServerError: not primary');
135+
shell.assertContainsOutput(
136+
'MongoServerError[NotPrimaryNoSecondaryOk]: not primary'
137+
);
134138
});
135139

136140
it('lists collections when readPreference is set via Mongo', async function () {
@@ -143,7 +147,9 @@ describe('e2e direct connection', function () {
143147
'db.getMongo().setReadPref("secondaryPreferred")'
144148
);
145149
await shell.executeLine('db.runCommand({ listCollections: 1 })');
146-
shell.assertContainsOutput('MongoServerError: not primary');
150+
shell.assertContainsOutput(
151+
'MongoServerError[NotPrimaryNoSecondaryOk]: not primary'
152+
);
147153
});
148154

149155
it('fails to list databases without explicit readPreference', async function () {
@@ -153,7 +159,9 @@ describe('e2e direct connection', function () {
153159
await shell.waitForPrompt();
154160
await shell.executeLine('use admin');
155161
await shell.executeLine('db.getMongo().getDBs()');
156-
shell.assertContainsError('MongoServerError: not primary');
162+
shell.assertContainsError(
163+
'MongoServerError[NotPrimaryNoSecondaryOk]: not primary'
164+
);
157165
});
158166

159167
it('lists databases when readPreference is in the connection string', async function () {

0 commit comments

Comments
 (0)