Skip to content

Commit a5ddeb9

Browse files
fix regress
1 parent 65e0e15 commit a5ddeb9

File tree

8 files changed

+37
-84
lines changed

8 files changed

+37
-84
lines changed

src/bson.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,14 @@ export function resolveBSONOptions(
132132
options?.enableUtf8Validation ?? parentOptions?.enableUtf8Validation ?? true
133133
};
134134
}
135+
136+
/** @internal */
137+
export function parseUtf8ValidationOption(options?: { enableUtf8Validation?: boolean }): {
138+
utf8: { writeErrors: false } | false;
139+
} {
140+
const enableUtf8Validation = options?.enableUtf8Validation;
141+
if (enableUtf8Validation === false) {
142+
return { utf8: false };
143+
}
144+
return { utf8: { writeErrors: false } };
145+
}

src/cmap/connection.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import {
6969
type MongoDBResponseConstructor
7070
} from './wire_protocol/responses';
7171
import { getReadPreference, isSharded } from './wire_protocol/shared';
72+
import { DeserializeOptions } from 'bson';
7273

7374
/** @internal */
7475
export interface CommandOptions extends BSONSerializeOptions {
@@ -487,7 +488,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
487488

488489
// If `documentsReturnedIn` not set or raw is not enabled, use input bson options
489490
// Otherwise, support raw flag. Raw only works for cursors that hardcode firstBatch/nextBatch fields
490-
const bsonOptions =
491+
const bsonOptions: DeserializeOptions =
491492
options.documentsReturnedIn == null || !options.raw
492493
? options
493494
: {

src/cmap/wire_protocol/on_demand/document.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DeserializeOptions } from 'bson';
12
import {
23
Binary,
34
type BSONElement,
@@ -330,14 +331,12 @@ export class OnDemandDocument {
330331
* Deserialize this object, DOES NOT cache result so avoid multiple invocations
331332
* @param options - BSON deserialization options
332333
*/
333-
public toObject(options?: BSONSerializeOptions): Record<string, any> {
334-
const exactBSONOptions = {
335-
...pluckBSONSerializeOptions(options ?? {}),
336-
validation: this.parseBsonSerializationOptions(options),
334+
public toObject(options?: DeserializeOptions & { enableUtf8Validation?: never }): Record<string, any> {
335+
return deserialize(this.bson, {
336+
...options,
337337
index: this.offset,
338338
allowObjectSmallerThanBufferSize: true
339-
};
340-
return deserialize(this.bson, exactBSONOptions);
339+
});
341340
}
342341

343342
private parseBsonSerializationOptions(options?: { enableUtf8Validation?: boolean }): {

src/cmap/wire_protocol/responses.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DeserializeOptions } from 'bson';
12
import {
23
type BSONElement,
34
type BSONSerializeOptions,
@@ -253,7 +254,7 @@ export class CursorResponse extends MongoDBResponse {
253254
);
254255
}
255256

256-
public shift(options?: BSONSerializeOptions): any {
257+
public shift(options?: DeserializeOptions & { __tag: 'shift options' }): any {
257258
if (this.iterated >= this.batchSize) {
258259
return null;
259260
}
@@ -305,7 +306,7 @@ export class ExplainedCursorResponse extends CursorResponse {
305306
return this._length;
306307
}
307308

308-
override shift(options?: BSONSerializeOptions | undefined) {
309+
override shift(options?: DeserializeOptions) {
309310
if (this._length === 0) return null;
310311
this._length -= 1;
311312
return this.toObject(options);

src/cursor/abstract_cursor.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Readable, Transform } from 'stream';
22

3-
import { type BSONSerializeOptions, type Document, Long, pluckBSONSerializeOptions } from '../bson';
3+
import { type BSONSerializeOptions, type Document, Long, parseUtf8ValidationOption, pluckBSONSerializeOptions } from '../bson';
44
import { type CursorResponse } from '../cmap/wire_protocol/responses';
55
import {
66
MongoAPIError,
@@ -21,6 +21,7 @@ import { type AsyncDisposable, configureResourceManagement } from '../resource_m
2121
import type { Server } from '../sdam/server';
2222
import { ClientSession, maybeClearPinnedConnection } from '../sessions';
2323
import { type MongoDBNamespace, squashError } from '../utils';
24+
import { DeserializeOptions } from 'bson';
2425

2526
/**
2627
* @internal
@@ -157,6 +158,8 @@ export abstract class AbstractCursor<
157158
/** @event */
158159
static readonly CLOSE = 'close' as const;
159160

161+
protected deserializationOptions: DeserializeOptions & { __tag: 'shift options' };
162+
160163
/** @internal */
161164
protected constructor(
162165
client: MongoClient,
@@ -211,6 +214,12 @@ export abstract class AbstractCursor<
211214
} else {
212215
this.cursorSession = this.cursorClient.startSession({ owner: this, explicit: false });
213216
}
217+
218+
this.deserializationOptions = {
219+
...this.cursorOptions,
220+
validation: parseUtf8ValidationOption(this.cursorOptions),
221+
__tag: 'shift options'
222+
}
214223
}
215224

216225
/**
@@ -304,7 +313,7 @@ export abstract class AbstractCursor<
304313
);
305314

306315
for (let count = 0; count < documentsToRead; count++) {
307-
const document = this.documents?.shift(this.cursorOptions);
316+
const document = this.documents?.shift(this.deserializationOptions);
308317
if (document != null) {
309318
bufferedDocs.push(document);
310319
}
@@ -406,7 +415,7 @@ export abstract class AbstractCursor<
406415
}
407416

408417
do {
409-
const doc = this.documents?.shift(this.cursorOptions);
418+
const doc = this.documents?.shift(this.deserializationOptions);
410419
if (doc != null) {
411420
if (this.transform != null) return await this.transformDocument(doc);
412421
return doc;
@@ -425,15 +434,15 @@ export abstract class AbstractCursor<
425434
throw new MongoCursorExhaustedError();
426435
}
427436

428-
let doc = this.documents?.shift(this.cursorOptions);
437+
let doc = this.documents?.shift(this.deserializationOptions);
429438
if (doc != null) {
430439
if (this.transform != null) return await this.transformDocument(doc);
431440
return doc;
432441
}
433442

434443
await this.fetchBatch();
435444

436-
doc = this.documents?.shift(this.cursorOptions);
445+
doc = this.documents?.shift(this.deserializationOptions);
437446
if (doc != null) {
438447
if (this.transform != null) return await this.transformDocument(doc);
439448
return doc;

src/cursor/aggregation_cursor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class AggregationCursor<TSchema = any> extends AbstractCursor<TSchema> {
7676
explain: verbosity ?? true
7777
})
7878
)
79-
).shift(this.aggregateOptions);
79+
).shift(this.deserializationOptions);
8080
}
8181

8282
/** Add a stage to the aggregation pipeline

src/cursor/find_cursor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export class FindCursor<TSchema = any> extends AbstractCursor<TSchema> {
143143
explain: verbosity ?? true
144144
})
145145
)
146-
).shift(this.findOptions);
146+
).shift(this.deserializationOptions);
147147
}
148148

149149
/** Set the cursor query */

test/unit/cmap/wire_protocol/responses.test.ts

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -17,74 +17,6 @@ describe('class MongoDBResponse', () => {
1717
it('is a subclass of OnDemandDocument', () => {
1818
expect(new MongoDBResponse(serialize({ ok: 1 }))).to.be.instanceOf(OnDemandDocument);
1919
});
20-
21-
context('utf8 validation', () => {
22-
let deseriailzeSpy: sinon.SinonStub<Parameters<typeof mdb.deserialize>>;
23-
beforeEach(function () {
24-
const deserialize = mdb.deserialize;
25-
deseriailzeSpy = sinon.stub<Parameters<typeof deserialize>>().callsFake(deserialize);
26-
sinon.stub(mdb, 'deserialize').get(() => {
27-
return deseriailzeSpy;
28-
});
29-
});
30-
afterEach(function () {
31-
sinon.restore();
32-
});
33-
34-
context('when enableUtf8Validation is not specified', () => {
35-
const options = { enableUtf8Validation: undefined };
36-
it('calls BSON deserialize with writeErrors validation turned off', () => {
37-
const res = new MongoDBResponse(serialize({}));
38-
res.toObject(options);
39-
40-
expect(deseriailzeSpy).to.have.been.called;
41-
42-
const [
43-
{
44-
args: [_buffer, { validation }]
45-
}
46-
] = deseriailzeSpy.getCalls();
47-
48-
expect(validation).to.deep.equal({ utf8: { writeErrors: false } });
49-
});
50-
});
51-
52-
context('when enableUtf8Validation is true', () => {
53-
const options = { enableUtf8Validation: true };
54-
it('calls BSON deserialize with writeErrors validation turned off', () => {
55-
const res = new MongoDBResponse(serialize({}));
56-
res.toObject(options);
57-
58-
expect(deseriailzeSpy).to.have.been.called;
59-
60-
const [
61-
{
62-
args: [_buffer, { validation }]
63-
}
64-
] = deseriailzeSpy.getCalls();
65-
66-
expect(validation).to.deep.equal({ utf8: { writeErrors: false } });
67-
});
68-
});
69-
70-
context('when enableUtf8Validation is false', () => {
71-
const options = { enableUtf8Validation: false };
72-
it('calls BSON deserialize with all validation disabled', () => {
73-
const res = new MongoDBResponse(serialize({}));
74-
res.toObject(options);
75-
76-
expect(deseriailzeSpy).to.have.been.called;
77-
78-
const [
79-
{
80-
args: [_buffer, { validation }]
81-
}
82-
] = deseriailzeSpy.getCalls();
83-
84-
expect(validation).to.deep.equal({ utf8: false });
85-
});
86-
});
87-
});
8820
});
8921

9022
describe('class CursorResponse', () => {

0 commit comments

Comments
 (0)