Skip to content

Commit 6a860a3

Browse files
authored
fix(findOneAndReplace): throw error if atomic operators provided for findOneAndReplace
Fixes NODE-1973
1 parent 9eb7fe7 commit 6a860a3

File tree

3 files changed

+59
-0
lines changed

3 files changed

+59
-0
lines changed

lib/collection.js

+7
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,13 @@ Collection.prototype.findOneAndReplace = function(filter, replacement, options,
15151515
if (replacement == null || typeof replacement !== 'object')
15161516
throw toError('replacement parameter must be an object');
15171517

1518+
// Check that there are no atomic operators
1519+
const keys = Object.keys(replacement);
1520+
1521+
if (keys[0] && keys[0][0] === '$') {
1522+
throw toError('The replacement document must not contain atomic operators.');
1523+
}
1524+
15181525
return executeOperation(this.s.topology, findOneAndReplace, [
15191526
this,
15201527
filter,

test/functional/collection_tests.js

+20
Original file line numberDiff line numberDiff line change
@@ -1898,4 +1898,24 @@ describe('Collection', function() {
18981898
}
18991899
});
19001900
});
1901+
1902+
it('should allow an empty replacement document for findOneAndReplace', function() {
1903+
const configuration = this.configuration;
1904+
const client = configuration.newClient({}, { w: 1 });
1905+
1906+
client.connect((err, client) => {
1907+
expect(err).to.be.null;
1908+
1909+
const db = client.db(configuration.db);
1910+
const collection = db.collection('find_one_and_replace');
1911+
1912+
collection.insertOne({ a: 1 }, err => {
1913+
expect(err).to.be.null;
1914+
1915+
expect(collection.findOneAndReplace.bind(collection, { a: 1 }, {})).to.not.throw();
1916+
});
1917+
1918+
client.close();
1919+
});
1920+
});
19011921
});

test/unit/collection_tests.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict';
2+
3+
const EventEmitter = require('events');
4+
const chai = require('chai');
5+
const expect = chai.expect;
6+
const Db = require('../../lib/db');
7+
8+
class MockTopology extends EventEmitter {
9+
constructor() {
10+
super();
11+
}
12+
13+
capabilities() {
14+
return {};
15+
}
16+
}
17+
18+
describe('Collection', function() {
19+
/**
20+
* @ignore
21+
*/
22+
it('should not allow atomic operators for findOneAndReplace', {
23+
metadata: { requires: { topology: 'single' } },
24+
test: function() {
25+
const db = new Db('fakeDb', new MockTopology());
26+
const collection = db.collection('test');
27+
expect(() => {
28+
collection.findOneAndReplace({ a: 1 }, { $set: { a: 14 } });
29+
}).to.throw('The replacement document must not contain atomic operators.');
30+
}
31+
});
32+
});

0 commit comments

Comments
 (0)