Skip to content

Commit a09536c

Browse files
authored
feat(query-parser, shell-bson-parser, constants): add shell helpers for legacy UUID MONGOSH-2486 (#594)
1 parent 93a649e commit a09536c

File tree

4 files changed

+253
-0
lines changed

4 files changed

+253
-0
lines changed

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',
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',
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',
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: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,36 @@ describe('mongodb-query-parser', function () {
121121
);
122122
});
123123

124+
it('should support LegacyJavaUUID', function () {
125+
assert.deepEqual(
126+
convert('LegacyJavaUUID("00112233-4455-6677-8899-aabbccddeeff")'),
127+
{
128+
$binary: 'd2ZVRDMiEQD/7t3Mu6qZiA==',
129+
$type: `0${bson.Binary.SUBTYPE_UUID_OLD}`,
130+
},
131+
);
132+
});
133+
134+
it('should support LegacyCSharpUUID', function () {
135+
assert.deepEqual(
136+
convert('LegacyCSharpUUID("00112233-4455-6677-8899-aabbccddeeff")'),
137+
{
138+
$binary: 'MyIRAFVEd2aImaq7zN3u/w==',
139+
$type: `0${bson.Binary.SUBTYPE_UUID_OLD}`,
140+
},
141+
);
142+
});
143+
144+
it('should support LegacyPythonUUID', function () {
145+
assert.deepEqual(
146+
convert('LegacyPythonUUID("00112233-4455-6677-8899-aabbccddeeff")'),
147+
{
148+
$binary: 'ABEiM0RVZneImaq7zN3u/w==',
149+
$type: `0${bson.Binary.SUBTYPE_UUID_OLD}`,
150+
},
151+
);
152+
});
153+
124154
// https://www.mongodb.com/docs/manual/reference/method/Binary.createFromHexString/
125155
it('should support Binary.createFromHexString', function () {
126156
assert.deepEqual(
@@ -644,6 +674,39 @@ e s`,
644674
);
645675
});
646676

677+
it('does not stringify LegacyJavaUUID', function () {
678+
const res = parseFilter(
679+
'{name: LegacyJavaUUID("00112233-4455-6677-8899-aabbccddeeff")}',
680+
);
681+
const stringified = stringify(res);
682+
assert.equal(
683+
stringified,
684+
"{name: BinData(3, 'd2ZVRDMiEQD/7t3Mu6qZiA==')}",
685+
);
686+
});
687+
688+
it('does not stringify LegacyCSharpUUID', function () {
689+
const res = parseFilter(
690+
'{name: LegacyCSharpUUID("00112233-4455-6677-8899-aabbccddeeff")}',
691+
);
692+
const stringified = stringify(res);
693+
assert.equal(
694+
stringified,
695+
"{name: BinData(3, 'MyIRAFVEd2aImaq7zN3u/w==')}",
696+
);
697+
});
698+
699+
it('does not stringify LegacyPythonUUID', function () {
700+
const res = parseFilter(
701+
'{name: LegacyPythonUUID("00112233-4455-6677-8899-aabbccddeeff")}',
702+
);
703+
const stringified = stringify(res);
704+
assert.equal(
705+
stringified,
706+
"{name: BinData(3, 'ABEiM0RVZneImaq7zN3u/w==')}",
707+
);
708+
});
709+
647710
// https://www.mongodb.com/docs/manual/reference/method/Binary.createFromHexString/
648711
it('should support Binary.createFromHexString', function () {
649712
const res = parseFilter(

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 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)