From d85c4a88d827660d7c21ccac5d3b4ca65ea18082 Mon Sep 17 00:00:00 2001 From: Matt Broadstone Date: Wed, 13 Nov 2019 09:36:19 -0500 Subject: [PATCH] feat: support driver info for drivers wrapping the node driver This allows drivers that wrap the node driver (e.g. Mongoose) report information about themselves in the client metadata generated by the driver. --- lib/core/topologies/shared.js | 15 +++++++++++ lib/mongo_client.js | 10 +++++++ lib/operations/connect.js | 3 ++- test/unit/client_tests.js | 50 +++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 test/unit/client_tests.js diff --git a/lib/core/topologies/shared.js b/lib/core/topologies/shared.js index 0f62b5beb9..ab01b02105 100644 --- a/lib/core/topologies/shared.js +++ b/lib/core/topologies/shared.js @@ -56,6 +56,21 @@ function createClientInfo(options) { clientInfo.application = { name: appname }; } + // support optionally provided wrapping driver info + if (options.driverInfo) { + if (options.driverInfo.name) { + clientInfo.driver.name = `${clientInfo.driver.name}|${options.driverInfo.name}`; + } + + if (options.driverInfo.version) { + clientInfo.driver.version = `${clientInfo.driver.version}|${options.driverInfo.version}`; + } + + if (options.driverInfo.platform) { + clientInfo.platform = `${clientInfo.platform}|${options.driverInfo.platform}`; + } + } + return clientInfo; } diff --git a/lib/mongo_client.js b/lib/mongo_client.js index 703eda64a0..4e0250aa75 100644 --- a/lib/mongo_client.js +++ b/lib/mongo_client.js @@ -77,6 +77,15 @@ const CloseOperation = require('./operations/close'); * @property {string[]} [extraOptions.mongocryptdSpawnArgs] Command line arguments to use when auto-spawning a mongocryptd */ +/** + * Configuration options for drivers wrapping the node driver. + * + * @typedef {Object} DriverInfoOptions + * @property {string} [name] The name of the driver + * @property {string} [version] The version of the driver + * @property {string} [platform] Optional platform information + */ + /** * Creates a new MongoClient instance * @class @@ -143,6 +152,7 @@ const CloseOperation = require('./operations/close'); * @param {boolean} [options.useNewUrlParser=true] Determines whether or not to use the new url parser. Enables the new, spec-compliant, url parser shipped in the core driver. This url parser fixes a number of problems with the original parser, and aims to outright replace that parser in the near future. Defaults to true, and must be explicitly set to false to use the legacy url parser. * @param {boolean} [options.useUnifiedTopology] Enables the new unified topology layer * @param {AutoEncryptionOptions} [options.autoEncryption] Optionally enable client side auto encryption + * @param {DriverInfoOptions} [options.driverInfo] Allows a wrapping driver to amend the client metadata generated by the driver to include information about the wrapping driver * @param {MongoClient~connectCallback} [callback] The command result callback * @return {MongoClient} a MongoClient instance */ diff --git a/lib/operations/connect.js b/lib/operations/connect.js index e8f251875d..56695bc316 100644 --- a/lib/operations/connect.js +++ b/lib/operations/connect.js @@ -137,7 +137,8 @@ const validOptionNames = [ 'useUnifiedTopology', 'serverSelectionTimeoutMS', 'useRecoveryToken', - 'autoEncryption' + 'autoEncryption', + 'driverInfo' ]; const ignoreOptionNames = ['native_parser']; diff --git a/test/unit/client_tests.js b/test/unit/client_tests.js new file mode 100644 index 0000000000..672fe193c8 --- /dev/null +++ b/test/unit/client_tests.js @@ -0,0 +1,50 @@ +'use strict'; + +const expect = require('chai').expect; +const mock = require('mongodb-mock-server'); + +describe('Client (unit)', function() { + let server; + + afterEach(() => mock.cleanup()); + beforeEach(() => { + return mock.createServer().then(_server => (server = _server)); + }); + + it('should let wrapping libraries amend the client metadata', function() { + let handshake; + server.setMessageHandler(request => { + const doc = request.document; + if (doc.ismaster) { + handshake = doc; + request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + } else if (doc.endSessions) { + request.reply({ ok: 1 }); + } + }); + + const client = this.configuration.newClient(`mongodb://${server.uri()}/`, { + useUnifiedTopology: true, + driverInfo: { + name: 'mongoose', + version: '5.7.10', + platform: 'llama edition' + } + }); + + return client.connect().then(() => { + this.defer(() => client.close()); + + expect(handshake) + .nested.property('client.driver') + .to.deep.equal({ + name: 'nodejs|mongoose', + version: '3.3.4|5.7.10' + }); + + expect(handshake) + .nested.property('client.platform') + .to.match(/llama edition/); + }); + }); +});