Skip to content

Commit e7f6e4e

Browse files
fix(NODE-5201): prevent warning when default value for deprecation option is used (#3646)
1 parent aa4f5c0 commit e7f6e4e

File tree

5 files changed

+93
-47
lines changed

5 files changed

+93
-47
lines changed

src/connection_string.ts

+36-36
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ export function parseOptions(
263263

264264
mongoOptions.hosts = isSRV ? [] : hosts.map(HostAddress.fromString);
265265

266-
const urlOptions = new CaseInsensitiveMap<any[]>();
266+
const urlOptions = new CaseInsensitiveMap<unknown[]>();
267267

268268
if (url.pathname !== '/' && url.pathname !== '') {
269269
const dbName = decodeURIComponent(
@@ -298,7 +298,7 @@ export function parseOptions(
298298
}
299299
}
300300

301-
const objectOptions = new CaseInsensitiveMap(
301+
const objectOptions = new CaseInsensitiveMap<unknown>(
302302
Object.entries(options).filter(([, v]) => v != null)
303303
);
304304

@@ -316,48 +316,42 @@ export function parseOptions(
316316

317317
// All option collection
318318

319-
const allOptions = new CaseInsensitiveMap();
319+
const allProvidedOptions = new CaseInsensitiveMap<unknown[]>();
320320

321-
const allKeys = new Set<string>([
322-
...urlOptions.keys(),
323-
...objectOptions.keys(),
324-
...DEFAULT_OPTIONS.keys()
325-
]);
321+
const allProvidedKeys = new Set<string>([...urlOptions.keys(), ...objectOptions.keys()]);
326322

327-
for (const key of allKeys) {
323+
for (const key of allProvidedKeys) {
328324
const values = [];
329325
const objectOptionValue = objectOptions.get(key);
330326
if (objectOptionValue != null) {
331327
values.push(objectOptionValue);
332328
}
333-
const urlValue = urlOptions.get(key);
334-
if (urlValue != null) {
335-
values.push(...urlValue);
336-
}
337-
const defaultOptionsValue = DEFAULT_OPTIONS.get(key);
338-
if (defaultOptionsValue != null) {
339-
values.push(defaultOptionsValue);
340-
}
341-
allOptions.set(key, values);
329+
330+
const urlValues = urlOptions.get(key) ?? [];
331+
values.push(...urlValues);
332+
allProvidedOptions.set(key, values);
342333
}
343334

344-
if (allOptions.has('tlsCertificateKeyFile') && !allOptions.has('tlsCertificateFile')) {
345-
allOptions.set('tlsCertificateFile', allOptions.get('tlsCertificateKeyFile'));
335+
if (
336+
allProvidedOptions.has('tlsCertificateKeyFile') &&
337+
!allProvidedOptions.has('tlsCertificateFile')
338+
) {
339+
allProvidedOptions.set('tlsCertificateFile', allProvidedOptions.get('tlsCertificateKeyFile'));
346340
}
347341

348-
if (allOptions.has('tls') || allOptions.has('ssl')) {
349-
const tlsAndSslOpts = (allOptions.get('tls') || [])
350-
.concat(allOptions.get('ssl') || [])
342+
if (allProvidedOptions.has('tls') || allProvidedOptions.has('ssl')) {
343+
const tlsAndSslOpts = (allProvidedOptions.get('tls') || [])
344+
.concat(allProvidedOptions.get('ssl') || [])
351345
.map(getBoolean.bind(null, 'tls/ssl'));
352346
if (new Set(tlsAndSslOpts).size !== 1) {
353347
throw new MongoParseError('All values of tls/ssl must be the same.');
354348
}
355349
}
356350

357-
checkTLSOptions(allOptions);
351+
checkTLSOptions(allProvidedOptions);
358352

359353
const unsupportedOptions = setDifference(
360-
allKeys,
354+
allProvidedKeys,
361355
Array.from(Object.keys(OPTIONS)).map(s => s.toLowerCase())
362356
);
363357
if (unsupportedOptions.size !== 0) {
@@ -371,9 +365,20 @@ export function parseOptions(
371365
// Option parsing and setting
372366

373367
for (const [key, descriptor] of Object.entries(OPTIONS)) {
374-
const values = allOptions.get(key);
375-
if (!values || values.length === 0) continue;
376-
setOption(mongoOptions, key, descriptor, values);
368+
const values = allProvidedOptions.get(key);
369+
if (!values || values.length === 0) {
370+
if (DEFAULT_OPTIONS.has(key)) {
371+
setOption(mongoOptions, key, descriptor, [DEFAULT_OPTIONS.get(key)]);
372+
}
373+
} else {
374+
const { deprecated } = descriptor;
375+
if (deprecated) {
376+
const deprecatedMsg = typeof deprecated === 'string' ? `: ${deprecated}` : '';
377+
emitWarning(`${key} is a deprecated option${deprecatedMsg}`);
378+
}
379+
380+
setOption(mongoOptions, key, descriptor, values);
381+
}
377382
}
378383

379384
if (mongoOptions.credentials) {
@@ -383,7 +388,7 @@ export function parseOptions(
383388
const isOidc = mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_OIDC;
384389
if (
385390
(isGssapi || isX509) &&
386-
allOptions.has('authSource') &&
391+
allProvidedOptions.has('authSource') &&
387392
mongoOptions.credentials.source !== '$external'
388393
) {
389394
// If authSource was explicitly given and its incorrect, we error
@@ -395,7 +400,7 @@ export function parseOptions(
395400
if (
396401
!(isGssapi || isX509 || isAws || isOidc) &&
397402
mongoOptions.dbName &&
398-
!allOptions.has('authSource')
403+
!allProvidedOptions.has('authSource')
399404
) {
400405
// inherit the dbName unless GSSAPI or X509, then silently ignore dbName
401406
// and there was no specific authSource given
@@ -571,14 +576,9 @@ function setOption(
571576
descriptor: OptionDescriptor,
572577
values: unknown[]
573578
) {
574-
const { target, type, transform, deprecated } = descriptor;
579+
const { target, type, transform } = descriptor;
575580
const name = target ?? key;
576581

577-
if (deprecated) {
578-
const deprecatedMsg = typeof deprecated === 'string' ? `: ${deprecated}` : '';
579-
emitWarning(`${key} is a deprecated option${deprecatedMsg}`);
580-
}
581-
582582
switch (type) {
583583
case 'boolean':
584584
mongoOptions[name] = getBoolean(name, values[0]);

test/integration/mongodb-handshake/mongodb-handshake.test.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from 'chai';
2-
import * as sinon from 'sinon';
2+
import Sinon, * as sinon from 'sinon';
33

44
import {
55
Connection,
@@ -11,6 +11,8 @@ import {
1111
describe('MongoDB Handshake', () => {
1212
let client;
1313

14+
afterEach(() => client.close());
15+
1416
context('when hello is too large', () => {
1517
before(() => {
1618
sinon.stub(Connection.prototype, 'command').callsFake(function (ns, cmd, options, callback) {
@@ -42,4 +44,21 @@ describe('MongoDB Handshake', () => {
4244
expect(error).to.match(/client metadata document must be less/);
4345
});
4446
});
47+
48+
context('when compressors are provided on the mongo client', () => {
49+
let spy: Sinon.SinonSpy;
50+
before(() => {
51+
spy = sinon.spy(Connection.prototype, 'command');
52+
});
53+
54+
after(() => sinon.restore());
55+
56+
it('constructs a handshake with the specified compressors', async function () {
57+
client = this.configuration.newClient({ compressors: ['snappy'] });
58+
await client.connect();
59+
expect(spy.called).to.be.true;
60+
const handshakeDoc = spy.getCall(0).args[1];
61+
expect(handshakeDoc).to.have.property('compression').to.deep.equal(['snappy']);
62+
});
63+
});
4564
});

test/tools/uri_spec_runner.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ export function executeUriValidationTest(
336336
case 'compressors':
337337
expect(options, `${errorMessage} ${optionKey}`)
338338
.to.have.property(optionKey)
339-
.deep.equal(optionValue.concat('none')); // TODO(NODE-3923): remove unnecessary appending
339+
.deep.equal(optionValue);
340340
break;
341341
case 'replicaset': // replicaset appears with both casings in the test expectations
342342
expect(options, `${errorMessage} replicaSet`)

test/unit/connection_string.test.ts

+33
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
MongoOptions,
1515
MongoParseError,
1616
MongoRuntimeError,
17+
OPTIONS,
1718
parseOptions,
1819
resolveSRVRecord
1920
} from '../mongodb';
@@ -642,4 +643,36 @@ describe('Connection String', function () {
642643
]);
643644
});
644645
});
646+
647+
context('default deprecated values', () => {
648+
afterEach(() => sinon.restore());
649+
before('ensure that `keepAlive` is deprecated', () => {
650+
const { deprecated } = OPTIONS.keepAlive;
651+
expect(deprecated).to.exist;
652+
});
653+
context('when no value is provided', () => {
654+
it('uses the default value', () => {
655+
const options = parseOptions('mongodb://localhost:27017');
656+
expect(options).to.have.property('keepAlive', true);
657+
});
658+
it('does not emit a deprecation warning', async () => {
659+
const spy = sinon.spy(process, 'emitWarning');
660+
parseOptions('mongodb://localhost:27017');
661+
expect(spy.called).to.be.false;
662+
});
663+
});
664+
665+
context('when a value is provided', () => {
666+
it('uses the provided value', () => {
667+
const options = parseOptions('mongodb://localhost:27017?keepAlive=false');
668+
expect(options).to.have.property('keepAlive', false);
669+
});
670+
it('emits a deprecation warning', async () => {
671+
const spy = sinon.spy(process, 'emitWarning');
672+
parseOptions('mongodb://localhost:27017?keepAlive=false');
673+
expect(spy.called, 'expected a warning to be emitted, but none was').to.be.true;
674+
expect(spy.getCalls()[0].args[0]).to.match(/keepAlive is a deprecated option/);
675+
});
676+
});
677+
});
645678
});

test/unit/mongo_client.test.js

+3-9
Original file line numberDiff line numberDiff line change
@@ -488,22 +488,16 @@ describe('MongoOptions', function () {
488488
const clientViaOpt = new MongoClient('mongodb://localhost', {
489489
compressors: ['zlib', 'snappy']
490490
});
491-
expect(clientViaOpt.options)
492-
.to.have.property('compressors')
493-
.deep.equal(['zlib', 'snappy', 'none']);
491+
expect(clientViaOpt.options).to.have.property('compressors').deep.equal(['zlib', 'snappy']);
494492
});
495493

496494
it('can be set when passed in as a comma-delimited string in the options object or URI', function () {
497495
const clientViaOpt = new MongoClient('mongodb://localhost', {
498496
compressors: 'zlib,snappy'
499497
});
500498
const clientViaUri = new MongoClient('mongodb://localhost?compressors=zlib,snappy');
501-
expect(clientViaOpt.options)
502-
.to.have.property('compressors')
503-
.deep.equal(['zlib', 'snappy', 'none']);
504-
expect(clientViaUri.options)
505-
.to.have.property('compressors')
506-
.deep.equal(['zlib', 'snappy', 'none']);
499+
expect(clientViaOpt.options).to.have.property('compressors').deep.equal(['zlib', 'snappy']);
500+
expect(clientViaUri.options).to.have.property('compressors').deep.equal(['zlib', 'snappy']);
507501
});
508502

509503
it('should validate that a string or an array of strings is provided as input', function () {

0 commit comments

Comments
 (0)