Skip to content

Commit

Permalink
feat(NODE-4419): UUID class deserialization (#509)
Browse files Browse the repository at this point in the history
  • Loading branch information
aditi-khare-mongoDB committed Aug 3, 2022
1 parent f5dc9ed commit ff2b975
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 2 deletions.
49 changes: 49 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -41,6 +41,7 @@
"@typescript-eslint/eslint-plugin": "^5.30.0",
"@typescript-eslint/parser": "^5.30.0",
"array-includes": "^3.1.3",
"array.prototype.flatmap": "^1.3.0",
"benchmark": "^2.1.4",
"chai": "^4.2.0",
"eslint": "^8.18.0",
Expand Down
11 changes: 9 additions & 2 deletions src/parser/deserializer.ts
Expand Up @@ -35,6 +35,8 @@ export interface DeserializeOptions {
promoteBuffers?: boolean;
/** when deserializing will promote BSON values to their Node.js closest equivalent types. */
promoteValues?: boolean;
/** when deserializing will return UUID type, if promoteBuffers is also true then promoteUUIDs will take precedence and a buffer will not be returned */
promoteUUIDs?: boolean;
/** allow to specify if there what fields we wish to return as unserialized raw buffer. */
fieldsAsRaw?: Document;
/** return BSON regular expressions as BSONRegExp instances. */
Expand Down Expand Up @@ -135,6 +137,7 @@ function deserializeObject(
const promoteBuffers = options['promoteBuffers'] == null ? false : options['promoteBuffers'];
const promoteLongs = options['promoteLongs'] == null ? true : options['promoteLongs'];
const promoteValues = options['promoteValues'] == null ? true : options['promoteValues'];
const promoteUUIDs = options.promoteUUIDs == null ? false : options.promoteUUIDs;

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

if (promoteBuffers && promoteValues) {
if (promoteUUIDs && subType === 4) {
value = new Binary(buffer.slice(index, index + binarySize), subType).toUUID();
} else if (promoteBuffers && promoteValues) {
value = buffer.slice(index, index + binarySize);
} else {
value = new Binary(buffer.slice(index, index + binarySize), subType);
Expand All @@ -440,7 +445,9 @@ function deserializeObject(
_buffer[i] = buffer[index + i];
}

if (promoteBuffers && promoteValues) {
if (promoteUUIDs && subType === 4) {
value = new Binary(_buffer, subType).toUUID();
} else if (promoteBuffers && promoteValues) {
value = _buffer;
} else {
value = new Binary(_buffer, subType);
Expand Down
61 changes: 61 additions & 0 deletions test/node/uuid_tests.js
Expand Up @@ -191,4 +191,65 @@ describe('UUID', () => {
expect(plainUUIDSerialization).to.deep.equal(toBinarySerialization);
});
});

describe('deserialize', () => {
const originalUUID = new BSON.UUID();
const binaryUUID = originalUUID.toBinary();
const serializedUUID = BSON.serialize({ uuid: originalUUID.toBinary() });

it('should promoteUUIDs when flag is true', () => {
const { uuid: promotedUUID } = BSON.deserialize(serializedUUID, { promoteUUIDs: true });
expect(promotedUUID._bsontype).to.equal('UUID');
expect(promotedUUID).to.deep.equal(originalUUID);
});

it('should not promoteUUIDs when flag is false', () => {
const { uuid: unpromotedUUID } = BSON.deserialize(serializedUUID, { promoteUUIDs: false });
expect(unpromotedUUID._bsontype).to.equal('Binary');
expect(unpromotedUUID).to.deep.equal(binaryUUID);
});

it('should not promoteUUIDs when flag is omitted', () => {
const { uuid: omittedFlagUUID } = BSON.deserialize(serializedUUID);
expect(omittedFlagUUID._bsontype).to.equal('Binary');
expect(omittedFlagUUID).to.deep.equal(binaryUUID);
});

it('should throw BSONTypeError if _bsontype is not UUID and promoteUUIDs is true', () => {
const binaryVar = new Binary(Buffer.from('abc'), BSON_BINARY_SUBTYPE_UUID_NEW);
const serializedBinary = BSON.serialize({ d: binaryVar });
expect(() => {
BSON.deserialize(serializedBinary, { promoteUUIDs: true });
}).to.throw(BSONTypeError);
});

describe('promoteBuffers', () => {
const promoteUUIDValues = [true, false, undefined];
const promoteBufferValues = [true, false, undefined];

const testCases = promoteUUIDValues.flatMap(promoteUUIDs =>
promoteBufferValues.flatMap(promoteBuffers => ({
options: { promoteUUIDs, promoteBuffers },
// promoteBuffers: true returns a Buffer so _bsontype does not exist
outcome: promoteUUIDs ? 'UUID' : promoteBuffers ? undefined : 'Binary'
}))
);

for (const { options, outcome } of testCases) {
it(`should deserialize to ${outcome} type when promoteUUIDs is ${options.promoteUUIDs} and promoteBuffers is ${options.promoteBuffers}`, () => {
const { uuid } = BSON.deserialize(serializedUUID, options);
expect(uuid._bsontype).to.equal(outcome);
if (uuid._bsontype === 'UUID') {
expect(uuid.id).to.deep.equal(originalUUID.id);
} else if (uuid._bsontype === 'Binary') {
expect(uuid.buffer).to.deep.equal(originalUUID.id);
} else if (uuid._bsontype === undefined) {
expect(uuid).to.deep.equal(originalUUID.id);
} else {
expect.fail('Unexpected _bsontype');
}
});
}
});
});
});
1 change: 1 addition & 0 deletions test/register-bson.js
Expand Up @@ -10,6 +10,7 @@
require('chai/register-expect');
require('array-includes/auto');
require('object.entries/auto');
require('array.prototype.flatmap/auto');

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

0 comments on commit ff2b975

Please sign in to comment.