Skip to content

Commit

Permalink
fix: db.command to not inherit options from parent
Browse files Browse the repository at this point in the history
Fixes `db.command` method to not inherit readPreference, writeConcern, readConcern
from parent. centralizes readPreference helpers translate, resolve, fromOptions to the
ReadPreference class. Adds Aspect "NO_INHERIT_OPTIONS" to command operations
for flagging specific operations. Creates `RunCommand` operation to wrap
`CommandV2`, updating `db.command` to use `CommandV2`.

NODE-2649
  • Loading branch information
Thomas Reggi committed Jul 12, 2020
1 parent eff550d commit 8f6c247
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 12 deletions.
3 changes: 2 additions & 1 deletion src/db.ts
Expand Up @@ -26,6 +26,7 @@ import AggregateOperation = require('./operations/aggregate');
import AddUserOperation = require('./operations/add_user');
import CollectionsOperation = require('./operations/collections');
import CommandOperation = require('./operations/command');
import RunCommandOperation = require('./operations/run_command');
import CreateCollectionOperation = require('./operations/create_collection');
import CreateIndexesOperation = require('./operations/create_indexes');
import { DropCollectionOperation, DropDatabaseOperation } from './operations/drop';
Expand Down Expand Up @@ -241,7 +242,7 @@ class Db {
if (typeof options === 'function') (callback = options), (options = {});
options = Object.assign({}, options);

const commandOperation = new CommandOperation(this, options, null, command);
const commandOperation = new RunCommandOperation(this, command, options);

return executeOperation(this.s.topology, commandOperation, callback);
}
Expand Down
11 changes: 6 additions & 5 deletions src/operations/command_v2.ts
Expand Up @@ -27,9 +27,10 @@ class CommandOperationV2 extends OperationBase {
super(options);

this.ns = parent.s.namespace.withCollection('$cmd');
this.readPreference = ReadPreference.resolve(parent, this.options);
this.readConcern = resolveReadConcern(parent, this.options);
this.writeConcern = resolveWriteConcern(parent, this.options);
const propertyProvider = this.hasAspect(Aspect.NO_INHERIT_OPTIONS) ? undefined : parent;
this.readPreference = ReadPreference.resolve(propertyProvider, this.options);
this.readConcern = resolveReadConcern(propertyProvider, this.options);
this.writeConcern = resolveWriteConcern(propertyProvider, this.options);
this.explain = false;

if (operationOptions && typeof operationOptions.fullResponse === 'boolean') {
Expand Down Expand Up @@ -108,11 +109,11 @@ class CommandOperationV2 extends OperationBase {
}

function resolveWriteConcern(parent: any, options: any) {
return WriteConcern.fromOptions(options) || parent.writeConcern;
return WriteConcern.fromOptions(options) || (parent && parent.writeConcern);
}

function resolveReadConcern(parent: any, options: any) {
return ReadConcern.fromOptions(options) || parent.readConcern;
return ReadConcern.fromOptions(options) || (parent && parent.readConcern);
}

export = CommandOperationV2;
3 changes: 2 additions & 1 deletion src/operations/operation.ts
Expand Up @@ -2,7 +2,8 @@ const Aspect = {
READ_OPERATION: Symbol('READ_OPERATION'),
WRITE_OPERATION: Symbol('WRITE_OPERATION'),
RETRYABLE: Symbol('RETRYABLE'),
EXECUTE_WITH_SELECTION: Symbol('EXECUTE_WITH_SELECTION')
EXECUTE_WITH_SELECTION: Symbol('EXECUTE_WITH_SELECTION'),
NO_INHERIT_OPTIONS: Symbol('NO_INHERIT_OPTIONS')
};

/**
Expand Down
21 changes: 21 additions & 0 deletions src/operations/run_command.ts
@@ -0,0 +1,21 @@
import CommandOperationV2 = require('./command_v2');
import { defineAspects, Aspect } from './operation';
import Db = require('../db');
import Collection = require('../collection');
import MongoClient = require('../mongo_client');
import { Server } from '../sdam/server';

class RunCommandOperation extends CommandOperationV2 {
command: any;
constructor(parent: MongoClient | Db | Collection, command: any, options: any) {
super(parent, options);
this.command = command;
}
execute(server: Server, callback: any) {
const command = this.command;
this.executeCommand(server, command, callback);
}
}
defineAspects(RunCommandOperation, [Aspect.EXECUTE_WITH_SELECTION, Aspect.NO_INHERIT_OPTIONS]);

export = RunCommandOperation;
20 changes: 15 additions & 5 deletions src/read_preference.ts
Expand Up @@ -127,7 +127,7 @@ class ReadPreference {
options = options || {};
const session = options.session;

const inheritedReadPreference = parent.readPreference;
const inheritedReadPreference = parent && parent.readPreference;

let readPreference;
if (options.readPreference) {
Expand All @@ -138,7 +138,7 @@ class ReadPreference {
} else if (inheritedReadPreference != null) {
readPreference = inheritedReadPreference;
} else {
throw new Error('No readPreference was provided or inherited.');
readPreference = ReadPreference.primary;
}

return typeof readPreference === 'string' ? new ReadPreference(readPreference) : readPreference;
Expand All @@ -148,12 +148,22 @@ class ReadPreference {
* Replaces options.readPreference with a ReadPreference instance
*/
static translate(options: any) {
if (options.readPreference == null) return undefined;
if (options.readPreference == null) return options;
const r = options.readPreference;
options.readPreference = ReadPreference.fromOptions(options);
if (!(options.readPreference instanceof ReadPreference)) {

if (typeof r === 'string') {
options.readPreference = new ReadPreference(r);
} else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {
const mode = r.mode || r.preference;
if (mode && typeof mode === 'string') {
options.readPreference = new ReadPreference(mode, r.tags, {
maxStalenessSeconds: r.maxStalenessSeconds
});
}
} else if (!(r instanceof ReadPreference)) {
throw new TypeError('Invalid read preference: ' + r);
}

return options;
}

Expand Down

0 comments on commit 8f6c247

Please sign in to comment.