From 54c456b4a4ff51c4f6734cff550d8aa53a47db15 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 19 Nov 2020 16:04:38 -0500 Subject: [PATCH] feat: Add MongoOptions interface (#2616) Add MongoOptions representing the internal combined view of options. NODE-2698 --- package-lock.json | 162 ++++++++++++++++++++--------- package.json | 10 +- src/connection_string.ts | 48 +++++++++ src/mongo_client.ts | 113 +++++++++++++++++++- test/functional/connection.test.js | 4 +- 5 files changed, 277 insertions(+), 60 deletions(-) diff --git a/package-lock.json b/package-lock.json index 075ffbfe9c..505e4a45b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -312,21 +312,22 @@ "dev": true }, "@microsoft/api-extractor": { - "version": "7.9.11", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.9.11.tgz", - "integrity": "sha512-t+LwGAuTjr+odFEl5xV3vl7qOWf84CM8BWKgb93kEnVd8uha3KfuWtDfnstxG4oC/TL6tu5+9rOwKJiNIidf2A==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.12.0.tgz", + "integrity": "sha512-YDd7AUkIayPLooMasDyV4vle1TLUQhFp2v/tGdRU+WAVbnyVUDXXa20WEfbPEZ4QVlgN+77EX6f2K6GyKd713A==", "dev": true, "requires": { - "@microsoft/api-extractor-model": "7.8.19", + "@microsoft/api-extractor-model": "7.12.0", "@microsoft/tsdoc": "0.12.19", - "@rushstack/node-core-library": "3.30.0", - "@rushstack/ts-command-line": "4.6.4", + "@rushstack/node-core-library": "3.35.1", + "@rushstack/rig-package": "0.2.8", + "@rushstack/ts-command-line": "4.7.7", "colors": "~1.2.1", "lodash": "~4.17.15", "resolve": "~1.17.0", "semver": "~7.3.0", "source-map": "~0.6.1", - "typescript": "~3.9.5" + "typescript": "~4.0.5" }, "dependencies": { "semver": { @@ -334,23 +335,17 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true - }, - "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", - "dev": true } } }, "@microsoft/api-extractor-model": { - "version": "7.8.19", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.8.19.tgz", - "integrity": "sha512-tEEPuww0Gbyw9LuTcJ7nDCTjb+aLSAox8Xl9/iSxNTv5yHJN1QX3cqajlC3ibDHlRz7oMpQfHZX7YpAygbgIvg==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.12.0.tgz", + "integrity": "sha512-TxoAbL/lauS3k/brBWVsiQTnyHBwHrAGJhTuiD0tWS/eu4dLNULchcSQfcOaFS91OgDEz4lMMbClgChFuo+53Q==", "dev": true, "requires": { "@microsoft/tsdoc": "0.12.19", - "@rushstack/node-core-library": "3.30.0" + "@rushstack/node-core-library": "3.35.1" } }, "@microsoft/tsdoc": { @@ -360,21 +355,21 @@ "dev": true }, "@microsoft/tsdoc-config": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.13.5.tgz", - "integrity": "sha512-KlnIdTRnPSsU9Coz9wzDAkT8JCLopP3ec1sgsgo7trwE6QLMKRpM4hZi2uzVX897SW49Q4f439auGBcQLnZQfA==", + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.13.6.tgz", + "integrity": "sha512-VJjV35PnrNISoX2WMemZjnCIdOUPTRpCz6pu8inISotLd3SgoDSJygGaE7+lOYdCtDl+4c8PWJdZivxxXgOnLw==", "dev": true, "requires": { - "@microsoft/tsdoc": "0.12.20", + "@microsoft/tsdoc": "0.12.21", "ajv": "~6.12.3", "jju": "~1.4.0", "resolve": "~1.12.0" }, "dependencies": { "@microsoft/tsdoc": { - "version": "0.12.20", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.20.tgz", - "integrity": "sha512-/b13m37QZYPV6nCOiqkFyvlQjlTNvAcQpgFZ6ZKIqtStJxNdqVo/frULubxMUMWi6p9Uo5f4BRlguv5ViFcL0A==", + "version": "0.12.21", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.21.tgz", + "integrity": "sha512-j+9OJ0A0buZZaUn6NxeHUVpoa05tY2PgVs7kXJhJQiKRB0G1zQqbJxer3T7jWtzpqQWP89OBDluyIeyTsMk8Sg==", "dev": true }, "resolve": { @@ -389,9 +384,9 @@ } }, "@rushstack/node-core-library": { - "version": "3.30.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.30.0.tgz", - "integrity": "sha512-vZo1fi/ObL3CmRXlQUX/E1xL9KL9arBfCJ7pYf3O/vFrD8ffSfpQ6+6lhgAsKrCIM5Epddsgeb2REPxMwYZX1g==", + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.35.1.tgz", + "integrity": "sha512-ZwnXp2loZyVUgrZ+fEKKF/EHl0ikcy6SCsd34ewYXoEAs0XWIy2VS9bemrfaFtd2VzJ/G/ZbP3xHkqRnUPKJ4Q==", "dev": true, "requires": { "@types/node": "10.17.13", @@ -419,10 +414,29 @@ } } }, + "@rushstack/rig-package": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.2.8.tgz", + "integrity": "sha512-Ltjeg1a5Sx7XTW9oBxmcfhHseBLnH7I/8d6tAtjx5s0r7F6WmNVJdxVmt86qNfXcFRsiGNrzLqjMwlcX3GyldQ==", + "dev": true, + "requires": { + "@types/node": "10.17.13", + "resolve": "~1.17.0", + "strip-json-comments": "~3.1.1" + }, + "dependencies": { + "@types/node": { + "version": "10.17.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", + "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", + "dev": true + } + } + }, "@rushstack/ts-command-line": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.6.4.tgz", - "integrity": "sha512-ubIANZimyU07+ChU56LfiD36NJ8gvw1txlvUP20GYNQi4lf5N0xEnev4r+AtKkOdnowpGy60ObGmYxSUpSacpw==", + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.7.tgz", + "integrity": "sha512-COSDys0WTVCORKam2hsTL32As4fHAf1RqC6FKS98hgR0Z90nh1JX8fGNkvSdxaZ6dOuNTJj3txh+SpWoHJoZJA==", "dev": true, "requires": { "@types/argparse": "1.0.38", @@ -2369,6 +2383,27 @@ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.20.tgz", "integrity": "sha512-/b13m37QZYPV6nCOiqkFyvlQjlTNvAcQpgFZ6ZKIqtStJxNdqVo/frULubxMUMWi6p9Uo5f4BRlguv5ViFcL0A==", "dev": true + }, + "@microsoft/tsdoc-config": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.13.5.tgz", + "integrity": "sha512-KlnIdTRnPSsU9Coz9wzDAkT8JCLopP3ec1sgsgo7trwE6QLMKRpM4hZi2uzVX897SW49Q4f439auGBcQLnZQfA==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.12.20", + "ajv": "~6.12.3", + "jju": "~1.4.0", + "resolve": "~1.12.0" + } + }, + "resolve": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.3.tgz", + "integrity": "sha512-hF6+hAPlxjqHWrw4p1rF3Wztbgxd4AjA5VlUzY5zcTb4J8D3JK4/1RjU48pHz2PJWzGVsLB1VWZkvJzhK2CCOA==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } } } }, @@ -3437,9 +3472,9 @@ "dev": true }, "highlight.js": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.2.tgz", - "integrity": "sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA==", + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.3.2.tgz", + "integrity": "sha512-3jRT7OUYsVsKvukNKZCtnvRcFyCJqSEIuIMsEybAXRiFSwpt65qjPd/Pr+UOdYt7WJlt+lj3+ypUsHiySBp/Jw==", "dev": true }, "hosted-git-info": { @@ -6483,6 +6518,12 @@ "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, + "spec-xunit-file": { + "version": "0.0.1-3", + "resolved": "https://registry.npmjs.org/spec-xunit-file/-/spec-xunit-file-0.0.1-3.tgz", + "integrity": "sha1-hVpmq4w4LrMWXfkoqB0HSQKdI4Y=", + "dev": true + }, "split": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", @@ -7124,21 +7165,22 @@ } }, "typedoc": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.18.0.tgz", - "integrity": "sha512-UgDQwapCGQCCdYhEQzQ+kGutmcedklilgUGf62Vw6RdI29u6FcfAXFQfRTiJEbf16aK3YnkB20ctQK1JusCRbA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.19.2.tgz", + "integrity": "sha512-oDEg1BLEzi1qvgdQXc658EYgJ5qJLVSeZ0hQ57Eq4JXy6Vj2VX4RVo18qYxRWz75ifAaYuYNBUCnbhjd37TfOg==", "dev": true, "requires": { "fs-extra": "^9.0.1", "handlebars": "^4.7.6", - "highlight.js": "^10.0.0", - "lodash": "^4.17.15", - "lunr": "^2.3.8", + "highlight.js": "^10.2.0", + "lodash": "^4.17.20", + "lunr": "^2.3.9", "marked": "^1.1.1", "minimatch": "^3.0.0", "progress": "^2.0.3", + "semver": "^7.3.2", "shelljs": "^0.8.4", - "typedoc-default-themes": "^0.10.2" + "typedoc-default-themes": "^0.11.4" }, "dependencies": { "fs-extra": { @@ -7154,19 +7196,39 @@ } }, "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } } }, "marked": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-1.1.1.tgz", - "integrity": "sha512-mJzT8D2yPxoPh7h0UXkB+dBj4FykPJ2OIfxAWeIHrvoHDkFxukV/29QxoFQoPM6RLEwhIFdJpmKBlqVM3s2ZIw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.4.tgz", + "integrity": "sha512-6x5TFGCTKSQBLTZtOburGxCxFEBJEGYVLwCMTBCxzvyuisGcC20UNzDSJhCr/cJ/Kmh6ulfJm10g6WWEAJ3kvg==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "typedoc-default-themes": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.11.4.tgz", + "integrity": "sha512-Y4Lf+qIb9NTydrexlazAM46SSLrmrQRqWiD52593g53SsmUFioAsMWt8m834J6qsp+7wHRjxCXSZeiiW5cMUdw==", "dev": true }, "universalify": { @@ -7197,9 +7259,9 @@ } }, "typescript": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", - "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", + "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", "dev": true }, "typescript-cached-transpile": { diff --git a/package.json b/package.json index 9ddf80835a..012b1ea0a9 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@microsoft/api-extractor": "^7.9.10", - "@microsoft/tsdoc-config": "^0.13.5", + "@microsoft/api-extractor": "^7.12.0", + "@microsoft/tsdoc-config": "^0.13.6", "@types/aws4": "^1.5.1", "@types/bl": "^2.1.0", "@types/bson": "^4.0.2", @@ -66,14 +66,14 @@ "sinon": "^4.3.0", "sinon-chai": "^3.2.0", "snappy": "^6.3.0", - "spec-xunit-file": "0.0.1-3", "source-map-support": "^0.5.19", + "spec-xunit-file": "0.0.1-3", "standard-version": "^7.1.0", "through2": "^3.0.1", "ts-node": "^9.0.0", - "typedoc": "^0.18.0", + "typedoc": "^0.19.2", "typedoc-plugin-pages": "^1.0.1", - "typescript": "^4.0.2", + "typescript": "^4.0.5", "typescript-cached-transpile": "^0.0.6", "worker-farm": "^1.5.0", "wtfnode": "^0.8.2", diff --git a/src/connection_string.ts b/src/connection_string.ts index 2e9d8bdd27..fa3ee8dd4d 100644 --- a/src/connection_string.ts +++ b/src/connection_string.ts @@ -1,12 +1,14 @@ import * as url from 'url'; import * as qs from 'querystring'; import * as dns from 'dns'; +import { URL } from 'url'; import { ReadPreference } from './read_preference'; import { MongoParseError } from './error'; import type { AnyOptions, Callback } from './utils'; import type { ConnectionOptions } from './cmap/connection'; import type { Document } from './bson'; import type { CompressorName } from './cmap/wire_protocol/compression'; +import type { MongoClientOptions, MongoOptions } from './mongo_client'; /** * The following regular expression validates a connection string and breaks the @@ -760,3 +762,49 @@ export function parseConnectionString( callback(undefined, result); } + +// NEW PARSER WORK... + +const HOSTS_REGEX = new RegExp( + '(?mongodb(?:\\+srv|)):\\/\\/(?:(?[^:]*)(?::(?[^@]*))?@)?(?[^\\/?]*)(?.*)' +); + +function parseURI(uri: string): { srv: boolean; url: URL; hosts: string[] } { + const match = uri.match(HOSTS_REGEX); + if (!match) { + throw new MongoParseError(`Invalid connection string ${uri}`); + } + + const protocol = match.groups?.protocol; + const username = match.groups?.username; + const password = match.groups?.password; + const hosts = match.groups?.hosts; + const rest = match.groups?.rest; + + if (!protocol || !hosts) { + throw new MongoParseError('Invalid connection string, protocol and host(s) required'); + } + + const authString = username ? (password ? `${username}:${password}` : `${username}`) : ''; + return { + srv: protocol.includes('srv'), + url: new URL(`${protocol.toLowerCase()}://${authString}@dummyHostname${rest}`), + hosts: hosts.split(',') + }; +} + +export function parseOptions( + uri: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + options: MongoClientOptions = {} +): Readonly { + try { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { srv, url, hosts } = parseURI(uri); + const mongoOptions: MongoOptions = ({ srv, hosts } as unknown) as MongoOptions; + // TODO(NODE-2699): option parse logic + return Object.freeze(mongoOptions); + } catch { + return Object.freeze({} as MongoOptions); + } +} diff --git a/src/mongo_client.ts b/src/mongo_client.ts index ad01cc1f04..88b4e6a976 100644 --- a/src/mongo_client.ts +++ b/src/mongo_client.ts @@ -18,6 +18,10 @@ import type { Topology } from './sdam/topology'; import type { ClientSession, ClientSessionOptions } from './sessions'; import type { OperationParent } from './operations/command'; import type { TagSet } from './sdam/server_description'; +import type { ConnectionOptions as TLSConnectionOptions } from 'tls'; +import type { TcpSocketConnectOpts as ConnectionOptions } from 'net'; +import type { MongoCredentials } from './cmap/auth/mongo_credentials'; +import { parseOptions } from './connection_string'; /** @public */ export enum LogLevel { @@ -80,7 +84,7 @@ export interface MongoURIOptions extends Pick, + Omit, + Omit, + Required< + Pick< + MongoClientOptions, + | 'autoEncryption' + | 'compression' + | 'compressors' + | 'connectTimeoutMS' + | 'dbName' + | 'directConnection' + | 'domainsEnabled' + | 'driverInfo' + | 'forceServerObjectId' + | 'gssapiServiceName' + | 'ha' + | 'haInterval' + | 'heartbeatFrequencyMS' + | 'keepAlive' + | 'keepAliveInitialDelay' + | 'localThresholdMS' + | 'logger' + | 'maxIdleTimeMS' + | 'maxPoolSize' + | 'minPoolSize' + | 'monitorCommands' + | 'noDelay' + | 'numberOfRetries' + | 'pkFactory' + | 'promiseLibrary' + | 'raw' + | 'reconnectInterval' + | 'reconnectTries' + | 'replicaSet' + | 'retryReads' + | 'retryWrites' + | 'serverSelectionTimeoutMS' + | 'serverSelectionTryOnce' + | 'socketTimeoutMS' + | 'tlsAllowInvalidCertificates' + | 'tlsAllowInvalidHostnames' + | 'tlsInsecure' + | 'waitQueueMultiple' + | 'waitQueueTimeoutMS' + | 'zlibCompressionLevel' + > + > { + hosts: { host: string; port: number }[]; + srv: boolean; + credentials: MongoCredentials; + readPreference: ReadPreference; + readConcern: ReadConcern; + writeConcern: WriteConcern; + + /** + * # NOTE ABOUT TLS Options + * + * If set TLS enabled, equivalent to setting the ssl option. + * + * ### Additional options: + * + * | nodejs option | MongoDB equivalent | type | + * |:---------------------|--------------------------------------------------------- |:---------------------------------------| + * | `ca` | `sslCA`, `tlsCAFile` | `string \| Buffer \| Buffer[]` | + * | `crl` | `sslCRL` | `string \| Buffer \| Buffer[]` | + * | `cert` | `sslCert`, `tlsCertificateFile`, `tlsCertificateKeyFile` | `string \| Buffer \| Buffer[]` | + * | `key` | `sslKey`, `tlsCertificateKeyFile` | `string \| Buffer \| KeyObject[]` | + * | `passphrase` | `sslPass`, `tlsCertificateKeyFilePassword` | `string` | + * | `rejectUnauthorized` | `sslValidate` | `boolean` | + * + */ + tls: boolean; + + /** + * Turn these options into a reusable options dictionary + */ + toJSON(): Record; + /** + * Turn these options into a reusable connection URI + */ + toURI(): string; +} diff --git a/test/functional/connection.test.js b/test/functional/connection.test.js index 403d5ef445..302e3a4532 100644 --- a/test/functional/connection.test.js +++ b/test/functional/connection.test.js @@ -39,8 +39,8 @@ describe('Connection', function () { ); client.connect(function (err, client) { - var db = client.db(configuration.db); expect(err).to.not.exist; + var db = client.db(configuration.db); db.collection('domainSocketCollection0').insert({ a: 1 }, { w: 1 }, function (err) { expect(err).to.not.exist; @@ -103,8 +103,8 @@ describe('Connection', function () { ); client.connect(function (err, client) { - var db = client.db(configuration.db); expect(err).to.not.exist; + var db = client.db(configuration.db); db.collection('domainSocketCollection1').insert({ x: 1 }, { w: 1 }, function (err) { expect(err).to.not.exist;