Skip to content

Commit

Permalink
fix(NODE-3150): allow retrieving PCRE-style RegExp
Browse files Browse the repository at this point in the history
  • Loading branch information
andymina committed Jun 25, 2021
1 parent 1a57ba8 commit ca9e2dc
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 12 deletions.
4 changes: 3 additions & 1 deletion src/bson.ts
Expand Up @@ -48,7 +48,6 @@ export interface BSONSerializeOptions
| 'evalFunctions'
| 'cacheFunctions'
| 'cacheFunctionsCrc32'
| 'bsonRegExp'
| 'allowObjectSmallerThanBufferSize'
| 'index'
> {
Expand All @@ -64,6 +63,7 @@ export function pluckBSONSerializeOptions(options: BSONSerializeOptions): BSONSe
promoteLongs,
serializeFunctions,
ignoreUndefined,
bsonRegExp,
raw
} = options;
return {
Expand All @@ -73,6 +73,7 @@ export function pluckBSONSerializeOptions(options: BSONSerializeOptions): BSONSe
promoteLongs,
serializeFunctions,
ignoreUndefined,
bsonRegExp,
raw
};
}
Expand All @@ -94,6 +95,7 @@ export function resolveBSONOptions(
promoteValues: options?.promoteValues ?? parentOptions?.promoteValues ?? true,
promoteBuffers: options?.promoteBuffers ?? parentOptions?.promoteBuffers ?? false,
ignoreUndefined: options?.ignoreUndefined ?? parentOptions?.ignoreUndefined ?? false,
bsonRegExp: options?.bsonRegExp ?? parentOptions?.bsonRegExp ?? false,
serializeFunctions: options?.serializeFunctions ?? parentOptions?.serializeFunctions ?? false,
fieldsAsRaw: options?.fieldsAsRaw ?? parentOptions?.fieldsAsRaw ?? {}
};
Expand Down
3 changes: 2 additions & 1 deletion src/cmap/auth/mongodb_aws.ts
Expand Up @@ -18,7 +18,8 @@ const AWS_EC2_PATH = '/latest/meta-data/iam/security-credentials';
const bsonOptions: BSONSerializeOptions = {
promoteLongs: true,
promoteValues: true,
promoteBuffers: false
promoteBuffers: false,
bsonRegExp: false
};

