Skip to content

Commit 8a41879

Browse files
committed
feat(query-parser, shell-bson-parser): add legacy UUID helpers MONGOSH-2486
1 parent ab477c0 commit 8a41879

File tree

6 files changed

+228
-0
lines changed

6 files changed

+228
-0
lines changed

.tool-versions

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nodejs 20.19.2

packages/mongodb-constants/src/bson-types.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,46 @@ const BSON_TYPES = [
122122
description: 'BSON Regex type',
123123
snippet: "RegExp('${1:source}', '${2:opts}')",
124124
},
125+
{
126+
name: 'LegacyJavaUUID',
127+
value: 'LegacyJavaUUID',
128+
label: 'LegacyJavaUUID',
129+
score: 1,
130+
meta: 'bson-legacy-uuid-support',
131+
version: '0.0.0',
132+
description: 'BSON Binary subtype 3 (Java legacy UUID)',
133+
snippet: "LegacyJavaUUID('${1:uuid}')",
134+
},
135+
{
136+
name: 'LegacyCSharpUUID',
137+
value: 'LegacyCSharpUUID',
138+
label: 'LegacyCSharpUUID',
139+
score: 1,
140+
meta: 'bson-legacy-uuid-support',
141+
version: '0.0.0',
142+
description: 'BSON Binary subtype 3 (CSharp legacy UUID)',
143+
snippet: "LegacyCSharpUUID('${1:uuid}')",
144+
},
145+
{
146+
name: 'LegacyPythonUUID',
147+
value: 'LegacyPythonUUID',
148+
label: 'LegacyPythonUUID',
149+
score: 1,
150+
meta: 'bson-legacy-uuid-support',
151+
version: '0.0.0',
152+
description: 'BSON Binary subtype 3 (Python legacy UUID)',
153+
snippet: "LegacyPythonUUID('${1:uuid}')",
154+
},
155+
{
156+
name: 'UUID',
157+
value: 'UUID',
158+
label: 'UUID',
159+
score: 1,
160+
meta: 'bson',
161+
version: '0.0.0',
162+
description: 'BSON Binary subtype 4',
163+
snippet: "UUID('${1:uuid}')",
164+
},
125165
] as const;
126166

127167
export { BSON_TYPES };

packages/query-parser/src/index.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,38 @@ describe('mongodb-query-parser', function () {
120120
);
121121
});
122122

