@@ -19,6 +19,7 @@ import {
19
19
ServerApi ,
20
20
ServerApiVersion
21
21
} from './mongo_client' ;
22
+ import type { OneOrMore } from './mongo_types' ;
22
23
import { PromiseProvider } from './promise_provider' ;
23
24
import { ReadConcern , ReadConcernLevel } from './read_concern' ;
24
25
import { ReadPreference , ReadPreferenceMode } from './read_preference' ;
@@ -28,6 +29,7 @@ import {
28
29
Callback ,
29
30
DEFAULT_PK_FACTORY ,
30
31
emitWarning ,
32
+ emitWarningOnce ,
31
33
HostAddress ,
32
34
isRecord ,
33
35
makeClientMetadata ,
@@ -184,8 +186,22 @@ const FALSEHOODS = new Set(['false', 'f', '0', 'n', 'no', '-1']);
184
186
function getBoolean ( name : string , value : unknown ) : boolean {
185
187
if ( typeof value === 'boolean' ) return value ;
186
188
const valueString = String ( value ) . toLowerCase ( ) ;
187
- if ( TRUTHS . has ( valueString ) ) return true ;
188
- if ( FALSEHOODS . has ( valueString ) ) return false ;
189
+ if ( TRUTHS . has ( valueString ) ) {
190
+ if ( valueString !== 'true' ) {
191
+ emitWarningOnce (
192
+ `deprecated value for ${ name } : ${ valueString } - please update to ${ name } : true instead`
193
+ ) ;
194
+ }
195
+ return true ;
196
+ }
197
+ if ( FALSEHOODS . has ( valueString ) ) {
198
+ if ( valueString !== 'false' ) {
199
+ emitWarningOnce (
200
+ `deprecated value for ${ name } : ${ valueString } - please update to ${ name } : false instead`
201
+ ) ;
202
+ }
203
+ return false ;
204
+ }
189
205
throw new MongoParseError ( `Expected ${ name } to be stringified boolean value, got: ${ value } ` ) ;
190
206
}
191
207
@@ -204,31 +220,24 @@ function getUint(name: string, value: unknown): number {
204
220
return parsedValue ;
205
221
}
206
222
207
- function toRecord ( value : string ) : Record < string , any > {
208
- const record = Object . create ( null ) ;
223
+ /** Wrap a single value in an array if the value is not an array */
224
+ function toArray < T > ( value : OneOrMore < T > ) : T [ ] {
225
+ return Array . isArray ( value ) ? value : [ value ] ;
226
+ }
227
+
228
+ function * entriesFromString ( value : string ) {
209
229
const keyValuePairs = value . split ( ',' ) ;
210
230
for ( const keyValue of keyValuePairs ) {
211
231
const [ key , value ] = keyValue . split ( ':' ) ;
212
232
if ( value == null ) {
213
233
throw new MongoParseError ( 'Cannot have undefined values in key value pairs' ) ;
214
234
}
215
- try {
216
- // try to get a boolean
217
- record [ key ] = getBoolean ( '' , value ) ;
218
- } catch {
219
- try {
220
- // try to get a number
221
- record [ key ] = getInt ( '' , value ) ;
222
- } catch {
223
- // keep value as a string
224
- record [ key ] = value ;
225
- }
226
- }
235
+
236
+ yield [ key , value ] ;
227
237
}
228
- return record ;
229
238
}
230
239
231
- class CaseInsensitiveMap extends Map < string , any > {
240
+ class CaseInsensitiveMap < Value = any > extends Map < string , Value > {
232
241
constructor ( entries : Array < [ string , any ] > = [ ] ) {
233
242
super ( entries . map ( ( [ k , v ] ) => [ k . toLowerCase ( ) , v ] ) ) ;
234
243
}
@@ -262,7 +271,7 @@ export function parseOptions(
262
271
const mongoOptions = Object . create ( null ) ;
263
272
mongoOptions . hosts = isSRV ? [ ] : hosts . map ( HostAddress . fromString ) ;
264
273
265
- const urlOptions = new CaseInsensitiveMap ( ) ;
274
+ const urlOptions = new CaseInsensitiveMap < any [ ] > ( ) ;
266
275
267
276
if ( url . pathname !== '/' && url . pathname !== '' ) {
268
277
const dbName = decodeURIComponent (
@@ -324,16 +333,11 @@ export function parseOptions(
324
333
] ) ;
325
334
326
335
for ( const key of allKeys ) {
327
- const values = [ ] ;
328
- if ( objectOptions . has ( key ) ) {
329
- values . push ( objectOptions . get ( key ) ) ;
330
- }
331
- if ( urlOptions . has ( key ) ) {
332
- values . push ( ...urlOptions . get ( key ) ) ;
333
- }
334
- if ( DEFAULT_OPTIONS . has ( key ) ) {
335
- values . push ( DEFAULT_OPTIONS . get ( key ) ) ;
336
- }
336
+ const values = [ objectOptions , urlOptions , DEFAULT_OPTIONS ] . flatMap ( optionsObject => {
337
+ const options = optionsObject . get ( key ) ?? [ ] ;
338
+ return toArray ( options ) ;
339
+ } ) ;
340
+
337
341
allOptions . set ( key , values ) ;
338
342
}
339
343
@@ -478,12 +482,11 @@ export function parseOptions(
478
482
throw new MongoParseError ( 'Can only specify both of proxy username/password or neither' ) ;
479
483
}
480
484
481
- if (
482
- urlOptions . get ( 'proxyHost' ) ?. length > 1 ||
483
- urlOptions . get ( 'proxyPort' ) ?. length > 1 ||
484
- urlOptions . get ( 'proxyUsername' ) ?. length > 1 ||
485
- urlOptions . get ( 'proxyPassword' ) ?. length > 1
486
- ) {
485
+ const proxyOptions = [ 'proxyHost' , 'proxyPort' , 'proxyUsername' , 'proxyPassword' ] . map (
486
+ key => urlOptions . get ( key ) ?? [ ]
487
+ ) ;
488
+
489
+ if ( proxyOptions . some ( options => options . length > 1 ) ) {
487
490
throw new MongoParseError (
488
491
'Proxy options cannot be specified multiple times in the connection string'
489
492
) ;
@@ -629,14 +632,26 @@ export const OPTIONS = {
629
632
} ,
630
633
authMechanismProperties : {
631
634
target : 'credentials' ,
632
- transform ( { options, values : [ value ] } ) : MongoCredentials {
633
- if ( typeof value === 'string' ) {
634
- value = toRecord ( value ) ;
635
+ transform ( { options, values : [ optionValue ] } ) : MongoCredentials {
636
+ if ( typeof optionValue === 'string' ) {
637
+ const mechanismProperties = Object . create ( null ) ;
638
+
639
+ for ( const [ key , value ] of entriesFromString ( optionValue ) ) {
640
+ try {
641
+ mechanismProperties [ key ] = getBoolean ( key , value ) ;
642
+ } catch {
643
+ mechanismProperties [ key ] = value ;
644
+ }
645
+ }
646
+
647
+ return MongoCredentials . merge ( options . credentials , {
648
+ mechanismProperties
649
+ } ) ;
635
650
}
636
- if ( ! isRecord ( value ) ) {
651
+ if ( ! isRecord ( optionValue ) ) {
637
652
throw new MongoParseError ( 'AuthMechanismProperties must be an object' ) ;
638
653
}
639
- return MongoCredentials . merge ( options . credentials , { mechanismProperties : value } ) ;
654
+ return MongoCredentials . merge ( options . credentials , { mechanismProperties : optionValue } ) ;
640
655
}
641
656
} ,
642
657
authSource : {
@@ -965,7 +980,7 @@ export const OPTIONS = {
965
980
for ( const tag of values ) {
966
981
const readPreferenceTag : TagSet = Object . create ( null ) ;
967
982
if ( typeof tag === 'string' ) {
968
- for ( const [ k , v ] of Object . entries ( toRecord ( tag ) ) ) {
983
+ for ( const [ k , v ] of entriesFromString ( tag ) ) {
969
984
readPreferenceTag [ k ] = v ;
970
985
}
971
986
}
0 commit comments