Skip to content

Commit

Permalink
fix regress
Browse files Browse the repository at this point in the history
  • Loading branch information
baileympearson committed Sep 4, 2024
1 parent 65e0e15 commit a5ddeb9
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 84 deletions.
11 changes: 11 additions & 0 deletions src/bson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,14 @@ export function resolveBSONOptions(
options?.enableUtf8Validation ?? parentOptions?.enableUtf8Validation ?? true
};
}

/** @internal */
export function parseUtf8ValidationOption(options?: { enableUtf8Validation?: boolean }): {
utf8: { writeErrors: false } | false;
} {
const enableUtf8Validation = options?.enableUtf8Validation;
if (enableUtf8Validation === false) {
return { utf8: false };
}
return { utf8: { writeErrors: false } };
}
3 changes: 2 additions & 1 deletion src/cmap/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import {
type MongoDBResponseConstructor
} from './wire_protocol/responses';
import { getReadPreference, isSharded } from './wire_protocol/shared';
import { DeserializeOptions } from 'bson';

/** @internal */
export interface CommandOptions extends BSONSerializeOptions {
Expand Down Expand Up @@ -487,7 +488,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {

// If `documentsReturnedIn` not set or raw is not enabled, use input bson options
// Otherwise, support raw flag. Raw only works for cursors that hardcode firstBatch/nextBatch fields
const bsonOptions =
const bsonOptions: DeserializeOptions =
options.documentsReturnedIn == null || !options.raw
? options
: {
Expand Down
11 changes: 5 additions & 6 deletions src/cmap/wire_protocol/on_demand/document.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DeserializeOptions } from 'bson';
import {
Binary,
type BSONElement,
Expand Down Expand Up @@ -330,14 +331,12 @@ export class OnDemandDocument {
* Deserialize this object, DOES NOT cache result so avoid multiple invocations
* @param options - BSON deserialization options
*/
public toObject(options?: BSONSerializeOptions): Record<string, any> {
const exactBSONOptions = {
...pluckBSONSerializeOptions(options ?? {}),
validation: this.parseBsonSerializationOptions(options),
public toObject(options?: DeserializeOptions & { enableUtf8Validation?: never }): Record<string, any> {
return deserialize(this.bson, {
...options,
index: this.offset,
allowObjectSmallerThanBufferSize: true
};
return deserialize(this.bson, exactBSONOptions);
});
}

private parseBsonSerializationOptions(options?: { enableUtf8Validation?: boolean }): {
Expand Down
5 changes: 3 additions & 2 deletions src/cmap/wire_protocol/responses.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DeserializeOptions } from 'bson';
import {
type BSONElement,
type BSONSerializeOptions,
Expand Down Expand Up @@ -253,7 +254,7 @@ export class CursorResponse extends MongoDBResponse {
);
}

public shift(options?: BSONSerializeOptions): any {
public shift(options?: DeserializeOptions & { __tag: 'shift options' }): any {
if (this.iterated >= this.batchSize) {
return null;
}
Expand Down Expand Up @@ -305,7 +306,7 @@ export class ExplainedCursorResponse extends CursorResponse {
return this._length;
}

override shift(options?: BSONSerializeOptions | undefined) {
override shift(options?: DeserializeOptions) {
if (this._length === 0) return null;
this._length -= 1;
return this.toObject(options);
Expand Down
19 changes: 14 additions & 5 deletions src/cursor/abstract_cursor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Readable, Transform } from 'stream';

import { type BSONSerializeOptions, type Document, Long, pluckBSONSerializeOptions } from '../bson';
import { type BSONSerializeOptions, type Document, Long, parseUtf8ValidationOption, pluckBSONSerializeOptions } from '../bson';
import { type CursorResponse } from '../cmap/wire_protocol/responses';
import {
MongoAPIError,
Expand All @@ -21,6 +21,7 @@ import { type AsyncDisposable, configureResourceManagement } from '../resource_m
import type { Server } from '../sdam/server';
import { ClientSession, maybeClearPinnedConnection } from '../sessions';
import { type MongoDBNamespace, squashError } from '../utils';
import { DeserializeOptions } from 'bson';

/**
* @internal
Expand Down Expand Up @@ -157,6 +158,8 @@ export abstract class AbstractCursor<
/** @event */
static readonly CLOSE = 'close' as const;

protected deserializationOptions: DeserializeOptions & { __tag: 'shift options' };

/** @internal */
protected constructor(
client: MongoClient,
Expand Down Expand Up @@ -211,6 +214,12 @@ export abstract class AbstractCursor<
} else {
this.cursorSession = this.cursorClient.startSession({ owner: this, explicit: false });
}

this.deserializationOptions = {
...this.cursorOptions,
validation: parseUtf8ValidationOption(this.cursorOptions),
__tag: 'shift options'
}
}

/**
Expand Down Expand Up @@ -304,7 +313,7 @@ export abstract class AbstractCursor<
);

for (let count = 0; count < documentsToRead; count++) {
const document = this.documents?.shift(this.cursorOptions);
const document = this.documents?.shift(this.deserializationOptions);
if (document != null) {
bufferedDocs.push(document);
}
Expand Down Expand Up @@ -406,7 +415,7 @@ export abstract class AbstractCursor<
}

do {
const doc = this.documents?.shift(this.cursorOptions);
const doc = this.documents?.shift(this.deserializationOptions);
if (doc != null) {
if (this.transform != null) return await this.transformDocument(doc);
return doc;
Expand All @@ -425,15 +434,15 @@ export abstract class AbstractCursor<
throw new MongoCursorExhaustedError();
}

let doc = this.documents?.shift(this.cursorOptions);
let doc = this.documents?.shift(this.deserializationOptions);
if (doc != null) {
if (this.transform != null) return await this.transformDocument(doc);
return doc;
}

await this.fetchBatch();

doc = this.documents?.shift(this.cursorOptions);
doc = this.documents?.shift(this.deserializationOptions);
if (doc != null) {
if (this.transform != null) return await this.transformDocument(doc);
return doc;
Expand Down
2 changes: 1 addition & 1 deletion src/cursor/aggregation_cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class AggregationCursor<TSchema = any> extends AbstractCursor<TSchema> {
explain: verbosity ?? true
})
)
).shift(this.aggregateOptions);
).shift(this.deserializationOptions);
}

/** Add a stage to the aggregation pipeline
Expand Down
2 changes: 1 addition & 1 deletion src/cursor/find_cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export class FindCursor<TSchema = any> extends AbstractCursor<TSchema> {
explain: verbosity ?? true
})
)
).shift(this.findOptions);
).shift(this.deserializationOptions);
}

/** Set the cursor query */
Expand Down
68 changes: 0 additions & 68 deletions test/unit/cmap/wire_protocol/responses.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,74 +17,6 @@ describe('class MongoDBResponse', () => {
it('is a subclass of OnDemandDocument', () => {
expect(new MongoDBResponse(serialize({ ok: 1 }))).to.be.instanceOf(OnDemandDocument);
});

context('utf8 validation', () => {
let deseriailzeSpy: sinon.SinonStub<Parameters<typeof mdb.deserialize>>;
beforeEach(function () {
const deserialize = mdb.deserialize;
deseriailzeSpy = sinon.stub<Parameters<typeof deserialize>>().callsFake(deserialize);
sinon.stub(mdb, 'deserialize').get(() => {
return deseriailzeSpy;
});
});
afterEach(function () {
sinon.restore();
});

context('when enableUtf8Validation is not specified', () => {
const options = { enableUtf8Validation: undefined };
it('calls BSON deserialize with writeErrors validation turned off', () => {
const res = new MongoDBResponse(serialize({}));
res.toObject(options);

expect(deseriailzeSpy).to.have.been.called;

const [
{
args: [_buffer, { validation }]
}
] = deseriailzeSpy.getCalls();

expect(validation).to.deep.equal({ utf8: { writeErrors: false } });
});
});

context('when enableUtf8Validation is true', () => {
const options = { enableUtf8Validation: true };
it('calls BSON deserialize with writeErrors validation turned off', () => {
const res = new MongoDBResponse(serialize({}));
res.toObject(options);

expect(deseriailzeSpy).to.have.been.called;

const [
{
args: [_buffer, { validation }]
}
] = deseriailzeSpy.getCalls();

expect(validation).to.deep.equal({ utf8: { writeErrors: false } });
});
});

context('when enableUtf8Validation is false', () => {
const options = { enableUtf8Validation: false };
it('calls BSON deserialize with all validation disabled', () => {
const res = new MongoDBResponse(serialize({}));
res.toObject(options);

expect(deseriailzeSpy).to.have.been.called;

const [
{
args: [_buffer, { validation }]
}
] = deseriailzeSpy.getCalls();

expect(validation).to.deep.equal({ utf8: false });
});
});
});
});

describe('class CursorResponse', () => {
Expand Down

0 comments on commit a5ddeb9

Please sign in to comment.