123+
// TODO: prototype replace test with val for new val.
124+
125+
it('should support LegacyJavaUUID', function () {
126+
assert.deepEqual(
127+
convert('LegacyJavaUUID("00112233-4455-6677-8899-aabbccddeeff")'),
128+
{
129+
$binary: 'OyQRAeK7QlWMr0E2xWapYg==',
130+
$type: `0${bson.Binary.SUBTYPE_UUID_OLD}`,
131+
},
132+
);
133+
});
134+
135+
it('should support LegacyCSharpUUID', function () {
136+
assert.deepEqual(
137+
convert('LegacyCSharpUUID("00112233-4455-6677-8899-aabbccddeeff")'),
138+
{
139+
$binary: 'OyQRAeK7QlWMr0E2xWapYg==',
140+
$type: `0${bson.Binary.SUBTYPE_UUID_OLD}`,
141+
},
142+
);
143+
});
144+
145+
it('should support LegacyPythonUUID', function () {
146+
assert.deepEqual(
147+
convert('LegacyPythonUUID("00112233-4455-6677-8899-aabbccddeeff")'),
148+
{
149+
$binary: 'OyQRAeK7QlWMr0E2xWapYg==',
150+
$type: `0${bson.Binary.SUBTYPE_UUID_OLD}`,
151+
},
152+
);
153+
});
154+
123155
// https://www.mongodb.com/docs/manual/reference/method/Binary.createFromHexString/
124156
it('should support Binary.createFromHexString', function () {
125157
assert.deepEqual(
@@ -643,6 +675,8 @@ e s`,
643675
);
644676
});
645677

678+
// TODO: New tests for the legacy UUID formats.
679+
646680
// https://www.mongodb.com/docs/manual/reference/method/Binary.createFromHexString/
647681
it('should support Binary.createFromHexString', function () {
648682
const res = parseFilter(

packages/query-parser/src/stringify.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ const BSON_TO_JS_STRING = {
6060
return `ObjectId('${v.toString('hex')}')`;
6161
},
6262
Binary: function (v: Binary) {
63+
// TODO: handle UUID subtypes Compass side or with an
64+
// option for subType 3 handling here.
65+
6366
const subType = v.sub_type;
6467
if (subType === 4 && v.buffer.length === 16) {
6568
let uuidHex = '';

packages/shell-bson-parser/src/index.spec.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,78 @@ describe('@mongodb-js/shell-bson-parser', function () {
5151
});
5252
});
5353

54+
it('should create new UUIDs', function () {
55+
expect(parse('{name: UUID()}'))
56+
.to.have.property('name')
57+
.that.is.instanceOf(bson.Binary);
58+
expect(parse('{name: LegacyCSharpUUID()}'))
59+
.to.have.property('name')
60+
.that.is.instanceOf(bson.Binary);
61+
expect(parse('{name: LegacyJavaUUID()}'))
62+
.to.have.property('name')
63+
.that.is.instanceOf(bson.Binary);
64+
expect(parse('{name: LegacyPythonUUID()}'))
65+
.to.have.property('name')
66+
.that.is.instanceOf(bson.Binary);
67+
});
68+
69+
describe('with a set UUID generation', function () {
70+
let sandbox: SinonSandbox;
71+
72+
beforeEach(function () {
73+
sandbox = createSandbox();
74+
75+
sandbox.replace((bson as any).UUID.prototype, 'toHexString', function () {
76+
return '00112233-4455-6677-8899-aabbccddeeff';
77+
});
78+
sandbox.replace((bson as any).UUID.prototype, 'toBinary', function () {
79+
return new bson.Binary(
80+
Buffer.from('00112233445566778899aabbccddeeff', 'hex'),
81+
4,
82+
);
83+
});
84+
});
85+
afterEach(function () {
86+
sandbox.restore();
87+
});
88+
89+
it('should create new UUIDs in the correct formats for legacy', function () {
90+
expect(parse('{name: UUID()}')).to.have.deep.equal({
91+
name: new bson.Binary(
92+
Buffer.from('00112233445566778899aabbccddeeff', 'hex'),
93+
4,
94+
),
95+
});
96+
expect(parse('{name: LegacyCSharpUUID()}')).to.have.deep.equal({
97+
name: new bson.Binary(
98+
Buffer.from('33221100554477668899aabbccddeeff', 'hex'),
99+
3,
100+
),
101+
});
102+
expect(parse('{name: LegacyJavaUUID()}')).to.have.deep.equal({
103+
name: new bson.Binary(
104+
Buffer.from('7766554433221100ffeeddccbbaa9988', 'hex'),
105+
3,
106+
),
107+
});
108+
expect(parse('{name: LegacyPythonUUID()}')).to.have.deep.equal({
109+
name: new bson.Binary(
110+
Buffer.from('00112233445566778899aabbccddeeff', 'hex'),
111+
3,
112+
),
113+
});
114+
});
115+
});
116+
54117
it('should accept a complex query', function () {
55118
expect(
56119
parse(`{
57120
RegExp: /test/ig,
58121
Binary: new Binary(),
59122
BinData: BinData(3, 'dGVzdAo='),
123+
LegacyCSharpUUID: LegacyCSharpUUID('00112233-4455-6677-8899-aabbccddeeff'),
124+
LegacyJavaUUID: LegacyJavaUUID('00112233-4455-6677-8899-aabbccddeeff'),
125+
LegacyPythonUUID: LegacyPythonUUID('00112233-4455-6677-8899-aabbccddeeff'),
60126
UUID: UUID('3d37923d-ab8e-4931-9e46-93df5fd3599e'),
61127
Code: Code('function() {}'),
62128
DBRef: new DBRef('tests', new ObjectId("5e159ba7eac34211f2252aaa"), 'test'),
@@ -84,6 +150,18 @@ describe('@mongodb-js/shell-bson-parser', function () {
84150
RegExp: /test/gi,
85151
Binary: new bson.Binary(),
86152
BinData: new bson.Binary(Buffer.from('dGVzdAo=', 'base64'), 3),
153+
LegacyCSharpUUID: new bson.Binary(
154+
Buffer.from('33221100554477668899aabbccddeeff', 'hex'),
155+
3,
156+
),
157+
LegacyJavaUUID: new bson.Binary(
158+
Buffer.from('7766554433221100ffeeddccbbaa9988', 'hex'),
159+
3,
160+
),
161+
LegacyPythonUUID: new bson.Binary(
162+
Buffer.from('00112233445566778899aabbccddeeff', 'hex'),
163+
3,
164+
),
87165
UUID: new bson.Binary(
88166
Buffer.from('3d37923dab8e49319e4693df5fd3599e', 'hex'),
89167
4,

packages/shell-bson-parser/src/scope.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,78 @@ const SCOPE_ANY: { [x: string]: Function } = lookupMap({
4141
Binary: function (buffer: any, subType: any) {
4242
return new bson.Binary(buffer, subType);
4343
},
44+
45+
// Legacy UUID functions are from originally taken from
46+
// https://github.com/mongodb/mongo-csharp-driver/blob/ac2b2a61c6b7a193cf0266dfb8c65f86c2bf7572/uuidhelpers.js
47+
LegacyJavaUUID: function (u: any) {
48+
if (u === undefined) {
49+
// Generate a new UUID and format it.
50+
u = new bson.UUID().toHexString();
51+
}
52+
53+
let hex: string = String.prototype.replace.call(u, /[{}-]/g, () => '');
54+
let msb = String.prototype.substring.call(hex, 0, 16);
55+
let lsb = String.prototype.substring.call(hex, 16, 32);
56+
msb =
57+
String.prototype.substring.call(msb, 14, 16) +
58+
String.prototype.substring.call(msb, 12, 14) +
59+
String.prototype.substring.call(msb, 10, 12) +
60+
String.prototype.substring.call(msb, 8, 10) +
61+
String.prototype.substring.call(msb, 6, 8) +
62+
String.prototype.substring.call(msb, 4, 6) +
63+
String.prototype.substring.call(msb, 2, 4) +
64+
String.prototype.substring.call(msb, 0, 2);
65+
lsb =
66+
String.prototype.substring.call(lsb, 14, 16) +
67+
String.prototype.substring.call(lsb, 12, 14) +
68+
String.prototype.substring.call(lsb, 10, 12) +
69+
String.prototype.substring.call(lsb, 8, 10) +
70+
String.prototype.substring.call(lsb, 6, 8) +
71+
String.prototype.substring.call(lsb, 4, 6) +
72+
String.prototype.substring.call(lsb, 2, 4) +
73+
String.prototype.substring.call(lsb, 0, 2);
74+
hex = msb + lsb;
75+
76+
const hexBuffer = Buffer.from(hex, 'hex');
77+
return new bson.Binary(hexBuffer, 3);
78+
},
79+
LegacyCSharpUUID: function (u: any) {
80+
if (u === undefined) {
81+
// Generate a new UUID and format it.
82+
u = new bson.UUID().toHexString();
83+
}
84+
85+
let hex: string = String.prototype.replace.call(u, /[{}-]/g, () => '');
86+
const a =
87+
String.prototype.substring.call(hex, 6, 8) +
88+
String.prototype.substring.call(hex, 4, 6) +
89+
String.prototype.substring.call(hex, 2, 4) +
90+
String.prototype.substring.call(hex, 0, 2);
91+
const b =
92+
String.prototype.substring.call(hex, 10, 12) +
93+
String.prototype.substring.call(hex, 8, 10);
94+
const c =
95+
String.prototype.substring.call(hex, 14, 16) +
96+
String.prototype.substring.call(hex, 12, 14);
97+
const d = String.prototype.substring.call(hex, 16, 32);
98+
hex = a + b + c + d;
99+
100+
const hexBuffer = Buffer.from(hex, 'hex');
101+
return new bson.Binary(hexBuffer, 3);
102+
},
103+
LegacyPythonUUID: function (u: any) {
104+
if (u === undefined) {
105+
return new bson.Binary(new bson.UUID().toBinary().buffer, 3);
106+
}
107+
108+
return new bson.Binary(
109+
Buffer.from(
110+
String.prototype.replace.call(u, /[{}-]/g, () => ''),
111+
'hex',
112+
),
113+
3,
114+
);
115+
},
44116
BinData: function (t: any, d: any) {
45117
return new bson.Binary(Buffer.from(d, 'base64'), t);
46118
},

0 commit comments

Comments
 (0)