Skip to content

Commit ff2b975

Browse files
feat(NODE-4419): UUID class deserialization (#509)
1 parent f5dc9ed commit ff2b975

File tree

5 files changed

+121
-2
lines changed

5 files changed

+121
-2
lines changed

package-lock.json

+49
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"@typescript-eslint/eslint-plugin": "^5.30.0",
4242
"@typescript-eslint/parser": "^5.30.0",
4343
"array-includes": "^3.1.3",
44+
"array.prototype.flatmap": "^1.3.0",
4445
"benchmark": "^2.1.4",
4546
"chai": "^4.2.0",
4647
"eslint": "^8.18.0",

src/parser/deserializer.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export interface DeserializeOptions {
3535
promoteBuffers?: boolean;
3636
/** when deserializing will promote BSON values to their Node.js closest equivalent types. */
3737
promoteValues?: boolean;
38+
/** when deserializing will return UUID type, if promoteBuffers is also true then promoteUUIDs will take precedence and a buffer will not be returned */
39+
promoteUUIDs?: boolean;
3840
/** allow to specify if there what fields we wish to return as unserialized raw buffer. */
3941
fieldsAsRaw?: Document;
4042
/** return BSON regular expressions as BSONRegExp instances. */
@@ -135,6 +137,7 @@ function deserializeObject(
135137
const promoteBuffers = options['promoteBuffers'] == null ? false : options['promoteBuffers'];
136138
const promoteLongs = options['promoteLongs'] == null ? true : options['promoteLongs'];
137139
const promoteValues = options['promoteValues'] == null ? true : options['promoteValues'];
140+
const promoteUUIDs = options.promoteUUIDs == null ? false : options.promoteUUIDs;
138141

139142
// Ensures default validation option if none given
140143
const validation = options.validation == null ? { utf8: true } : options.validation;
@@ -413,7 +416,9 @@ function deserializeObject(
413416
throw new BSONError('Binary type with subtype 0x02 contains too short binary size');
414417
}
415418

416-
if (promoteBuffers && promoteValues) {
419+
if (promoteUUIDs && subType === 4) {
420+
value = new Binary(buffer.slice(index, index + binarySize), subType).toUUID();
421+
} else if (promoteBuffers && promoteValues) {
417422
value = buffer.slice(index, index + binarySize);
418423
} else {
419424
value = new Binary(buffer.slice(index, index + binarySize), subType);
@@ -440,7 +445,9 @@ function deserializeObject(
440445
_buffer[i] = buffer[index + i];
441446
}
442447

443-
if (promoteBuffers && promoteValues) {
448+
if (promoteUUIDs && subType === 4) {
449+
value = new Binary(_buffer, subType).toUUID();
450+
} else if (promoteBuffers && promoteValues) {
444451
value = _buffer;
445452
} else {
446453
value = new Binary(_buffer, subType);

test/node/uuid_tests.js

+61
Original file line numberDiff line numberDiff line change
@@ -191,4 +191,65 @@ describe('UUID', () => {
191191
expect(plainUUIDSerialization).to.deep.equal(toBinarySerialization);
192192
});
193193
});
194+
195+
describe('deserialize', () => {
196+
const originalUUID = new BSON.UUID();
197+
const binaryUUID = originalUUID.toBinary();
198+
const serializedUUID = BSON.serialize({ uuid: originalUUID.toBinary() });
199+
200+
it('should promoteUUIDs when flag is true', () => {
201+
const { uuid: promotedUUID } = BSON.deserialize(serializedUUID, { promoteUUIDs: true });
202+
expect(promotedUUID._bsontype).to.equal('UUID');
203+
expect(promotedUUID).to.deep.equal(originalUUID);
204+
});
205+
206+
it('should not promoteUUIDs when flag is false', () => {
207+
const { uuid: unpromotedUUID } = BSON.deserialize(serializedUUID, { promoteUUIDs: false });
208+
expect(unpromotedUUID._bsontype).to.equal('Binary');
209+
expect(unpromotedUUID).to.deep.equal(binaryUUID);
210+
});
211+
212+
it('should not promoteUUIDs when flag is omitted', () => {
213+
const { uuid: omittedFlagUUID } = BSON.deserialize(serializedUUID);
214+
expect(omittedFlagUUID._bsontype).to.equal('Binary');
215+
expect(omittedFlagUUID).to.deep.equal(binaryUUID);
216+
});
217+
218+
it('should throw BSONTypeError if _bsontype is not UUID and promoteUUIDs is true', () => {
219+
const binaryVar = new Binary(Buffer.from('abc'), BSON_BINARY_SUBTYPE_UUID_NEW);
220+
const serializedBinary = BSON.serialize({ d: binaryVar });
221+
expect(() => {
222+
BSON.deserialize(serializedBinary, { promoteUUIDs: true });
223+
}).to.throw(BSONTypeError);
224+
});
225+
226+
describe('promoteBuffers', () => {
227+
const promoteUUIDValues = [true, false, undefined];
228+
const promoteBufferValues = [true, false, undefined];
229+
230+
const testCases = promoteUUIDValues.flatMap(promoteUUIDs =>
231+
promoteBufferValues.flatMap(promoteBuffers => ({
232+
options: { promoteUUIDs, promoteBuffers },
233+
// promoteBuffers: true returns a Buffer so _bsontype does not exist
234+
outcome: promoteUUIDs ? 'UUID' : promoteBuffers ? undefined : 'Binary'
235+
}))
236+
);
237+
238+
for (const { options, outcome } of testCases) {
239+
it(`should deserialize to ${outcome} type when promoteUUIDs is ${options.promoteUUIDs} and promoteBuffers is ${options.promoteBuffers}`, () => {
240+
const { uuid } = BSON.deserialize(serializedUUID, options);
241+
expect(uuid._bsontype).to.equal(outcome);
242+
if (uuid._bsontype === 'UUID') {
243+
expect(uuid.id).to.deep.equal(originalUUID.id);
244+
} else if (uuid._bsontype === 'Binary') {
245+
expect(uuid.buffer).to.deep.equal(originalUUID.id);
246+
} else if (uuid._bsontype === undefined) {
247+
expect(uuid).to.deep.equal(originalUUID.id);
248+
} else {
249+
expect.fail('Unexpected _bsontype');
250+
}
251+
});
252+
}
253+
});
254+
});
194255
});

test/register-bson.js

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
require('chai/register-expect');
1111
require('array-includes/auto');
1212
require('object.entries/auto');
13+
require('array.prototype.flatmap/auto');
1314

1415
const BSON = require('../lib/bson');
1516
const { ensureBuffer } = require('../lib/ensure_buffer');

0 commit comments

Comments
 (0)