interface AWSSaslContinuePayload {
Expand Down
34 changes: 26 additions & 8 deletions src/cmap/commands.ts
Expand Up @@ -491,6 +491,7 @@ export class Response {
promoteLongs: boolean;
promoteValues: boolean;
promoteBuffers: boolean;
bsonRegExp?: boolean;
index?: number;

constructor(
Expand All @@ -502,7 +503,12 @@ export class Response {
this.parsed = false;
this.raw = message;
this.data = msgBody;
this.opts = opts ?? { promoteLongs: true, promoteValues: true, promoteBuffers: false };
this.opts = opts ?? {
promoteLongs: true,
promoteValues: true,
promoteBuffers: false,
bsonRegExp: false
};

// Read the message header
this.length = msgHeader.length;
Expand Down Expand Up @@ -530,6 +536,7 @@ export class Response {
typeof this.opts.promoteValues === 'boolean' ? this.opts.promoteValues : true;
this.promoteBuffers =
typeof this.opts.promoteBuffers === 'boolean' ? this.opts.promoteBuffers : false;
this.bsonRegExp = typeof this.opts.bsonRegExp === 'boolean' ? this.opts.bsonRegExp : false;
}

isParsed(): boolean {
Expand All @@ -547,13 +554,15 @@ export class Response {
const promoteLongs = options.promoteLongs ?? this.opts.promoteLongs;
const promoteValues = options.promoteValues ?? this.opts.promoteValues;
const promoteBuffers = options.promoteBuffers ?? this.opts.promoteBuffers;
const bsonRegExp = options.bsonRegExp ?? this.opts.bsonRegExp;
let bsonSize;

// Set up the options
const _options: BSONSerializeOptions = {
promoteLongs: promoteLongs,
promoteValues: promoteValues,
promoteBuffers: promoteBuffers
promoteLongs,
promoteValues,
promoteBuffers,
bsonRegExp
};

// Position within OP_REPLY at which documents start
Expand Down Expand Up @@ -765,6 +774,7 @@ export class BinMsg {
promoteLongs: boolean;
promoteValues: boolean;
promoteBuffers: boolean;
bsonRegExp: boolean;
documents: (Document | Buffer)[];
index?: number;

Expand All @@ -777,7 +787,12 @@ export class BinMsg {
this.parsed = false;
this.raw = message;
this.data = msgBody;
this.opts = opts ?? { promoteLongs: true, promoteValues: true, promoteBuffers: false };
this.opts = opts ?? {
promoteLongs: true,
promoteValues: true,
promoteBuffers: false,
bsonRegExp: false
};

// Read the message header
this.length = msgHeader.length;
Expand All @@ -796,6 +811,7 @@ export class BinMsg {
typeof this.opts.promoteValues === 'boolean' ? this.opts.promoteValues : true;
this.promoteBuffers =
typeof this.opts.promoteBuffers === 'boolean' ? this.opts.promoteBuffers : false;
this.bsonRegExp = typeof this.opts.bsonRegExp === 'boolean' ? this.opts.bsonRegExp : false;

this.documents = [];
}
Expand All @@ -816,12 +832,14 @@ export class BinMsg {
const promoteLongs = options.promoteLongs ?? this.opts.promoteLongs;
const promoteValues = options.promoteValues ?? this.opts.promoteValues;
const promoteBuffers = options.promoteBuffers ?? this.opts.promoteBuffers;
const bsonRegExp = options.bsonRegExp ?? this.opts.bsonRegExp;

// Set up the options
const _options: BSONSerializeOptions = {
promoteLongs: promoteLongs,
promoteValues: promoteValues,
promoteBuffers: promoteBuffers
promoteLongs,
promoteValues,
promoteBuffers,
bsonRegExp
};

while (this.index < this.data.length) {
Expand Down
1 change: 1 addition & 0 deletions src/cmap/connection.ts
Expand Up @@ -792,6 +792,7 @@ function write(
promoteLongs: typeof options.promoteLongs === 'boolean' ? options.promoteLongs : true,
promoteValues: typeof options.promoteValues === 'boolean' ? options.promoteValues : true,
promoteBuffers: typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false,
bsonRegExp: typeof options.bsonRegExp === 'boolean' ? options.bsonRegExp : false,
raw: typeof options.raw === 'boolean' ? options.raw : false,
started: 0
};
Expand Down
3 changes: 2 additions & 1 deletion src/cmap/wire_protocol/shared.ts
Expand Up @@ -42,7 +42,8 @@ export function applyCommonQueryOptions(
raw: typeof options.raw === 'boolean' ? options.raw : false,
promoteLongs: typeof options.promoteLongs === 'boolean' ? options.promoteLongs : true,
promoteValues: typeof options.promoteValues === 'boolean' ? options.promoteValues : true,
promoteBuffers: typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false
promoteBuffers: typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false,
bsonRegExp: typeof options.bsonRegExp === 'boolean' ? options.bsonRegExp : false
});

if (options.session) {
Expand Down
3 changes: 3 additions & 0 deletions src/connection_string.ts
Expand Up @@ -588,6 +588,9 @@ export const OPTIONS = {
autoEncryption: {
type: 'record'
},
bsonRegExp: {
type: 'boolean'
},
serverApi: {
target: 'serverApi',
transform({ values: [version] }): ServerApi {
Expand Down
2 changes: 1 addition & 1 deletion src/db.ts
Expand Up @@ -61,14 +61,14 @@ const DB_OPTIONS_ALLOW_LIST = [
'raw',
'authSource',
'ignoreUndefined',
'promoteLongs',
'readConcern',
'retryMiliSeconds',
'numberOfRetries',
'loggerLevel',
'logger',
'promoteBuffers',
'promoteLongs',
'bsonRegExp',
'promoteValues',
'compression',
'retryWrites'
Expand Down
1 change: 1 addition & 0 deletions src/operations/create_collection.ts
Expand Up @@ -25,6 +25,7 @@ const ILLEGAL_COMMAND_FIELDS = new Set([
'promoteLongs',
'promoteValues',
'promoteBuffers',
'bsonRegExp',
'serializeFunctions',
'ignoreUndefined'
]);
Expand Down
1 change: 1 addition & 0 deletions src/operations/map_reduce.ts
Expand Up @@ -30,6 +30,7 @@ const exclusionList = [
'promoteLongs',
'promoteValues',
'promoteBuffers',
'bsonRegExp',
'serializeFunctions',
'ignoreUndefined',
'scope' // this option is reformatted thus exclude the original
Expand Down
2 changes: 2 additions & 0 deletions test/types/bson.test-d.ts
Expand Up @@ -9,6 +9,7 @@ expectType<boolean | undefined>(options.ignoreUndefined);
expectType<boolean | undefined>(options.promoteLongs);
expectType<boolean | undefined>(options.promoteBuffers);
expectType<boolean | undefined>(options.promoteValues);
expectType<boolean | undefined>(options.bsonRegExp);
expectType<Document | undefined>(options.fieldsAsRaw);

type PermittedBSONOptionKeys =
Expand All @@ -18,6 +19,7 @@ type PermittedBSONOptionKeys =
| 'promoteLongs'
| 'promoteBuffers'
| 'promoteValues'
| 'bsonRegExp'
| 'fieldsAsRaw'
| 'raw';

Expand Down
36 changes: 36 additions & 0 deletions test/unit/bson_regex.test.js
@@ -0,0 +1,36 @@
'use strict';

const { expect } = require('chai');
const { BSONRegExp } = require('../../src/index');

describe('BSONRegExp', () => {
describe('bsonRegExp option', () => {
// define client and option for tests to use
let client;
const option = { bsonRegExp: true };
for (const passOptionTo of ['client', 'db', 'collection', 'operation']) {
it(`should respond with BSONRegExp class with option passed to ${passOptionTo}`, async function () {
try {
client = this.configuration.newClient(passOptionTo === 'client' ? option : undefined);
await client.connect();

const db = client.db('bson_regex_db', passOptionTo === 'db' ? option : undefined);
const collection = db.collection(
'bson_regex_coll',
passOptionTo === 'collection' ? option : undefined
);

await collection.insertOne({ regex: new BSONRegExp('abc', 'imx') });
const res = await collection.findOne(
{ regex: new BSONRegExp('abc', 'imx') },
passOptionTo === 'operation' ? option : undefined
);

expect(res).has.property('regex').that.is.instanceOf(BSONRegExp);
} finally {
await client.close();
}
});
}
});
});

0 comments on commit ca9e2dc

Please sign in to comment.