Skip to content

Commit

Permalink
fix(NODE-3290): backport versioned api validation and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
emadum committed Jun 30, 2021
1 parent 93a47fd commit 4867d92
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 4 deletions.
4 changes: 4 additions & 0 deletions lib/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ try {
} catch (err) {} // eslint-disable-line

module.exports = {
// Versioned API
ServerApiVersion: Object.freeze({
v1: '1'
}),
// Errors
MongoError: require('./error').MongoError,
MongoNetworkError: require('./error').MongoNetworkError,
Expand Down
25 changes: 25 additions & 0 deletions lib/mongo_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const Db = require('./db');
const EventEmitter = require('events').EventEmitter;
const inherits = require('util').inherits;
const MongoError = require('./core').MongoError;
const ServerApiVersion = require('./core').ServerApiVersion;
const deprecate = require('util').deprecate;
const WriteConcern = require('./write_concern');
const MongoDBNamespace = require('./utils').MongoDBNamespace;
Expand All @@ -14,6 +15,7 @@ const NativeTopology = require('./topologies/native_topology');
const connect = require('./operations/connect').connect;
const validOptions = require('./operations/connect').validOptions;


/**
* @fileOverview The **MongoClient** class is a class that allows for making Connections to MongoDB.
*
Expand Down Expand Up @@ -183,6 +185,8 @@ const validOptions = require('./operations/connect').validOptions;
* @property {function} [callback] The command result callback
*/



/**
* Creates a new MongoClient instance
* @constructor
Expand All @@ -197,6 +201,27 @@ function MongoClient(url, options) {

if (options && options.autoEncryption) require('./encrypter'); // Does CSFLE lib check

if (options.serverApi) {
const serverApiToValidate =
typeof options.serverApi === 'string' ? { version: options.serverApi } : options.serverApi;
const versionToValidate = serverApiToValidate && serverApiToValidate.version;
if (!versionToValidate) {
throw new MongoError(
`Invalid \`serverApi\` property; must specify a version from the following enum: ["${Object.values(
ServerApiVersion
).join('", "')}"]`
);
}
if (!Object.values(ServerApiVersion).some(v => v === versionToValidate)) {
throw new MongoError(
`Invalid server API version=${versionToValidate}; must be in the following enum: ["${Object.values(
ServerApiVersion
).join('", "')}"]`
);
}
options.serverApi = serverApiToValidate;
}

// The internal state
this.s = {
url: url,
Expand Down
71 changes: 67 additions & 4 deletions test/functional/versioned-api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,80 @@
const expect = require('chai').expect;
const loadSpecTests = require('../spec/index').loadSpecTests;
const runUnifiedTest = require('./unified-spec-runner/runner').runUnifiedTest;
const ServerApiVersion = require('../../lib/core').ServerApiVersion;

describe('Versioned API', function() {
it('should throw an error if serverApi version is provided via the uri with new parser', {
metadata: { topology: 'single' },
test: function(done) {
describe('client option validation', function() {
it('is supported as a client option when it is a valid ServerApiVersion string', function() {
const validVersions = Object.values(ServerApiVersion);
expect(validVersions.length).to.be.at.least(1);
for (const version of validVersions) {
const client = this.configuration.newClient('mongodb://localhost/', {
serverApi: version
});
expect(client.s.options)
.to.have.property('serverApi')
.deep.equal({ version });
}
});

it('is supported as a client option when it is an object with a valid version property', function() {
const validVersions = Object.values(ServerApiVersion);
expect(validVersions.length).to.be.at.least(1);
for (const version of validVersions) {
const client = this.configuration.newClient('mongodb://localhost/', {
serverApi: { version }
});
expect(client.s.options)
.to.have.property('serverApi')
.deep.equal({ version });
}
});

it('is not supported as a client option when it is an invalid string', function() {
expect(() =>
this.configuration.newClient('mongodb://localhost/', {
serverApi: 'bad'
})
).to.throw(/^Invalid server API version=bad;/);
});

it('is not supported as a client option when it is a number', function() {
expect(() =>
this.configuration.newClient('mongodb://localhost/', {
serverApi: 1
})
).to.throw(/^Invalid `serverApi` property;/);
});

it('is not supported as a client option when it is an object without a specified version', function() {
expect(() =>
this.configuration.newClient('mongodb://localhost/', {
serverApi: {}
})
).to.throw(/^Invalid `serverApi` property;/);
});

it('is not supported as a client option when it is an object with an invalid specified version', function() {
expect(() =>
this.configuration.newClient('mongodb://localhost/', {
serverApi: { version: 1 }
})
).to.throw(/^Invalid server API version=1;/);
expect(() =>
this.configuration.newClient('mongodb://localhost/', {
serverApi: { version: 'bad' }
})
).to.throw(/^Invalid server API version=bad;/);
});

it('is not supported as a URI option even when it is a valid ServerApiVersion string', function(done) {
const client = this.configuration.newClient({ serverApi: '1' }, { useNewUrlParser: true });
client.connect(err => {
expect(err).to.match(/URI cannot contain `serverApi`, it can only be passed to the client/);
client.close(done);
});
}
});
});

for (const versionedApiTest of loadSpecTests('versioned-api')) {
Expand Down

0 comments on commit 4867d92

Please sign in to comment.