diff --git a/packages/shell-api/src/mongo.ts b/packages/shell-api/src/mongo.ts index 5fcd655f95..7fe52fde84 100644 --- a/packages/shell-api/src/mongo.ts +++ b/packages/shell-api/src/mongo.ts @@ -21,6 +21,8 @@ import { } from './decorators'; import { ChangeStreamOptions, + ClientSessionOptions, + CommandOperationOptions, Document, generateUri, ListDatabasesOptions, @@ -451,22 +453,37 @@ export default class Mongo extends ShellApiClass { @topologies([Topologies.ReplSet]) startSession(options: Document = {}): Session { - const driverOptions = {}; - if (options === undefined) { - return new Session(this, driverOptions, this._serviceProvider.startSession(driverOptions)); + const allTransactionOptions = [ + 'readConcern', 'writeConcern', 'readPreference', 'maxCommitTimeMS' + ] as const; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + function assertAllTransactionOptionsUsed(_options: (typeof allTransactionOptions)[number]) { + // These typechecks might look weird, but will tell us if we are missing + // support for a newly introduced driver option when it is being added + // to the driver API. + } + assertAllTransactionOptionsUsed('' as Exclude); + const defaultTransactionOptions: TransactionOptions = {}; + for (const key of allTransactionOptions) { + if (typeof options[key] !== 'undefined') { + defaultTransactionOptions[key] = options[key]; + } + } + + const allSessionOptions = [ 'causalConsistency', 'snapshot' ] as const; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + function assertAllSessionOptionsUsed(_options: (typeof allSessionOptions)[number] | 'defaultTransactionOptions') {} + assertAllSessionOptionsUsed('' as keyof ClientSessionOptions); + const driverOptions: ClientSessionOptions = {}; + if (Object.keys(defaultTransactionOptions).length > 0) { + driverOptions.defaultTransactionOptions = defaultTransactionOptions; + } + for (const key of allSessionOptions) { + if (typeof options[key] !== 'undefined') { + driverOptions[key] = options[key]; + } } - const defaultTransactionOptions = {} as TransactionOptions; - // Only include option if not undef - Object.assign(defaultTransactionOptions, - options.readConcern && { readConcern: options.readConcern }, - options.writeConcern && { writeConcern: options.writeConcern }, - options.readPreference && { readPreference: options.readPreference } - ); - Object.assign(driverOptions, - Object.keys(defaultTransactionOptions).length > 0 && { defaultTransactionOptions: defaultTransactionOptions }, - options.causalConsistency !== undefined && { causalConsistency: options.causalConsistency } - ); return new Session(this, driverOptions, this._serviceProvider.startSession(driverOptions)); } diff --git a/packages/shell-api/src/session.spec.ts b/packages/shell-api/src/session.spec.ts index 6c1c912b78..644e39bd8b 100644 --- a/packages/shell-api/src/session.spec.ts +++ b/packages/shell-api/src/session.spec.ts @@ -13,7 +13,7 @@ import { ALL_TOPOLOGIES } from './enums'; import { CliServiceProvider } from '../../service-provider-server'; -import { startTestCluster, skipIfApiStrict } from '../../../testing/integration-testing-hooks'; +import { startTestCluster, skipIfServerVersion, skipIfApiStrict } from '../../../testing/integration-testing-hooks'; import { ensureMaster, ensureSessionExists } from '../../../testing/helpers'; import Database from './database'; import { CommonErrors, MongoshInvalidInputError } from '@mongosh/errors'; @@ -227,6 +227,22 @@ describe('Session', () => { } expect.fail('Error not thrown'); }); + context('with 5.0+ server', () => { + skipIfApiStrict(); + skipIfServerVersion(srv0, '< 5.0'); + it('starts a session with snapshot reads if requested', async() => { + session = mongo.startSession({ snapshot: true }); + await session.getDatabase(databaseName).getCollection('coll').findOne({}); + try { + await session.getDatabase(databaseName).getCollection('coll').insertOne({}); + expect.fail('missed exception'); + } catch (e) { + expect(e.message).to.include('snapshot'); // Cannot do writes with snapshot: true + } + expect(session._session.snapshotEnabled).to.equal(true); + await session.endSession(); + }); + }); }); describe('transaction methods are called', () => { it('cannot call start transaction twice', () => {