From 8511225c240553683d9b3f1b7c81600922639065 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 8 Dec 2022 05:19:29 -0500 Subject: [PATCH] feat!(NODE-4710): remove capital D ObjectID export (#528) Co-authored-by: Durran Jordan --- docs/upgrade-to-v5.md | 4 +++ src/bson.ts | 7 +---- src/extended_json.ts | 5 ++- test/node/exports.test.ts | 1 - test/node/extended_json.test.ts | 9 +++--- test/node/object_id_tests.js | 55 +++++++++++++++++++++++++++++++++ test/types/bson.test-d.ts | 2 +- 7 files changed, 70 insertions(+), 13 deletions(-) diff --git a/docs/upgrade-to-v5.md b/docs/upgrade-to-v5.md index 9cf7a701..b576ea0b 100644 --- a/docs/upgrade-to-v5.md +++ b/docs/upgrade-to-v5.md @@ -130,3 +130,7 @@ Now `-0` can be used directly BSON.deserialize(BSON.serialize({ d: -0 })) // type preservation, returns { d: -0 } ``` + +### Capital "D" ObjectID export removed + +For clarity the deprecated and duplicate export `ObjectID` has been removed. `ObjectId` matches the class name and is equal in every way to the capital "D" export. diff --git a/src/bson.ts b/src/bson.ts index 87f1181a..89f49dfe 100644 --- a/src/bson.ts +++ b/src/bson.ts @@ -49,11 +49,7 @@ export { MinKey, MaxKey, BSONRegExp, - Decimal128, - // In 4.0.0 and 4.0.1, this property name was changed to ObjectId to match the class name. - // This caused interoperability problems with previous versions of the library, so in - // later builds we changed it back to ObjectID (capital D) to match legacy implementations. - ObjectId as ObjectID + Decimal128 }; export { BSONError, BSONTypeError } from './error'; export { BSONType } from './constants'; @@ -273,7 +269,6 @@ const BSON = { MaxKey, MinKey, ObjectId, - ObjectID: ObjectId, BSONRegExp, BSONSymbol, Timestamp, diff --git a/src/extended_json.ts b/src/extended_json.ts index 9f5f5505..c85b6805 100644 --- a/src/extended_json.ts +++ b/src/extended_json.ts @@ -273,7 +273,10 @@ const BSON_TYPE_MAPPINGS = { MaxKey: () => new MaxKey(), MinKey: () => new MinKey(), ObjectID: (o: ObjectId) => new ObjectId(o), - ObjectId: (o: ObjectId) => new ObjectId(o), // support 4.0.0/4.0.1 before _bsontype was reverted back to ObjectID + // The _bsontype for ObjectId is spelled with a capital "D", to the mapping above will be used (most of the time) + // specifically BSON versions 4.0.0 and 4.0.1 the _bsontype was changed to "ObjectId" so we keep this mapping to support + // those version of BSON + ObjectId: (o: ObjectId) => new ObjectId(o), BSONRegExp: (o: BSONRegExp) => new BSONRegExp(o.pattern, o.options), Symbol: (o: BSONSymbol) => new BSONSymbol(o.value), Timestamp: (o: Timestamp) => Timestamp.fromBits(o.low, o.high) diff --git a/test/node/exports.test.ts b/test/node/exports.test.ts index 542a5b64..161e5506 100644 --- a/test/node/exports.test.ts +++ b/test/node/exports.test.ts @@ -22,7 +22,6 @@ const EXPECTED_EXPORTS = [ 'MaxKey', 'BSONRegExp', 'Decimal128', - 'ObjectID', 'BSONError', 'BSONTypeError', 'setInternalBufferSize', diff --git a/test/node/extended_json.test.ts b/test/node/extended_json.test.ts index c84ed08d..890aaa26 100644 --- a/test/node/extended_json.test.ts +++ b/test/node/extended_json.test.ts @@ -1,6 +1,7 @@ import * as BSON from '../register-bson'; const EJSON = BSON.EJSON; import * as vm from 'node:vm'; +import { expect } from 'chai'; // BSON types const Binary = BSON.Binary; @@ -13,7 +14,7 @@ const Int32 = BSON.Int32; const Long = BSON.Long; const MaxKey = BSON.MaxKey; const MinKey = BSON.MinKey; -const ObjectID = BSON.ObjectID; +const ObjectID = BSON.ObjectId; const ObjectId = BSON.ObjectId; const BSONRegExp = BSON.BSONRegExp; const BSONSymbol = BSON.BSONSymbol; @@ -365,14 +366,14 @@ describe('Extended JSON', function () { binary: new bsonModule.Binary(buffer), code: new bsonModule.Code('function() {}'), dbRef: new bsonModule.DBRef('tests', new Int32(1), 'test'), - decimal128: new bsonModule.Decimal128.fromString('9991223372036854775807'), + decimal128: bsonModule.Decimal128.fromString('9991223372036854775807'), double: new bsonModule.Double(10.1), int32: new bsonModule.Int32(10), - long: new bsonModule.Long.fromString('1223372036854775807'), + long: bsonModule.Long.fromString('1223372036854775807'), maxKey: new bsonModule.MaxKey(), // minKey: new bsonModule.MinKey(), // broken until #310 is fixed in 1.x objectId: bsonModule.ObjectId.createFromHexString('111111111111111111111111'), - objectID: bsonModule.ObjectID.createFromHexString('111111111111111111111111'), + objectID: bsonModule.ObjectId.createFromHexString('111111111111111111111111'), bsonRegExp: new bsonModule.BSONRegExp('hello world', 'i'), symbol: bsonModule.BSONSymbol ? new bsonModule.BSONSymbol('symbol') diff --git a/test/node/object_id_tests.js b/test/node/object_id_tests.js index 85489833..0d3e37b3 100644 --- a/test/node/object_id_tests.js +++ b/test/node/object_id_tests.js @@ -2,9 +2,12 @@ const Buffer = require('buffer').Buffer; const BSON = require('../register-bson'); +const EJSON = BSON.EJSON; const BSONTypeError = BSON.BSONTypeError; const ObjectId = BSON.ObjectId; const util = require('util'); +const { expect } = require('chai'); +const { bufferFromHexArray } = require('./tools/utils'); const getSymbolFrom = require('./tools/utils').getSymbolFrom; const isBufferOrUint8Array = require('./tools/utils').isBufferOrUint8Array; @@ -28,6 +31,58 @@ describe('ObjectId', function () { }); }); + describe('_bsontype casing cross compatibility', () => { + it('EJSON stringify understands capital or lowercase D _bsontype', () => { + const resultFromCapitalD = EJSON.stringify( + { a: new ObjectId('00'.repeat(12)) }, + { relaxed: false } + ); + const resultFromLowercaseD = EJSON.stringify( + { + a: new (class extends ObjectId { + get _bsontype() { + return 'ObjectId'; + } + })('00'.repeat(12)) + }, + { relaxed: false } + ); + + expect(JSON.parse(resultFromCapitalD)) + .to.have.property('a') + .that.deep.equals({ $oid: '00'.repeat(12) }); + expect(JSON.parse(resultFromLowercaseD)) + .to.have.property('a') + .that.deep.equals({ $oid: '00'.repeat(12) }); + }); + + it('EJSON stringify understands capital or lowercase D _bsontype', () => { + const resultFromCapitalD = BSON.serialize( + { a: new ObjectId('00'.repeat(12)) }, + { relaxed: false } + ); + const resultFromLowercaseD = BSON.serialize( + { + a: new (class extends ObjectId { + get _bsontype() { + return 'ObjectId'; + } + })('00'.repeat(12)) + }, + { relaxed: false } + ); + + const expectedBytes = bufferFromHexArray([ + '07', // oid type + '6100', // 'a\x00' + '00'.repeat(12) // oid bytes + ]); + + expect(resultFromCapitalD).to.deep.equal(expectedBytes); + expect(resultFromLowercaseD).to.deep.equal(expectedBytes); + }); + }); + it('creates an objectId with user defined value in the timestamp field', function () { const a = ObjectId.createFromTime(1); expect(a.id.slice(0, 4)).to.deep.equal(Buffer.from([0, 0, 0, 1])); diff --git a/test/types/bson.test-d.ts b/test/types/bson.test-d.ts index 7c25258a..82e85a52 100644 --- a/test/types/bson.test-d.ts +++ b/test/types/bson.test-d.ts @@ -51,7 +51,7 @@ expectError(MinKey.prototype.toJSON); expectError(Long.prototype.toJSON); expectError(BSONRegExp.prototype.toJSON); -// ObjectID uses a capital for backwards compatibility +// ObjectID uses a capital "D", this does not relate to the class name, or export name, only the determination for serialization expectType<'ObjectID'>(ObjectId.prototype._bsontype) // BSONSymbol was renamed to not conflict with the global JS Symbol // but its _bsontype is still 'Symbol'