From 549ea5f9b798804583b20d4591270d44e982d9bc Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 15 Nov 2021 21:08:39 +0100 Subject: [PATCH 1/2] fix(shell-api): add toBSON overrides to MinKey/MaxKey ctors MONGOSH-1024 Add `.toBSON` overrides so that sending `MinKey` or `MaxKey` over the wire results in the same document that `MinKey()` or `MaxKey()` would have generated, for compatibility with the legacy shell. --- packages/cli-repl/test/e2e-bson.spec.ts | 13 ++++++++++++ packages/shell-api/src/shell-bson.spec.ts | 24 +++++++++++++++++++++++ packages/shell-api/src/shell-bson.ts | 5 +++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/cli-repl/test/e2e-bson.spec.ts b/packages/cli-repl/test/e2e-bson.spec.ts index 063f49eceb..ab48f23af6 100644 --- a/packages/cli-repl/test/e2e-bson.spec.ts +++ b/packages/cli-repl/test/e2e-bson.spec.ts @@ -317,6 +317,19 @@ describe('BSON e2e', function() { shell.assertNoErrors(); }); }); + describe('MaxKey/MinKey special handling', () => { + it('inserts and retrieves MaxKey/MinKey regardless of whether they have been called as functions', async() => { + await shell.executeLine(`use ${dbName}`); + await shell.executeLine(`db.test.insertOne({ + maxfn: MaxKey, maxval: MaxKey(), minfn: MinKey, minval: MinKey() + })`); + const output = await shell.executeLine(`db.test.findOne()`); + expect(output).to.include('maxfn: MaxKey()'); + expect(output).to.include('maxval: MaxKey()'); + expect(output).to.include('minfn: MinKey()'); + expect(output).to.include('minval: MinKey()'); + }); + }); describe('help methods', () => { it('ObjectId has help when returned from the server', async() => { const value = new bson.ObjectId(); diff --git a/packages/shell-api/src/shell-bson.spec.ts b/packages/shell-api/src/shell-bson.spec.ts index 916aa3b035..e7aa3ad5f4 100644 --- a/packages/shell-api/src/shell-bson.spec.ts +++ b/packages/shell-api/src/shell-bson.spec.ts @@ -1,6 +1,7 @@ /* eslint camelcase: 0, new-cap: 0 */ import { CommonErrors, MongoshInvalidInputError } from '@mongosh/errors'; import { bson } from '@mongosh/service-provider-core'; +import { serialize as bsonSerialize, deserialize as bsonDeserialize } from 'bson'; import { expect } from 'chai'; import sinon from 'ts-sinon'; import { ALL_SERVER_VERSIONS } from './enums'; @@ -77,6 +78,10 @@ describe('Shell BSON', () => { const s = new (shellBson.MaxKey as any)(); expect(s._bsontype).to.equal('MaxKey'); }); + it('using toBSON', () => { + const s = (shellBson.MaxKey as any).toBSON(); + expect(s._bsontype).to.equal('MaxKey'); + }); it('has help and other metadata', async() => { const s = shellBson.MaxKey(); expect((await toShellResult(s.help)).type).to.equal('Help'); @@ -93,6 +98,10 @@ describe('Shell BSON', () => { const s = new (shellBson.MinKey as any)(); expect(s._bsontype).to.equal('MinKey'); }); + it('using toBSON', () => { + const s = (shellBson.MinKey as any).toBSON(); + expect(s._bsontype).to.equal('MinKey'); + }); it('has help and other metadata', async() => { const s = shellBson.MinKey(); expect((await toShellResult(s.help)).type).to.equal('Help'); @@ -100,6 +109,20 @@ describe('Shell BSON', () => { expect(s.serverVersions).to.deep.equal(ALL_SERVER_VERSIONS); }); }); + describe('MinKey & MaxKey constructor special handling', () => { + it('round-trips through bson as expected', () => { + const { MinKey, MaxKey } = shellBson as any; + const expected = { a: { $minKey: 1 }, b: { $maxKey: 1 } }; + function roundtrip(value: any): any { + return bson.EJSON.serialize(bsonDeserialize(bsonSerialize(value))); + } + + expect(roundtrip({ a: new MinKey(), b: new MaxKey() })).to.deep.equal(expected); + expect(roundtrip({ a: MinKey(), b: MaxKey() })).to.deep.equal(expected); + expect(roundtrip({ a: MinKey.toBSON(), b: MaxKey.toBSON() })).to.deep.equal(expected); + expect(roundtrip({ a: MinKey, b: MaxKey })).to.deep.equal(expected); + }); + }); describe('ObjectId', () => { it('without new', () => { const s = shellBson.ObjectId('5ebbe8e2905bb493d6981b6b'); @@ -615,6 +638,7 @@ describe('Shell BSON', () => { delete bsonProperties.length; delete shellProperties.index; // ObjectId.index is a random number delete bsonProperties.index; // ObjectId.index is a random number + delete shellProperties.toBSON; // toBSON is something we add for MaxKey/MinKey as a shell-specific extension try { expect(shellProperties).to.deep.equal(bsonProperties); } catch (err) { diff --git a/packages/shell-api/src/shell-bson.ts b/packages/shell-api/src/shell-bson.ts index c3aa2170c4..83aa8d1aaa 100644 --- a/packages/shell-api/src/shell-bson.ts +++ b/packages/shell-api/src/shell-bson.ts @@ -94,12 +94,13 @@ export default function constructShellBson(bson: typeof BSON, printWarning: (msg assertArgsDefinedType([object], ['object'], 'bsonsize'); return bson.calculateObjectSize(object); }, + // See https://jira.mongodb.org/browse/MONGOSH-1024 for context on the toBSON additions MaxKey: Object.assign(function MaxKey(): typeof bson.MaxKey.prototype { return new bson.MaxKey(); - }, { ...bson.MaxKey, prototype: bson.MaxKey.prototype }), + }, { ...bson.MaxKey, toBSON: () => new bson.MaxKey(), prototype: bson.MaxKey.prototype }), MinKey: Object.assign(function MinKey(): typeof bson.MinKey.prototype { return new bson.MinKey(); - }, { ...bson.MinKey, prototype: bson.MinKey.prototype }), + }, { ...bson.MinKey, toBSON: () => new bson.MinKey(), prototype: bson.MinKey.prototype }), ObjectId: Object.assign(function ObjectId(id?: string | number | typeof bson.ObjectId.prototype | Buffer): typeof bson.ObjectId.prototype { assertArgsDefinedType([id], [[undefined, 'string', 'number', 'object']], 'ObjectId'); return new bson.ObjectId(id); From 579a43cac1f4ee6fbad39c8c8df57e537a48b7f9 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 17 Nov 2021 11:22:01 +0100 Subject: [PATCH 2/2] fixup: lint --- packages/cli-repl/test/e2e-bson.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli-repl/test/e2e-bson.spec.ts b/packages/cli-repl/test/e2e-bson.spec.ts index ab48f23af6..47c47952a2 100644 --- a/packages/cli-repl/test/e2e-bson.spec.ts +++ b/packages/cli-repl/test/e2e-bson.spec.ts @@ -323,7 +323,7 @@ describe('BSON e2e', function() { await shell.executeLine(`db.test.insertOne({ maxfn: MaxKey, maxval: MaxKey(), minfn: MinKey, minval: MinKey() })`); - const output = await shell.executeLine(`db.test.findOne()`); + const output = await shell.executeLine('db.test.findOne()'); expect(output).to.include('maxfn: MaxKey()'); expect(output).to.include('maxval: MaxKey()'); expect(output).to.include('minfn: MinKey()');