Skip to content

Commit 7ae6eac

Browse files
fix(NODE-6067): make topology descriptions JSON stringifiable (#4076)
Co-authored-by: Aditi Khare <[email protected]>
1 parent 63ba4ac commit 7ae6eac

File tree

3 files changed

+81
-11
lines changed

3 files changed

+81
-11
lines changed

src/sdam/topology_description.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import type { ObjectId } from '../bson';
1+
import { EJSON } from 'bson';
2+
3+
import { type ObjectId } from '../bson';
24
import * as WIRE_CONSTANTS from '../cmap/wire_protocol/constants';
35
import { MongoRuntimeError, type MongoServerError } from '../error';
46
import { compareObjectId, shuffle } from '../utils';
@@ -342,6 +344,16 @@ export class TopologyDescription {
342344
hasServer(address: string): boolean {
343345
return this.servers.has(address);
344346
}
347+
348+
/**
349+
* Returns a JSON-serializable representation of the TopologyDescription. This is primarily
350+
* intended for use with JSON.stringify().
351+
*
352+
* This method will not throw.
353+
*/
354+
toJSON() {
355+
return EJSON.serialize(this);
356+
}
345357
}
346358

347359
function topologyTypeForServerType(serverType: ServerType): TopologyType {

test/integration/node-specific/errors.ts

+34-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect } from 'chai';
22

3-
import { MongoClient, MongoError, MongoServerSelectionError } from '../../mongodb';
3+
import { MongoClient, MongoError, MongoServerSelectionError, ReadPreference } from '../../mongodb';
44

55
describe('Error (Integration)', function () {
66
describe('AggregateErrors', function () {
@@ -51,4 +51,37 @@ describe('Error (Integration)', function () {
5151
expect(error).to.be.instanceOf(MongoServerSelectionError);
5252
expect(error.message).not.to.be.empty;
5353
});
54+
55+
context('when a server selection error is stringified', function () {
56+
it(
57+
'the error"s topology description correctly displays the `servers`',
58+
{ requires: { topology: 'replicaset' } },
59+
async function () {
60+
const client: MongoClient = this.configuration.newClient({
61+
serverSelectionTimeoutMS: 1000
62+
});
63+
try {
64+
await client.connect();
65+
66+
const error = await client
67+
.db('foo')
68+
.collection('bar')
69+
.find(
70+
{},
71+
{
72+
// Use meaningless read preference tags to ensure that the server selection fails
73+
readPreference: new ReadPreference('secondary', [{ ny: 'ny' }])
74+
}
75+
)
76+
.toArray()
77+
.catch(e => JSON.parse(JSON.stringify(e)));
78+
79+
const servers = error.reason.servers;
80+
expect(Object.keys(servers).length > 0).to.be.true;
81+
} finally {
82+
await client.close();
83+
}
84+
}
85+
);
86+
});
5487
});

test/integration/server-discovery-and-monitoring/topology_description.test.ts

+34-9
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,22 @@ describe('TopologyDescription (integration tests)', function () {
1414
await client.close();
1515
});
1616

17+
beforeEach(async function () {
18+
client = this.configuration.newClient();
19+
await client.connect();
20+
});
21+
1722
context('options', function () {
23+
let client: MongoClient;
24+
25+
afterEach(async function () {
26+
await client.close();
27+
});
28+
29+
beforeEach(async function () {
30+
client = this.configuration.newClient();
31+
});
32+
1833
context('localThresholdMS', function () {
1934
it('should default to 15ms', async function () {
2035
const options: MongoClientOptions = {};
@@ -35,15 +50,6 @@ describe('TopologyDescription (integration tests)', function () {
3550
});
3651

3752
context('topology types', function () {
38-
let client: MongoClient;
39-
beforeEach(async function () {
40-
client = this.configuration.newClient();
41-
});
42-
43-
afterEach(async function () {
44-
await client.close();
45-
});
46-
4753
const topologyTypesMap = new Map<TopologyTypeRequirement, TopologyType>([
4854
['single', TopologyType.Single],
4955
['replicaset', TopologyType.ReplicaSetWithPrimary],
@@ -65,4 +71,23 @@ describe('TopologyDescription (integration tests)', function () {
6571
);
6672
}
6773
});
74+
75+
describe('json stringification', function () {
76+
it('can be stringified without error', function () {
77+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain
78+
const description = client.topology?.description!;
79+
expect(description).to.exist;
80+
81+
expect(() => JSON.stringify(description)).not.to.throw;
82+
});
83+
84+
it('properly stringifies the server description map', function () {
85+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain
86+
const description = client.topology?.description!;
87+
expect(description).to.exist;
88+
89+
const { servers } = JSON.parse(JSON.stringify(description));
90+
expect(Object.keys(servers).length > 0, '`servers` stringified with no servers.').to.be.true;
91+
});
92+
});
6893
});

0 commit comments

Comments
 (0)