Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions packages/i18n/src/locales/en_US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1685,9 +1685,14 @@ const translations: Catalog = {
description: 'Closes a Mongo object, disposing of related resources and closing the underlying connection.'
},
getReadConcern: {
link: 'https://docs.mongodb.com/manual/reference/method/db.getReadConcern',
description: 'Calls the getReadConcern command',
example: 'db.getReadConcern()'
link: 'https://docs.mongodb.com/manual/reference/method/Mongo.getReadConcern',
description: 'Returns the ReadConcern set for the connection.',
example: 'db.getMongo().getReadConcern()'
},
getWriteConcern: {
link: 'https://docs.mongodb.com/manual/reference/method/Mongo.getWriteConcern',
description: 'Returns the WriteConcern set for the connection.',
example: 'db.getMongo().getWriteConcern()'
},
getReadPref: {
link: 'https://docs.mongodb.com/manual/reference/method/Mongo.getReadPref',
Expand All @@ -1714,6 +1719,11 @@ const translations: Catalog = {
description: 'Sets the ReadConcern for the connection',
example: 'db.getMongo().setReadConcern(level)'
},
setWriteConcern: {
link: 'https://docs.mongodb.com/manual/reference/method/Mongo.setWriteConcern',
description: 'Sets the WriteConcern for the connection',
example: 'db.getMongo().setWriteConcern(\'majority\')'
},
setCausalConsistency: {
description: 'This method is deprecated. It is not possible to set causal consistency for an entire connection due to driver limitations, use startSession({causalConsistency: <>}) instead.'
},
Expand Down
12 changes: 12 additions & 0 deletions packages/shell-api/src/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,18 @@ describe('Shell API (integration)', function() {
expect(serviceProvider.mongoClient).to.not.equal(oldMC);
});
});
describe('setWriteConcern', () => {
it('reconnects', async() => {
const oldMC = serviceProvider.mongoClient;
expect(mongo.getWriteConcern()).to.equal(undefined);
await mongo.setWriteConcern('majority', 200);
expect(mongo.getWriteConcern()).to.deep.equal({
w: 'majority',
wtimeout: 200
});
expect(serviceProvider.mongoClient).to.not.equal(oldMC);
});
});
describe('close', () => {
it('removes the connection from the set of connections', async() => {
// eslint-disable-next-line new-cap
Expand Down
55 changes: 54 additions & 1 deletion packages/shell-api/src/mongo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,33 @@ describe('Mongo', () => {
expect.fail();
});
});
describe('getWriteConcern', () => {
it('calls serviceProvider.getWriteConcern', async() => {
const expectedResult: WriteConcern = { w: 'majority', wtimeout: 200 };
serviceProvider.getWriteConcern.returns(expectedResult as any);
const res = mongo.getWriteConcern();
expect(serviceProvider.getWriteConcern).to.have.been.calledWith();
expect(res).to.equal(expectedResult);
});

it('returns undefined if not set', async() => {
serviceProvider.getWriteConcern.returns(undefined);
const res = mongo.getWriteConcern();
expect(serviceProvider.getWriteConcern).to.have.been.calledWith();
expect(res).to.equal(undefined);
});

it('throws InternalError if getWriteConcern errors', async() => {
const expectedError = new Error();
serviceProvider.getWriteConcern.throws(expectedError);
try {
mongo.getWriteConcern();
} catch (catchedError) {
return expect(catchedError).to.be.instanceOf(MongoshInternalError);
}
expect.fail();
});
});
describe('setReadPref', () => {
it('calls serviceProvider.restConnectionOptions', async() => {
serviceProvider.resetConnectionOptions.resolves();
Expand All @@ -394,7 +421,7 @@ describe('Mongo', () => {
});
});
describe('setReadConcern', () => {
it('calls serviceProvider.restConnectionOptions', async() => {
it('calls serviceProvider.resetConnectionOptions', async() => {
serviceProvider.resetConnectionOptions.resolves();
await mongo.setReadConcern('majority');
expect(serviceProvider.resetConnectionOptions).to.have.been.calledWith({
Expand All @@ -415,6 +442,32 @@ describe('Mongo', () => {
expect.fail();
});
});
describe('setWriteConcern', () => {
[
{ args: ['majority'], opts: { w: 'majority' } },
{ args: ['majority', 200], opts: { w: 'majority', wtimeoutMS: 200 } },
{ args: ['majority', 200, false], opts: { w: 'majority', wtimeoutMS: 200, journal: false } },
{ args: ['majority', undefined, false], opts: { w: 'majority', journal: false } },
{ args: [{ w: 'majority', wtimeout: 200, fsync: 1 }], opts: { w: 'majority', wtimeoutMS: 200, journal: true } }
].forEach(({ args, opts }) => {
it(`calls serviceProvider.resetConnectionOptions for args ${JSON.stringify(args)}`, async() => {
serviceProvider.resetConnectionOptions.resolves();
await mongo.setWriteConcern.call(mongo, ...args); // tricking TS into thinking the arguments are correct
expect(serviceProvider.resetConnectionOptions).to.have.been.calledWith(opts);
});
});

it('throws if resetConnectionOptions errors', async() => {
const expectedError = new Error();
serviceProvider.resetConnectionOptions.throws(expectedError);
try {
await mongo.setWriteConcern('majority');
} catch (catchedError) {
return expect(catchedError).to.equal(expectedError);
}
expect.fail();
});
});
describe('startSession', () => {
beforeEach(() => {
serviceProvider.startSession.returns(driverSession as any);
Expand Down
64 changes: 63 additions & 1 deletion packages/shell-api/src/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ import {
MongoClientOptions,
AutoEncryptionOptions as SPAutoEncryption,
ServerApi,
ServerApiVersionId
ServerApiVersionId,
WriteConcern
} from '@mongosh/service-provider-core';
import type Collection from './collection';
import Database from './database';
Expand Down Expand Up @@ -351,6 +352,14 @@ export default class Mongo extends ShellApiClass {
}
}

getWriteConcern(): WriteConcern | undefined {
try {
return this._serviceProvider.getWriteConcern();
} catch {
throw new MongoshInternalError('Error retrieving WriteConcern.');
}
}

@returnsPromise
async setReadPref(mode: ReadPreferenceLike, tagSet?: Record<string, string>[], hedgeOptions?: Document): Promise<void> {
await this._serviceProvider.resetConnectionOptions({
Expand All @@ -368,6 +377,59 @@ export default class Mongo extends ShellApiClass {
await this._serviceProvider.resetConnectionOptions({ readConcern: { level: level } });
}

async setWriteConcern(concern: WriteConcern): Promise<void>
async setWriteConcern(wValue: string | number, wtimeoutMSValue?: number | undefined, jValue?: boolean | undefined): Promise<void>
@returnsPromise
async setWriteConcern(concernOrWValue: WriteConcern | string | number, wtimeoutMSValue?: number | undefined, jValue?: boolean | undefined): Promise<void> {
const options: MongoClientOptions = {};
let concern: WriteConcern;

if (typeof concernOrWValue === 'object') {
if (wtimeoutMSValue !== undefined || jValue !== undefined) {
throw new MongoshInvalidInputError('If concern is given as an object no other arguments must be specified', CommonErrors.InvalidArgument);
}
concern = concernOrWValue;
} else {
concern = {};
if (typeof concernOrWValue !== 'string' && typeof concernOrWValue !== 'number') {
throw new MongoshInvalidInputError(`w value must be a number or string, got: ${typeof concernOrWValue}`, CommonErrors.InvalidArgument);
} else if (typeof concernOrWValue === 'number' && concernOrWValue < 0) {
throw new MongoshInvalidInputError(`w value must be equal to or greather than 0, got: ${concernOrWValue}`, CommonErrors.InvalidArgument);
}
concern.w = concernOrWValue as any;

if (wtimeoutMSValue !== undefined) {
if (typeof wtimeoutMSValue !== 'number') {
throw new MongoshInvalidInputError(`wtimeoutMS value must be a number, got: ${typeof wtimeoutMSValue}`, CommonErrors.InvalidArgument);
} else if (wtimeoutMSValue < 0) {
throw new MongoshInvalidInputError(`wtimeoutMS must be equal to or greather than 0, got: ${wtimeoutMSValue}`, CommonErrors.InvalidArgument);
}
concern.wtimeout = wtimeoutMSValue;
}

if (jValue !== undefined) {
if (typeof jValue !== 'boolean') {
throw new MongoshInvalidInputError(`j value must be a boolean, got: ${typeof jValue}`, CommonErrors.InvalidArgument);
}
concern.j = jValue;
}
}

if (concern.w !== undefined) {
options.w = concern.w;
}
if (concern.wtimeout !== undefined) {
options.wtimeoutMS = concern.wtimeout;
}
if (concern.j !== undefined) {
options.journal = concern.j;
}
if (concern.fsync !== undefined) {
options.journal = !!concern.fsync;
}
await this._serviceProvider.resetConnectionOptions(options);
}

@topologies([Topologies.ReplSet])
startSession(options: Document = {}): Session {
const driverOptions = {};
Expand Down