Skip to content

Commit 1dc7eae

Browse files
feat(NODE-4535): automatically promote UUIDs when deserializing and parsing UUIDs (#513)
1 parent e9afa9d commit 1dc7eae

File tree

6 files changed

+27
-218
lines changed

6 files changed

+27
-218
lines changed

src/binary.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ export class Binary {
278278
if (!data) {
279279
throw new BSONTypeError(`Unexpected Binary Extended JSON format ${JSON.stringify(doc)}`);
280280
}
281-
return new Binary(data, type);
281+
return type === BSON_BINARY_SUBTYPE_UUID_NEW ? new UUID(data) : new Binary(data, type);
282282
}
283283

284284
/** @internal */

src/parser/deserializer.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ 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;
4038
/** allow to specify if there what fields we wish to return as unserialized raw buffer. */
4139
fieldsAsRaw?: Document;
4240
/** return BSON regular expressions as BSONRegExp instances. */
@@ -137,7 +135,6 @@ function deserializeObject(
137135
const promoteBuffers = options['promoteBuffers'] == null ? false : options['promoteBuffers'];
138136
const promoteLongs = options['promoteLongs'] == null ? true : options['promoteLongs'];
139137
const promoteValues = options['promoteValues'] == null ? true : options['promoteValues'];
140-
const promoteUUIDs = options.promoteUUIDs == null ? false : options.promoteUUIDs;
141138

142139
// Ensures default validation option if none given
143140
const validation = options.validation == null ? { utf8: true } : options.validation;
@@ -416,12 +413,13 @@ function deserializeObject(
416413
throw new BSONError('Binary type with subtype 0x02 contains too short binary size');
417414
}
418415

419-
if (promoteUUIDs && subType === 4) {
420-
value = new Binary(buffer.slice(index, index + binarySize), subType).toUUID();
421-
} else if (promoteBuffers && promoteValues) {
416+
if (promoteBuffers && promoteValues) {
422417
value = buffer.slice(index, index + binarySize);
423418
} else {
424419
value = new Binary(buffer.slice(index, index + binarySize), subType);
420+
if (subType === constants.BSON_BINARY_SUBTYPE_UUID_NEW) {
421+
value = value.toUUID();
422+
}
425423
}
426424
} else {
427425
const _buffer = Buffer.alloc(binarySize);
@@ -445,12 +443,12 @@ function deserializeObject(
445443
_buffer[i] = buffer[index + i];
446444
}
447445

448-
if (promoteUUIDs && subType === 4) {
449-
value = new Binary(_buffer, subType).toUUID();
450-
} else if (promoteBuffers && promoteValues) {
446+
if (promoteBuffers && promoteValues) {
451447
value = _buffer;
448+
} else if (subType === constants.BSON_BINARY_SUBTYPE_UUID_NEW) {
449+
value = new Binary(buffer.slice(index, index + binarySize), subType).toUUID();
452450
} else {
453-
value = new Binary(_buffer, subType);
451+
value = new Binary(buffer.slice(index, index + binarySize), subType);
454452
}
455453
}
456454

test/node/bson_compliance_test.js

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,6 @@
22

33
const Buffer = require('buffer').Buffer;
44
const BSON = require('../register-bson');
5-
const Code = BSON.Code;
6-
const Binary = BSON.Binary;
7-
const Timestamp = BSON.Timestamp;
8-
const Long = BSON.Long;
9-
const ObjectId = BSON.ObjectId;
10-
const DBRef = BSON.DBRef;
11-
const MinKey = BSON.MinKey;
12-
const MaxKey = BSON.MaxKey;
135

146
describe('BSON Compliance', function () {
157
/**
@@ -36,77 +28,4 @@ describe('BSON Compliance', function () {
3628

3729
done();
3830
});
39-
40-
/**
41-
* @ignore
42-
*/
43-
it('Pass all valid BSON serialization scenarios ./compliance/valid.json', function (done) {
44-
// Read and parse the json file
45-
const scenarios = require('./compliance/valid');
46-
47-
// Translate extended json to correctly typed doc
48-
function translate(doc, object) {
49-
for (let name in doc) {
50-
if (
51-
typeof doc[name] === 'number' ||
52-
typeof doc[name] === 'string' ||
53-
typeof doc[name] === 'boolean'
54-
) {
55-
object[name] = doc[name];
56-
} else if (Array.isArray(doc[name])) {
57-
object[name] = translate(doc[name], []);
58-
} else if (doc[name]['$numberLong']) {
59-
object[name] = Long.fromString(doc[name]['$numberLong']);
60-
} else if (doc[name]['$undefined']) {
61-
object[name] = null;
62-
} else if (doc[name]['$date']) {
63-
const date = new Date();
64-
date.setTime(parseInt(doc[name]['$date']['$numberLong'], 10));
65-
object[name] = date;
66-
} else if (doc[name]['$regexp']) {
67-
object[name] = new RegExp(doc[name]['$regexp'], doc[name]['$options'] || '');
68-
} else if (doc[name]['$oid']) {
69-
object[name] = new ObjectId(doc[name]['$oid']);
70-
} else if (doc[name]['$binary']) {
71-
object[name] = new Binary(doc[name]['$binary'], doc[name]['$type'] || 1);
72-
} else if (doc[name]['$timestamp']) {
73-
object[name] = Timestamp.fromBits(
74-
parseInt(doc[name]['$timestamp']['t'], 10),
75-
parseInt(doc[name]['$timestamp']['i'])
76-
);
77-
} else if (doc[name]['$ref']) {
78-
object[name] = new DBRef(doc[name]['$ref'], doc[name]['$id'], doc[name]['$db']);
79-
} else if (doc[name]['$minKey']) {
80-
object[name] = new MinKey();
81-
} else if (doc[name]['$maxKey']) {
82-
object[name] = new MaxKey();
83-
} else if (doc[name]['$code']) {
84-
object[name] = new Code(doc[name]['$code'], doc[name]['$scope'] || {});
85-
} else if (doc[name] != null && typeof doc[name] === 'object') {
86-
object[name] = translate(doc[name], {});
87-
}
88-
}
89-
90-
return object;
91-
}
92-
93-
// Iterate over all the results
94-
scenarios.documents.forEach(function (doc) {
95-
if (doc.skip) return;
96-
// Create a buffer containing the payload
97-
const expectedData = Buffer.from(doc.encoded, 'hex');
98-
// Get the expectedDocument
99-
const expectedDocument = translate(doc.document, {});
100-
// Serialize to buffer
101-
const buffer = BSON.serialize(expectedDocument);
102-
// Validate the output
103-
expect(expectedData.toString('hex')).to.equal(buffer.toString('hex'));
104-
// Attempt to deserialize
105-
const object = BSON.deserialize(buffer, { promoteLongs: false });
106-
// // Validate the object
107-
expect(JSON.stringify(expectedDocument)).to.deep.equal(JSON.stringify(object));
108-
});
109-
110-
done();
111-
});
11231
});

test/node/compliance/valid.js

Lines changed: 0 additions & 69 deletions
This file was deleted.

test/node/extended_json_tests.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,5 +773,15 @@ Converting circular structure to EJSON:
773773
);
774774
expect(parsedUndashedInput).to.deep.equal(parsedDashedInput);
775775
});
776+
777+
it('should return UUID object when deserializing UUID subtype', () => {
778+
const exampleUUID = new BSON.UUID('878dac12-01cc-4830-b271-cbc8518e63ad');
779+
const stringifiedUUID = EJSON.stringify({ uuid: exampleUUID });
780+
const parsedUUID = EJSON.parse(stringifiedUUID);
781+
const expectedResult = {
782+
uuid: new UUID('878dac12-01cc-4830-b271-cbc8518e63ad')
783+
};
784+
expect(parsedUUID).to.deep.equal(expectedResult);
785+
});
776786
});
777787
});

test/node/uuid_tests.js

Lines changed: 8 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -193,63 +193,14 @@ describe('UUID', () => {
193193
});
194194

195195
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-
}
196+
it('should return UUID object when deserializing UUID subtype', () => {
197+
const exampleUUID = new BSON.UUID('878dac12-01cc-4830-b271-cbc8518e63ad');
198+
const serializedUUID = BSON.serialize({ uuid: exampleUUID });
199+
const deserializedUUID = BSON.deserialize(serializedUUID);
200+
const expectedResult = {
201+
uuid: new UUID('878dac12-01cc-4830-b271-cbc8518e63ad')
202+
};
203+
expect(deserializedUUID).to.deep.equal(expectedResult);
253204
});
254205
});
255206
});

0 commit comments

Comments
 (0)