Skip to content

Commit fdb828b

Browse files
author
Sophie Saskin
authored
fix(collection): only send bypassDocumentValidation if true
The bypassDocumentValidation key will only be set if it explicitly receives a true, otherwise it remains undefined. Fixes NODE-1492
1 parent b3ff3ed commit fdb828b

File tree

6 files changed

+260
-15
lines changed

6 files changed

+260
-15
lines changed

lib/apm.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class Instrumentation extends EventEmitter {
99
instrument(MongoClient, callback) {
1010
// store a reference to the original functions
1111
this.$MongoClient = MongoClient;
12-
const $prototypeConnect = this.$prototypeConnect = MongoClient.prototype.connect; // eslint-disable-line
12+
const $prototypeConnect = (this.$prototypeConnect = MongoClient.prototype.connect);
1313

1414
const instrumentation = this;
1515
MongoClient.prototype.connect = function(callback) {

lib/bulk/ordered.js

+8-5
Original file line numberDiff line numberDiff line change
@@ -300,14 +300,13 @@ function OrderedBulkOperation(topology, collection, options) {
300300
promiseLibrary: promiseLibrary,
301301
// Fundamental error
302302
err: null,
303-
// Bypass validation
304-
bypassDocumentValidation:
305-
typeof options.bypassDocumentValidation === 'boolean'
306-
? options.bypassDocumentValidation
307-
: false,
308303
// check keys
309304
checkKeys: typeof options.checkKeys === 'boolean' ? options.checkKeys : true
310305
};
306+
// bypass Validation
307+
if (options.bypassDocumentValidation === true) {
308+
this.s.bypassDocumentValidation = true;
309+
}
311310
}
312311

313312
OrderedBulkOperation.prototype.raw = function(op) {
@@ -490,6 +489,10 @@ var executeCommands = function(self, options, callback) {
490489
finalOptions.writeConcern = self.s.writeConcern;
491490
}
492491

492+
if (finalOptions.bypassDocumentValidation !== true) {
493+
delete finalOptions.bypassDocumentValidation;
494+
}
495+
493496
// Set an operationIf if provided
494497
if (self.operationId) {
495498
resultHandler.operationId = self.operationId;

lib/bulk/unordered.js

+8-5
Original file line numberDiff line numberDiff line change
@@ -309,14 +309,13 @@ var UnorderedBulkOperation = function(topology, collection, options) {
309309
collection: collection,
310310
// Promise Library
311311
promiseLibrary: promiseLibrary,
312-
// Bypass validation
313-
bypassDocumentValidation:
314-
typeof options.bypassDocumentValidation === 'boolean'
315-
? options.bypassDocumentValidation
316-
: false,
317312
// check keys
318313
checkKeys: typeof options.checkKeys === 'boolean' ? options.checkKeys : true
319314
};
315+
// bypass Validation
316+
if (options.bypassDocumentValidation === true) {
317+
this.s.bypassDocumentValidation = true;
318+
}
320319
};
321320

322321
/**
@@ -440,6 +439,10 @@ var executeBatch = function(self, batch, options, callback) {
440439
finalOptions.writeConcern = self.s.writeConcern;
441440
}
442441

442+
if (finalOptions.bypassDocumentValidation !== true) {
443+
delete finalOptions.bypassDocumentValidation;
444+
}
445+
443446
var resultHandler = function(err, result) {
444447
// Error is a driver related error not a bulk op error, terminate
445448
if ((err && err.driver) || (err && err.message)) {

lib/collection.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1720,7 +1720,7 @@ Collection.prototype.aggregate = function(pipeline, options, callback) {
17201720
decorateWithCollation(command, this, options);
17211721

17221722
// If we have bypassDocumentValidation set
1723-
if (typeof options.bypassDocumentValidation === 'boolean') {
1723+
if (options.bypassDocumentValidation === true) {
17241724
command.bypassDocumentValidation = options.bypassDocumentValidation;
17251725
}
17261726

lib/operations/collection_ops.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ function findAndModify(coll, query, sort, doc, options, callback) {
491491
}
492492

493493
// Have we specified bypassDocumentValidation
494-
if (typeof finalOptions.bypassDocumentValidation === 'boolean') {
494+
if (finalOptions.bypassDocumentValidation === true) {
495495
queryObject.bypassDocumentValidation = finalOptions.bypassDocumentValidation;
496496
}
497497

@@ -874,7 +874,7 @@ function mapReduce(coll, map, reduce, options, callback) {
874874
};
875875

876876
// Exclusion list
877-
const exclusionList = ['readPreference', 'session'];
877+
const exclusionList = ['readPreference', 'session', 'bypassDocumentValidation'];
878878

879879
// Add any other options passed in
880880
for (let n in options) {
@@ -909,7 +909,7 @@ function mapReduce(coll, map, reduce, options, callback) {
909909
}
910910

911911
// Is bypassDocumentValidation specified
912-
if (typeof options.bypassDocumentValidation === 'boolean') {
912+
if (options.bypassDocumentValidation === true) {
913913
mapCommandHash.bypassDocumentValidation = options.bypassDocumentValidation;
914914
}
915915

test/unit/bypass_validation_tests.js

+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
'use strict';
2+
3+
const MongoClient = require('../..').MongoClient;
4+
const expect = require('chai').expect;
5+
const mock = require('mongodb-mock-server');
6+
7+
describe('bypass document validation', function() {
8+
const test = {};
9+
beforeEach(() => {
10+
return mock.createServer().then(server => {
11+
test.server = server;
12+
});
13+
});
14+
afterEach(() => mock.cleanup());
15+
16+
// general test for aggregate function
17+
function testAggregate(config, done) {
18+
const client = new MongoClient(`mongodb://${test.server.uri()}/test`);
19+
let close = e => {
20+
close = () => {};
21+
client.close(() => done(e));
22+
};
23+
24+
test.server.setMessageHandler(request => {
25+
const doc = request.document;
26+
if (doc.aggregate) {
27+
try {
28+
expect(doc.bypassDocumentValidation).equal(config.expected);
29+
request.reply({
30+
ok: 1,
31+
cursor: {
32+
firstBatch: [{}],
33+
id: 23,
34+
ns: 'test.test'
35+
}
36+
});
37+
} catch (e) {
38+
close(e);
39+
}
40+
}
41+
42+
if (doc.ismaster) {
43+
request.reply(Object.assign({}, mock.DEFAULT_ISMASTER));
44+
} else if (doc.endSessions) {
45+
request.reply({ ok: 1 });
46+
}
47+
});
48+
49+
client.connect(function(err, client) {
50+
expect(err).to.not.exist;
51+
const db = client.db('test');
52+
const collection = db.collection('test_c');
53+
54+
const options = { bypassDocumentValidation: config.actual };
55+
56+
const pipeline = [
57+
{
58+
$project: {}
59+
}
60+
];
61+
collection.aggregate(pipeline, options).next(() => close());
62+
});
63+
}
64+
// aggregate
65+
it('should only set bypass document validation if strictly true in aggregate', function(done) {
66+
testAggregate({ expected: true, actual: true }, done);
67+
});
68+
69+
it('should not set bypass document validation if not strictly true in aggregate', function(done) {
70+
testAggregate({ expected: undefined, actual: false }, done);
71+
});
72+
73+
// general test for mapReduce function
74+
function testMapReduce(config, done) {
75+
const client = new MongoClient(`mongodb://${test.server.uri()}/test`);
76+
let close = e => {
77+
close = () => {};
78+
client.close(() => done(e));
79+
};
80+
81+
test.server.setMessageHandler(request => {
82+
const doc = request.document;
83+
if (doc.mapreduce) {
84+
try {
85+
expect(doc.bypassDocumentValidation).equal(config.expected);
86+
request.reply({
87+
results: 't',
88+
ok: 1
89+
});
90+
} catch (e) {
91+
close(e);
92+
}
93+
}
94+
95+
if (doc.ismaster) {
96+
request.reply(Object.assign({}, mock.DEFAULT_ISMASTER));
97+
} else if (doc.endSessions) {
98+
request.reply({ ok: 1 });
99+
}
100+
});
101+
102+
client.connect(function(err, client) {
103+
expect(err).to.not.exist;
104+
const db = client.db('test');
105+
const collection = db.collection('test_c');
106+
107+
const options = {
108+
out: 'test_c',
109+
bypassDocumentValidation: config.actual
110+
};
111+
112+
collection.mapReduce(function map() {}, function reduce() {}, options, e => {
113+
close(e);
114+
});
115+
});
116+
}
117+
// map reduce
118+
it('should only set bypass document validation if strictly true in mapReduce', function(done) {
119+
testMapReduce({ expected: true, actual: true }, done);
120+
});
121+
122+
it('should not set bypass document validation if not strictly true in mapReduce', function(done) {
123+
testMapReduce({ expected: undefined, actual: false }, done);
124+
});
125+
126+
// general test for findAndModify function
127+
function testFindAndModify(config, done) {
128+
const client = new MongoClient(`mongodb://${test.server.uri()}/test`);
129+
let close = e => {
130+
close = () => {};
131+
client.close(() => done(e));
132+
};
133+
134+
test.server.setMessageHandler(request => {
135+
const doc = request.document;
136+
if (doc.findAndModify) {
137+
try {
138+
expect(doc.bypassDocumentValidation).equal(config.expected);
139+
request.reply({
140+
ok: 1
141+
});
142+
} catch (e) {
143+
close(e);
144+
}
145+
}
146+
147+
if (doc.ismaster) {
148+
request.reply(Object.assign({}, mock.DEFAULT_ISMASTER));
149+
} else if (doc.endSessions) {
150+
request.reply({ ok: 1 });
151+
}
152+
});
153+
154+
client.connect(function(err, client) {
155+
expect(err).to.not.exist;
156+
const db = client.db('test');
157+
const collection = db.collection('test_c');
158+
159+
const options = { bypassDocumentValidation: config.actual };
160+
161+
collection.findAndModify(
162+
{ name: 'Andy' },
163+
{ rating: 1 },
164+
{ $inc: { score: 1 } },
165+
options,
166+
e => {
167+
close(e);
168+
}
169+
);
170+
});
171+
}
172+
// find and modify
173+
it('should only set bypass document validation if strictly true in findAndModify', function(done) {
174+
testFindAndModify({ expected: true, actual: true }, done);
175+
});
176+
177+
it('should not set bypass document validation if not strictly true in findAndModify', function(done) {
178+
testFindAndModify({ expected: undefined, actual: false }, done);
179+
});
180+
181+
// general test for BlukWrite to test changes made in ordered.js and unordered.js
182+
function testBulkWrite(config, done) {
183+
const client = new MongoClient(`mongodb://${test.server.uri()}/test`);
184+
let close = e => {
185+
close = () => {};
186+
client.close(() => done(e));
187+
};
188+
189+
test.server.setMessageHandler(request => {
190+
const doc = request.document;
191+
if (doc.insert) {
192+
try {
193+
expect(doc.bypassDocumentValidation).equal(config.expected);
194+
request.reply({
195+
ok: 1
196+
});
197+
} catch (e) {
198+
close(e);
199+
}
200+
}
201+
202+
if (doc.ismaster) {
203+
request.reply(Object.assign({}, mock.DEFAULT_ISMASTER));
204+
} else if (doc.endSessions) {
205+
request.reply({ ok: 1 });
206+
}
207+
});
208+
209+
client.connect(function(err, client) {
210+
expect(err).to.not.exist;
211+
const db = client.db('test');
212+
const collection = db.collection('test_c');
213+
214+
const options = {
215+
bypassDocumentValidation: config.actual,
216+
ordered: config.ordered
217+
};
218+
219+
collection.bulkWrite([{ insertOne: { document: { a: 1 } } }], options, () => close());
220+
});
221+
}
222+
// ordered bulk write, testing change in ordered.js
223+
it('should only set bypass document validation if strictly true in ordered bulkWrite', function(done) {
224+
testBulkWrite({ expected: true, actual: true, ordered: true }, done);
225+
});
226+
227+
it('should not set bypass document validation if not strictly true in ordered bulkWrite', function(done) {
228+
testBulkWrite({ expected: undefined, actual: false, ordered: true }, done);
229+
});
230+
231+
// unordered bulk write, testing change in ordered.js
232+
it('should only set bypass document validation if strictly true in unordered bulkWrite', function(done) {
233+
testBulkWrite({ expected: true, actual: true, ordered: false }, done);
234+
});
235+
236+
it('should not set bypass document validation if not strictly true in unordered bulkWrite', function(done) {
237+
testBulkWrite({ expected: undefined, actual: false, ordered: false }, done);
238+
});
239+
});

0 commit comments

Comments
 (0)