diff --git a/src/bulk/common.ts b/src/bulk/common.ts index 79a24cd5818..122c6267252 100644 --- a/src/bulk/common.ts +++ b/src/bulk/common.ts @@ -5,7 +5,8 @@ import { AnyError, MONGODB_ERROR_CODES, MongoServerError, - MongoDriverError + MongoDriverError, + MongoInvalidArgumentError } from '../error'; import { applyRetryableWrites, @@ -753,7 +754,7 @@ export class FindOperators { /** Add a single update operation to the bulk operation */ updateOne(updateDocument: Document): BulkOperationBase { if (!hasAtomicOperators(updateDocument)) { - throw new MongoDriverError('Update document requires atomic operators'); + throw new MongoInvalidArgumentError('Update document requires atomic operators'); } const currentOp = buildCurrentOp(this.bulkOperation); @@ -766,7 +767,7 @@ export class FindOperators { /** Add a replace one operation to the bulk operation */ replaceOne(replacement: Document): BulkOperationBase { if (hasAtomicOperators(replacement)) { - throw new MongoDriverError('Replacement document must not use atomic operators'); + throw new MongoInvalidArgumentError('Replacement document must not use atomic operators'); } const currentOp = buildCurrentOp(this.bulkOperation); @@ -1049,7 +1050,7 @@ export abstract class BulkOperationBase { */ find(selector: Document): FindOperators { if (!selector) { - throw new MongoDriverError('Bulk find operation must specify a selector'); + throw new MongoInvalidArgumentError('Bulk find operation must specify a selector'); } // Save a current selector @@ -1083,7 +1084,7 @@ export abstract class BulkOperationBase { if ('replaceOne' in op || 'updateOne' in op || 'updateMany' in op) { if ('replaceOne' in op) { if ('q' in op.replaceOne) { - throw new MongoDriverError('Raw operations are not allowed'); + throw new MongoInvalidArgumentError('Raw operations are not allowed'); } const updateStatement = makeUpdateStatement( op.replaceOne.filter, @@ -1091,35 +1092,35 @@ export abstract class BulkOperationBase { { ...op.replaceOne, multi: false } ); if (hasAtomicOperators(updateStatement.u)) { - throw new MongoDriverError('Replacement document must not use atomic operators'); + throw new MongoInvalidArgumentError('Replacement document must not use atomic operators'); } return this.addToOperationsList(BatchType.UPDATE, updateStatement); } if ('updateOne' in op) { if ('q' in op.updateOne) { - throw new MongoDriverError('Raw operations are not allowed'); + throw new MongoInvalidArgumentError('Raw operations are not allowed'); } const updateStatement = makeUpdateStatement(op.updateOne.filter, op.updateOne.update, { ...op.updateOne, multi: false }); if (!hasAtomicOperators(updateStatement.u)) { - throw new MongoDriverError('Update document requires atomic operators'); + throw new MongoInvalidArgumentError('Update document requires atomic operators'); } return this.addToOperationsList(BatchType.UPDATE, updateStatement); } if ('updateMany' in op) { if ('q' in op.updateMany) { - throw new MongoDriverError('Raw operations are not allowed'); + throw new MongoInvalidArgumentError('Raw operations are not allowed'); } const updateStatement = makeUpdateStatement(op.updateMany.filter, op.updateMany.update, { ...op.updateMany, multi: true }); if (!hasAtomicOperators(updateStatement.u)) { - throw new MongoDriverError('Update document requires atomic operators'); + throw new MongoInvalidArgumentError('Update document requires atomic operators'); } return this.addToOperationsList(BatchType.UPDATE, updateStatement); } @@ -1127,7 +1128,7 @@ export abstract class BulkOperationBase { if ('deleteOne' in op) { if ('q' in op.deleteOne) { - throw new MongoDriverError('Raw operations are not allowed'); + throw new MongoInvalidArgumentError('Raw operations are not allowed'); } return this.addToOperationsList( BatchType.DELETE, @@ -1137,7 +1138,7 @@ export abstract class BulkOperationBase { if ('deleteMany' in op) { if ('q' in op.deleteMany) { - throw new MongoDriverError('Raw operations are not allowed'); + throw new MongoInvalidArgumentError('Raw operations are not allowed'); } return this.addToOperationsList( BatchType.DELETE, diff --git a/src/bulk/ordered.ts b/src/bulk/ordered.ts index 187fa77c2f5..c28e08e719b 100644 --- a/src/bulk/ordered.ts +++ b/src/bulk/ordered.ts @@ -4,7 +4,7 @@ import type { Document } from '../bson'; import type { Collection } from '../collection'; import type { UpdateStatement } from '../operations/update'; import type { DeleteStatement } from '../operations/delete'; -import { MongoDriverError } from '../error'; +import { MongoInvalidArgumentError } from '../error'; /** @public */ export class OrderedBulkOperation extends BulkOperationBase { @@ -26,7 +26,7 @@ export class OrderedBulkOperation extends BulkOperationBase { // Throw error if the doc is bigger than the max BSON size if (bsonSize >= this.s.maxBsonObjectSize) - throw new MongoDriverError( + throw new MongoInvalidArgumentError( `Document is larger than the maximum size ${this.s.maxBsonObjectSize}` ); @@ -68,7 +68,7 @@ export class OrderedBulkOperation extends BulkOperationBase { // We have an array of documents if (Array.isArray(document)) { - throw new MongoDriverError('Operation passed in cannot be an Array'); + throw new MongoInvalidArgumentError('Operation passed in cannot be an Array'); } this.s.currentBatch.originalIndexes.push(this.s.currentIndex); diff --git a/src/change_stream.ts b/src/change_stream.ts index 45deb2b97fc..d74044d002f 100644 --- a/src/change_stream.ts +++ b/src/change_stream.ts @@ -1,5 +1,11 @@ import Denque = require('denque'); -import { MongoError, AnyError, isResumableError, MongoDriverError } from './error'; +import { + MongoError, + AnyError, + isResumableError, + MongoDriverError, + MongoInvalidArgumentError +} from './error'; import { AggregateOperation, AggregateOptions } from './operations/aggregate'; import { maxWireVersion, @@ -255,7 +261,7 @@ export class ChangeStream extends TypedEven } else if (parent instanceof MongoClient) { this.type = CHANGE_DOMAIN_TYPES.CLUSTER; } else { - throw new MongoDriverError( + throw new MongoInvalidArgumentError( 'parent provided to ChangeStream constructor is not an instance of Collection, Db, or MongoClient' ); } @@ -357,7 +363,7 @@ export class ChangeStream extends TypedEven */ stream(options?: CursorStreamOptions): Readable { this.streamOptions = options; - if (!this.cursor) throw new MongoDriverError(NO_CURSOR_ERROR); + if (!this.cursor) throw new MongoInvalidArgumentError(NO_CURSOR_ERROR); return this.cursor.stream(options); } diff --git a/src/cmap/auth/gssapi.ts b/src/cmap/auth/gssapi.ts index af964096951..2823c540290 100644 --- a/src/cmap/auth/gssapi.ts +++ b/src/cmap/auth/gssapi.ts @@ -1,5 +1,10 @@ import { AuthProvider, AuthContext } from './auth_provider'; -import { MongoDriverError, MongoError } from '../../error'; +import { + MongoDriverError, + MongoMissingCredentialsError, + MongoError, + MongoMissingDependencyError +} from '../../error'; import { Kerberos, KerberosClient } from '../../deps'; import { Callback, ns } from '../../utils'; import type { Document } from '../../bson'; @@ -15,7 +20,10 @@ import * as dns from 'dns'; export class GSSAPI extends AuthProvider { auth(authContext: AuthContext, callback: Callback): void { const { connection, credentials } = authContext; - if (credentials == null) return callback(new MongoDriverError('credentials required')); + if (credentials == null) + return callback( + new MongoMissingCredentialsError('credentials required for gssapi authentication') + ); const { username } = credentials; function externalCommand( command: Document, @@ -25,7 +33,7 @@ export class GSSAPI extends AuthProvider { } makeKerberosClient(authContext, (err, client) => { if (err) return callback(err); - if (client == null) return callback(new MongoDriverError('gssapi client missing')); + if (client == null) return callback(new MongoMissingDependencyError('gssapi client missing')); client.step('', (err, payload) => { if (err) return callback(err); diff --git a/src/cmap/auth/mongo_credentials.ts b/src/cmap/auth/mongo_credentials.ts index 7255e936138..945ead7775c 100644 --- a/src/cmap/auth/mongo_credentials.ts +++ b/src/cmap/auth/mongo_credentials.ts @@ -1,7 +1,7 @@ // Resolves the default auth mechanism according to import type { Document } from '../../bson'; -import { MongoDriverError } from '../../error'; +import { MongoDriverError, MongoMissingCredentialsError } from '../../error'; import { AuthMechanism } from './defaultAuthProviders'; // https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst @@ -122,7 +122,7 @@ export class MongoCredentials { this.mechanism === AuthMechanism.MONGODB_SCRAM_SHA256) && !this.username ) { - throw new MongoDriverError(`Username required for mechanism '${this.mechanism}'`); + throw new MongoMissingCredentialsError(`Username required for mechanism '${this.mechanism}'`); } if ( diff --git a/src/cmap/auth/mongocr.ts b/src/cmap/auth/mongocr.ts index 52e9eeb7332..685e1d25e2e 100644 --- a/src/cmap/auth/mongocr.ts +++ b/src/cmap/auth/mongocr.ts @@ -1,13 +1,13 @@ import * as crypto from 'crypto'; import { AuthProvider, AuthContext } from './auth_provider'; import { Callback, ns } from '../../utils'; -import { MongoDriverError } from '../../error'; +import { MongoMissingCredentialsError } from '../../error'; export class MongoCR extends AuthProvider { auth(authContext: AuthContext, callback: Callback): void { const { connection, credentials } = authContext; if (!credentials) { - return callback(new MongoDriverError('AuthContext must provide credentials.')); + return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.')); } const username = credentials.username; const password = credentials.password; diff --git a/src/cmap/auth/mongodb_aws.ts b/src/cmap/auth/mongodb_aws.ts index 7a78c6a8bd6..72739b8936e 100644 --- a/src/cmap/auth/mongodb_aws.ts +++ b/src/cmap/auth/mongodb_aws.ts @@ -4,7 +4,11 @@ import * as url from 'url'; import * as BSON from '../../bson'; import { AuthProvider, AuthContext } from './auth_provider'; import { MongoCredentials } from './mongo_credentials'; -import { MongoDriverError } from '../../error'; +import { + MongoDriverError, + MongoMissingCredentialsError, + MongoCompatibilityError +} from '../../error'; import { maxWireVersion, Callback, ns } from '../../utils'; import type { BSONSerializeOptions } from '../../bson'; @@ -32,7 +36,7 @@ export class MongoDBAWS extends AuthProvider { auth(authContext: AuthContext, callback: Callback): void { const { connection, credentials } = authContext; if (!credentials) { - return callback(new MongoDriverError('AuthContext must provide credentials.')); + return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.')); } if ('kModuleError' in aws4) { @@ -42,7 +46,9 @@ export class MongoDBAWS extends AuthProvider { if (maxWireVersion(connection) < 9) { callback( - new MongoDriverError('MONGODB-AWS authentication requires MongoDB version 4.4 or later') + new MongoCompatibilityError( + 'MONGODB-AWS authentication requires MongoDB version 4.4 or later' + ) ); return; } @@ -149,7 +155,9 @@ interface AWSCredentials { function makeTempCredentials(credentials: MongoCredentials, callback: Callback) { function done(creds: AWSCredentials) { if (!creds.AccessKeyId || !creds.SecretAccessKey || !creds.Token) { - callback(new MongoDriverError('Could not obtain temporary MONGODB-AWS credentials')); + callback( + new MongoMissingCredentialsError('Could not obtain temporary MONGODB-AWS credentials') + ); return; } diff --git a/src/cmap/auth/plain.ts b/src/cmap/auth/plain.ts index d34839c014c..afbca63d203 100644 --- a/src/cmap/auth/plain.ts +++ b/src/cmap/auth/plain.ts @@ -1,13 +1,13 @@ import { Binary } from '../../bson'; import { AuthProvider, AuthContext } from './auth_provider'; -import { MongoDriverError } from '../../error'; +import { MongoMissingCredentialsError } from '../../error'; import { Callback, ns } from '../../utils'; export class Plain extends AuthProvider { auth(authContext: AuthContext, callback: Callback): void { const { connection, credentials } = authContext; if (!credentials) { - return callback(new MongoDriverError('AuthContext must provide credentials.')); + return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.')); } const username = credentials.username; const password = credentials.password; diff --git a/src/cmap/auth/scram.ts b/src/cmap/auth/scram.ts index c19ed572f5d..db5e5d0b88d 100644 --- a/src/cmap/auth/scram.ts +++ b/src/cmap/auth/scram.ts @@ -1,6 +1,12 @@ import * as crypto from 'crypto'; import { Binary, Document } from '../../bson'; -import { AnyError, MongoDriverError, MongoServerError } from '../../error'; +import { + AnyError, + MongoDriverError, + MongoServerError, + MongoInvalidArgumentError, + MongoMissingCredentialsError +} from '../../error'; import { AuthProvider, AuthContext } from './auth_provider'; import { Callback, ns, emitWarning } from '../../utils'; import type { MongoCredentials } from './mongo_credentials'; @@ -103,10 +109,12 @@ function makeFirstMessage( function executeScram(cryptoMethod: CryptoMethod, authContext: AuthContext, callback: Callback) { const { connection, credentials } = authContext; if (!credentials) { - return callback(new MongoDriverError('AuthContext must provide credentials.')); + return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.')); } if (!authContext.nonce) { - return callback(new MongoDriverError('AuthContext must contain a valid nonce property')); + return callback( + new MongoInvalidArgumentError('AuthContext must contain a valid nonce property') + ); } const nonce = authContext.nonce; const db = credentials.source; @@ -131,10 +139,10 @@ function continueScramConversation( const connection = authContext.connection; const credentials = authContext.credentials; if (!credentials) { - return callback(new MongoDriverError('AuthContext must provide credentials.')); + return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.')); } if (!authContext.nonce) { - return callback(new MongoDriverError('Unable to continue SCRAM without valid nonce')); + return callback(new MongoInvalidArgumentError('Unable to continue SCRAM without valid nonce')); } const nonce = authContext.nonce; @@ -240,15 +248,15 @@ function parsePayload(payload: string) { function passwordDigest(username: string, password: string) { if (typeof username !== 'string') { - throw new MongoDriverError('username must be a string'); + throw new MongoInvalidArgumentError('username must be a string'); } if (typeof password !== 'string') { - throw new MongoDriverError('password must be a string'); + throw new MongoInvalidArgumentError('password must be a string'); } if (password.length === 0) { - throw new MongoDriverError('password cannot be empty'); + throw new MongoInvalidArgumentError('password cannot be empty'); } const md5 = crypto.createHash('md5'); diff --git a/src/cmap/auth/x509.ts b/src/cmap/auth/x509.ts index f425a069e2b..73f76880353 100644 --- a/src/cmap/auth/x509.ts +++ b/src/cmap/auth/x509.ts @@ -1,5 +1,5 @@ import { AuthProvider, AuthContext } from './auth_provider'; -import { MongoDriverError } from '../../error'; +import { MongoMissingCredentialsError } from '../../error'; import type { Document } from '../../bson'; import { Callback, ns } from '../../utils'; import type { MongoCredentials } from './mongo_credentials'; @@ -9,7 +9,7 @@ export class X509 extends AuthProvider { prepare(handshakeDoc: HandshakeDocument, authContext: AuthContext, callback: Callback): void { const { credentials } = authContext; if (!credentials) { - return callback(new MongoDriverError('AuthContext must provide credentials.')); + return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.')); } Object.assign(handshakeDoc, { speculativeAuthenticate: x509AuthenticateCommand(credentials) @@ -22,7 +22,7 @@ export class X509 extends AuthProvider { const connection = authContext.connection; const credentials = authContext.credentials; if (!credentials) { - return callback(new MongoDriverError('AuthContext must provide credentials.')); + return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.')); } const response = authContext.response; diff --git a/src/cmap/commands.ts b/src/cmap/commands.ts index 5b9eb7e9233..685af51b59a 100644 --- a/src/cmap/commands.ts +++ b/src/cmap/commands.ts @@ -5,7 +5,7 @@ import { OP_QUERY, OP_GETMORE, OP_KILL_CURSORS, OP_MSG } from './wire_protocol/c import type { Long, Document, BSONSerializeOptions } from '../bson'; import type { ClientSession } from '../sessions'; import type { CommandOptions } from './connection'; -import { MongoDriverError } from '../error'; +import { MongoDriverError, MongoInvalidArgumentError } from '../error'; // Incrementing request id let _requestId = 0; @@ -77,12 +77,12 @@ export class Query { constructor(ns: string, query: Document, options: OpQueryOptions) { // Basic options needed to be passed in - if (ns == null) throw new MongoDriverError('ns must be specified for query'); - if (query == null) throw new MongoDriverError('query must be specified for query'); + if (ns == null) throw new MongoInvalidArgumentError('ns must be specified for query'); + if (query == null) throw new MongoInvalidArgumentError('query must be specified for query'); // Validate that we are not passing 0x00 in the collection name if (ns.indexOf('\x00') !== -1) { - throw new MongoDriverError('namespace cannot contain a null character'); + throw new MongoInvalidArgumentError('namespace cannot contain a null character'); } // Basic options @@ -852,7 +852,7 @@ export class BinMsg { this.index += bsonSize; } else if (payloadType === 1) { // It was decided that no driver makes use of payload type 1 - throw new MongoDriverError('OP_MSG Payload Type 1 detected unsupported protocol'); + throw new MongoInvalidArgumentError('OP_MSG Payload Type 1 detected unsupported protocol'); } } diff --git a/src/cmap/connect.ts b/src/cmap/connect.ts index 0d4246944bb..29404ba725e 100644 --- a/src/cmap/connect.ts +++ b/src/cmap/connect.ts @@ -6,7 +6,9 @@ import { MongoNetworkTimeoutError, AnyError, MongoDriverError, - MongoServerError + MongoCompatibilityError, + MongoServerError, + MongoInvalidArgumentError } from '../error'; import { AUTH_PROVIDERS, AuthMechanism } from './auth/defaultAuthProviders'; import { AuthContext } from './auth/auth_provider'; @@ -58,13 +60,13 @@ function checkSupportedServer(ismaster: Document, options: ConnectionOptions) { const message = `Server at ${options.hostAddress} reports minimum wire version ${JSON.stringify( ismaster.minWireVersion )}, but this version of the Node.js Driver requires at most ${MAX_SUPPORTED_WIRE_VERSION} (MongoDB ${MAX_SUPPORTED_SERVER_VERSION})`; - return new MongoDriverError(message); + return new MongoCompatibilityError(message); } const message = `Server at ${options.hostAddress} reports maximum wire version ${ JSON.stringify(ismaster.maxWireVersion) ?? 0 }, but this version of the Node.js Driver requires at least ${MIN_SUPPORTED_WIRE_VERSION} (MongoDB ${MIN_SUPPORTED_SERVER_VERSION})`; - return new MongoDriverError(message); + return new MongoCompatibilityError(message); } function performInitialHandshake( @@ -85,7 +87,9 @@ function performInitialHandshake( !(credentials.mechanism === AuthMechanism.MONGODB_DEFAULT) && !AUTH_PROVIDERS.get(credentials.mechanism) ) { - callback(new MongoDriverError(`authMechanism '${credentials.mechanism}' not supported`)); + callback( + new MongoCompatibilityError(`authMechanism '${credentials.mechanism}' not supported`) + ); return; } } @@ -230,7 +234,7 @@ export const LEGAL_TCP_SOCKET_OPTIONS = [ function parseConnectOptions(options: ConnectionOptions): SocketConnectOpts { const hostAddress = options.hostAddress; - if (!hostAddress) throw new MongoDriverError('HostAddress required'); + if (!hostAddress) throw new MongoInvalidArgumentError('HostAddress required'); const result: Partial = {}; for (const name of LEGAL_TCP_SOCKET_OPTIONS) { diff --git a/src/cmap/connection_pool.ts b/src/cmap/connection_pool.ts index ac5f894d3eb..3961f479bc3 100644 --- a/src/cmap/connection_pool.ts +++ b/src/cmap/connection_pool.ts @@ -3,7 +3,7 @@ import { Logger } from '../logger'; import { APM_EVENTS, Connection, ConnectionEvents, ConnectionOptions } from './connection'; import { connect } from './connect'; import { eachAsync, makeCounter, Callback } from '../utils'; -import { MongoDriverError, MongoError } from '../error'; +import { MongoDriverError, MongoError, MongoInvalidArgumentError } from '../error'; import { PoolClosedError, WaitQueueTimeoutError } from './errors'; import { ConnectionPoolCreatedEvent, @@ -174,7 +174,7 @@ export class ConnectionPool extends TypedEventEmitter { }); if (this.options.minPoolSize > this.options.maxPoolSize) { - throw new MongoDriverError( + throw new MongoInvalidArgumentError( 'Connection pool minimum size must not be greater than maximum pool size' ); } diff --git a/src/cmap/wire_protocol/shared.ts b/src/cmap/wire_protocol/shared.ts index ffab8d047e4..9582453e308 100644 --- a/src/cmap/wire_protocol/shared.ts +++ b/src/cmap/wire_protocol/shared.ts @@ -1,6 +1,6 @@ import { ServerType } from '../../sdam/common'; import { TopologyDescription } from '../../sdam/topology_description'; -import { MongoDriverError } from '../../error'; +import { MongoInvalidArgumentError } from '../../error'; import { ReadPreference } from '../../read_preference'; import type { Document } from '../../bson'; import type { OpQueryOptions } from '../commands'; @@ -28,7 +28,7 @@ export function getReadPreference(cmd: Document, options?: ReadPreferenceOption) } if (!(readPreference instanceof ReadPreference)) { - throw new MongoDriverError('read preference must be a ReadPreference instance'); + throw new MongoInvalidArgumentError('read preference must be a ReadPreference instance'); } return readPreference; diff --git a/src/collection.ts b/src/collection.ts index 6a8d0305ad7..98dc73463cf 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -8,7 +8,7 @@ import { getTopology } from './utils'; import { Document, BSONSerializeOptions, resolveBSONOptions } from './bson'; -import { MongoDriverError } from './error'; +import { MongoInvalidArgumentError } from './error'; import { UnorderedBulkOperation } from './bulk/unordered'; import { OrderedBulkOperation } from './bulk/ordered'; import { ChangeStream, ChangeStreamOptions } from './change_stream'; @@ -390,7 +390,7 @@ export class Collection { options = options || { ordered: true }; if (!Array.isArray(operations)) { - throw new MongoDriverError('operations must be an array of documents'); + throw new MongoInvalidArgumentError('operations must be an array of documents'); } return executeOperation( @@ -700,7 +700,9 @@ export class Collection { callback?: Callback ): Promise | void { if (callback !== undefined && typeof callback !== 'function') { - throw new MongoDriverError('Third parameter to `findOne()` must be a callback or undefined'); + throw new MongoInvalidArgumentError( + 'Third parameter to `findOne()` must be a callback or undefined' + ); } if (typeof filter === 'function') @@ -730,10 +732,12 @@ export class Collection { find(filter: Filter, options?: FindOptions): FindCursor; find(filter?: Filter, options?: FindOptions): FindCursor { if (arguments.length > 2) { - throw new MongoDriverError('Third parameter to `collection.find()` must be undefined'); + throw new MongoInvalidArgumentError( + 'Third parameter to `collection.find()` must be undefined' + ); } if (typeof options === 'function') { - throw new MongoDriverError('`options` parameter must not be function'); + throw new MongoInvalidArgumentError('`options` parameter must not be function'); } return new FindCursor( @@ -1369,13 +1373,17 @@ export class Collection { options?: AggregateOptions ): AggregationCursor { if (arguments.length > 2) { - throw new MongoDriverError('Third parameter to `collection.aggregate()` must be undefined'); + throw new MongoInvalidArgumentError( + 'Third parameter to `collection.aggregate()` must be undefined' + ); } if (!Array.isArray(pipeline)) { - throw new MongoDriverError('`pipeline` parameter must be an array of aggregation stages'); + throw new MongoInvalidArgumentError( + '`pipeline` parameter must be an array of aggregation stages' + ); } if (typeof options === 'function') { - throw new MongoDriverError('`options` parameter must not be function'); + throw new MongoInvalidArgumentError('`options` parameter must not be function'); } return new AggregationCursor( @@ -1448,7 +1456,7 @@ export class Collection { // Out must always be defined (make sure we don't break weirdly on pre 1.8+ servers) // TODO NODE-3339: Figure out if this is still necessary given we no longer officially support pre-1.8 if (options?.out == null) { - throw new MongoDriverError( + throw new MongoInvalidArgumentError( 'the out option parameter must be defined, see mongodb docs for possible values' ); } diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index e2addc9738a..bf89c8fa0f1 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -1,7 +1,7 @@ import { Callback, maybePromise, MongoDBNamespace, ns } from '../utils'; import { Long, Document, BSONSerializeOptions, pluckBSONSerializeOptions } from '../bson'; import { ClientSession } from '../sessions'; -import { MongoDriverError } from '../error'; +import { MongoDriverError, MongoInvalidArgumentError } from '../error'; import { ReadPreference, ReadPreferenceLike } from '../read_preference'; import type { Server } from '../sdam/server'; import type { Topology } from '../sdam/topology'; @@ -319,7 +319,7 @@ export abstract class AbstractCursor< callback?: Callback ): Promise | void { if (typeof iterator !== 'function') { - throw new MongoDriverError('Missing required parameter `iterator`'); + throw new MongoInvalidArgumentError('Missing required parameter `iterator`'); } return maybePromise(callback, done => { const transform = this[kTransform]; @@ -462,11 +462,11 @@ export abstract class AbstractCursor< addCursorFlag(flag: CursorFlag, value: boolean): this { assertUninitialized(this); if (!CURSOR_FLAGS.includes(flag)) { - throw new MongoDriverError(`flag ${flag} is not one of ${CURSOR_FLAGS}`); + throw new MongoInvalidArgumentError(`flag ${flag} is not one of ${CURSOR_FLAGS}`); } if (typeof value !== 'boolean') { - throw new MongoDriverError(`flag ${flag} must be a boolean value`); + throw new MongoInvalidArgumentError(`flag ${flag} must be a boolean value`); } this[kOptions][flag] = value; @@ -517,7 +517,7 @@ export abstract class AbstractCursor< } else if (typeof readPreference === 'string') { this[kOptions].readPreference = ReadPreference.fromString(readPreference); } else { - throw new MongoDriverError('Invalid read preference: ' + readPreference); + throw new MongoInvalidArgumentError('Invalid read preference: ' + readPreference); } return this; @@ -546,7 +546,7 @@ export abstract class AbstractCursor< maxTimeMS(value: number): this { assertUninitialized(this); if (typeof value !== 'number') { - throw new MongoDriverError('maxTimeMS must be a number'); + throw new MongoInvalidArgumentError('maxTimeMS must be a number'); } this[kOptions].maxTimeMS = value; @@ -565,7 +565,7 @@ export abstract class AbstractCursor< } if (typeof value !== 'number') { - throw new MongoDriverError('batchSize requires an integer'); + throw new MongoInvalidArgumentError('batchSize requires an integer'); } this[kOptions].batchSize = value; diff --git a/src/cursor/find_cursor.ts b/src/cursor/find_cursor.ts index 672ad38b1f6..5b67cd069ef 100644 --- a/src/cursor/find_cursor.ts +++ b/src/cursor/find_cursor.ts @@ -1,5 +1,5 @@ import type { Document } from '../bson'; -import { MongoDriverError } from '../error'; +import { MongoDriverError, MongoInvalidArgumentError } from '../error'; import type { ExplainVerbosityLike } from '../explain'; import { CountOperation, CountOptions } from '../operations/count'; import { executeOperation, ExecutionResult } from '../operations/execute_operation'; @@ -129,7 +129,7 @@ export class FindCursor extends AbstractCursor { callback?: Callback ): Promise | void { if (typeof options === 'boolean') { - throw new MongoDriverError('Invalid first parameter to count'); + throw new MongoInvalidArgumentError('Invalid first parameter to count'); } if (typeof options === 'function') (callback = options), (options = {}); @@ -241,7 +241,7 @@ export class FindCursor extends AbstractCursor { addQueryModifier(name: string, value: string | boolean | number | Document): this { assertUninitialized(this); if (name[0] !== '$') { - throw new MongoDriverError(`${name} is not a valid query modifier`); + throw new MongoInvalidArgumentError(`${name} is not a valid query modifier`); } // Strip of the $ @@ -290,7 +290,7 @@ export class FindCursor extends AbstractCursor { break; default: - throw new MongoDriverError(`invalid query modifier: ${name}`); + throw new MongoInvalidArgumentError(`invalid query modifier: ${name}`); } return this; @@ -315,7 +315,7 @@ export class FindCursor extends AbstractCursor { maxAwaitTimeMS(value: number): this { assertUninitialized(this); if (typeof value !== 'number') { - throw new MongoDriverError('maxAwaitTimeMS must be a number'); + throw new MongoInvalidArgumentError('maxAwaitTimeMS must be a number'); } this[kBuiltOptions].maxAwaitTimeMS = value; @@ -330,7 +330,7 @@ export class FindCursor extends AbstractCursor { maxTimeMS(value: number): this { assertUninitialized(this); if (typeof value !== 'number') { - throw new MongoDriverError('maxTimeMS must be a number'); + throw new MongoInvalidArgumentError('maxTimeMS must be a number'); } this[kBuiltOptions].maxTimeMS = value; @@ -387,7 +387,7 @@ export class FindCursor extends AbstractCursor { allowDiskUse(): this { assertUninitialized(this); if (!this[kBuiltOptions].sort) { - throw new MongoDriverError('allowDiskUse requires a sort specification'); + throw new MongoInvalidArgumentError('allowDiskUse requires a sort specification'); } this[kBuiltOptions].allowDiskUse = true; return this; @@ -416,7 +416,7 @@ export class FindCursor extends AbstractCursor { } if (typeof value !== 'number') { - throw new MongoDriverError('limit requires an integer'); + throw new MongoInvalidArgumentError('limit requires an integer'); } this[kBuiltOptions].limit = value; @@ -435,7 +435,7 @@ export class FindCursor extends AbstractCursor { } if (typeof value !== 'number') { - throw new MongoDriverError('skip requires an integer'); + throw new MongoInvalidArgumentError('skip requires an integer'); } this[kBuiltOptions].skip = value; diff --git a/src/deps.ts b/src/deps.ts index 47ae9d85400..317c7c49933 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -1,4 +1,4 @@ -import { MongoDriverError } from './error'; +import { MongoMissingDependencyError } from './error'; import type { MongoClient } from './mongo_client'; import type { deserialize, Document, serialize } from './bson'; import type { Callback } from './utils'; @@ -20,8 +20,8 @@ function makeErrorModule(error: any) { export let Kerberos: | typeof import('kerberos') - | { kModuleError: MongoDriverError } = makeErrorModule( - new MongoDriverError( + | { kModuleError: MongoMissingDependencyError } = makeErrorModule( + new MongoMissingDependencyError( 'Optional module `kerberos` not found. Please install it to enable kerberos authentication' ) ); @@ -40,8 +40,10 @@ export interface KerberosClient { unwrap: (challenge: string, callback?: Callback) => Promise | void; } -export let Snappy: typeof import('snappy') | { kModuleError: MongoDriverError } = makeErrorModule( - new MongoDriverError( +export let Snappy: + | typeof import('snappy') + | { kModuleError: MongoMissingDependencyError } = makeErrorModule( + new MongoMissingDependencyError( 'Optional module `snappy` not found. Please install it to enable snappy compression' ) ); @@ -52,8 +54,8 @@ try { export let saslprep: | typeof import('saslprep') - | { kModuleError: MongoDriverError } = makeErrorModule( - new MongoDriverError( + | { kModuleError: MongoMissingDependencyError } = makeErrorModule( + new MongoMissingDependencyError( 'Optional module `saslprep` not found.' + ' Please install it to enable Stringprep Profile for User Names and Passwords' ) @@ -63,8 +65,10 @@ try { saslprep = require('saslprep'); } catch {} // eslint-disable-line -export let aws4: typeof import('aws4') | { kModuleError: MongoDriverError } = makeErrorModule( - new MongoDriverError( +export let aws4: + | typeof import('aws4') + | { kModuleError: MongoMissingDependencyError } = makeErrorModule( + new MongoMissingDependencyError( 'Optional module `aws4` not found. Please install it to enable AWS authentication' ) ); diff --git a/src/encrypter.ts b/src/encrypter.ts index 0665d1166de..40e3ee6e035 100644 --- a/src/encrypter.ts +++ b/src/encrypter.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-var-requires */ import { MongoClient, MongoClientOptions } from './mongo_client'; import type { AutoEncrypter, AutoEncryptionOptions } from './deps'; -import { MongoDriverError } from './error'; +import { MongoDriverError, MongoMissingDependencyError } from './error'; import { deserialize, serialize } from './bson'; import type { Callback } from './utils'; import { MONGO_CLIENT_EVENTS } from './operations/connect'; @@ -106,7 +106,7 @@ export class Encrypter { try { require.resolve('mongodb-client-encryption'); } catch (err) { - throw new MongoDriverError( + throw new MongoMissingDependencyError( 'Auto-encryption requested, but the module is not installed. ' + 'Please add `mongodb-client-encryption` as a dependency of your project' ); @@ -114,7 +114,7 @@ export class Encrypter { const mongodbClientEncryption = require('mongodb-client-encryption'); if (typeof mongodbClientEncryption.extension !== 'function') { - throw new MongoDriverError( + throw new MongoMissingDependencyError( 'loaded version of `mongodb-client-encryption` does not have property `extension`. ' + 'Please make sure you are loading the correct version of `mongodb-client-encryption`' ); diff --git a/src/error.ts b/src/error.ts index eb79d8bd2e8..53bef462c2d 100644 --- a/src/error.ts +++ b/src/error.ts @@ -245,6 +245,95 @@ export class MongoParseError extends MongoDriverError { } } +/** + * An error generated when the user misuses the driver API + * + * @privateRemarks + * Should **never** be directly instantiated + * + * @public + * @category Error + */ + +export class MongoAPIError extends MongoDriverError { + protected constructor(message: string) { + super(message); + } + + get name(): string { + return 'MongoAPIError'; + } +} + +/** + * An error generated when the user supplies malformed or unexpected arguments + * or when a required argument or field is not provided. + * + * + * @public + * @category Error + */ +export class MongoInvalidArgumentError extends MongoAPIError { + constructor(message: string) { + super(message); + } + + get name(): string { + return 'MongoInvalidArgumentError'; + } +} + +/** + * An error generated when a feature that is not enabled or allowed for the current server + * configuration is used + * + * + * @public + * @category Error + */ +export class MongoCompatibilityError extends MongoAPIError { + constructor(message: string) { + super(message); + } + + get name(): string { + return 'MongoCompatibilityError'; + } +} + +/** + * An error generated when the user fails to provide authentication credentials before attempting + * to connect to a mongo server instance. + * + * + * @public + * @category Error + */ +export class MongoMissingCredentialsError extends MongoAPIError { + constructor(message: string) { + super(message); + } + + get name(): string { + return 'MongoMissingCredentialsError'; + } +} + +/** + * An error generated when a required module or dependency is not present in the local environment + * + * @public + * @category Error + */ +export class MongoMissingDependencyError extends MongoAPIError { + constructor(message: string) { + super(message); + } + + get name(): string { + return 'MongoMissingDependencyError'; + } +} /** * An error signifying a general system issue * @public diff --git a/src/logger.ts b/src/logger.ts index 29bb3c16bf4..dfa8172823c 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,5 +1,5 @@ import { format } from 'util'; -import { MongoDriverError } from './error'; +import { MongoInvalidArgumentError } from './error'; // Filters for classes const classFilters: any = {}; @@ -222,7 +222,7 @@ export class Logger { */ static setCurrentLogger(logger: LoggerFunction): void { if (typeof logger !== 'function') { - throw new MongoDriverError('current logger must be a function'); + throw new MongoInvalidArgumentError('current logger must be a function'); } currentLogger = logger; @@ -253,7 +253,7 @@ export class Logger { newLevel !== LoggerLevel.DEBUG && newLevel !== LoggerLevel.WARN ) { - throw new MongoDriverError(`${newLevel} is an illegal logging level`); + throw new MongoInvalidArgumentError(`${newLevel} is an illegal logging level`); } level = newLevel; diff --git a/src/mongo_client.ts b/src/mongo_client.ts index 34ec5b56edc..c641971d12b 100644 --- a/src/mongo_client.ts +++ b/src/mongo_client.ts @@ -1,7 +1,7 @@ import { Db, DbOptions } from './db'; import { ChangeStream, ChangeStreamOptions } from './change_stream'; import type { ReadPreference, ReadPreferenceMode } from './read_preference'; -import { AnyError, MongoDriverError } from './error'; +import { AnyError, MongoDriverError, MongoInvalidArgumentError } from './error'; import type { W, WriteConcern } from './write_concern'; import { maybePromise, @@ -403,7 +403,7 @@ export class MongoClient extends TypedEventEmitter { connect(callback: Callback): void; connect(callback?: Callback): Promise | void { if (callback && typeof callback !== 'function') { - throw new MongoDriverError('`connect` only accepts a callback'); + throw new MongoInvalidArgumentError('`connect` only accepts a callback'); } return maybePromise(callback, cb => { @@ -556,7 +556,7 @@ export class MongoClient extends TypedEventEmitter { } if (callback == null) { - throw new MongoDriverError('Missing required callback parameter'); + throw new MongoInvalidArgumentError('Missing required callback parameter'); } const session = this.startSession(options); diff --git a/src/operations/add_user.ts b/src/operations/add_user.ts index ed51140dff3..5181951eb75 100644 --- a/src/operations/add_user.ts +++ b/src/operations/add_user.ts @@ -1,7 +1,7 @@ import * as crypto from 'crypto'; import { Aspect, defineAspects } from './operation'; import { CommandOperation, CommandOperationOptions } from './command'; -import { MongoDriverError } from '../error'; +import { MongoInvalidArgumentError } from '../error'; import { Callback, emitWarningOnce, getTopology } from '../utils'; import type { Document } from '../bson'; import type { Server } from '../sdam/server'; @@ -54,7 +54,7 @@ export class AddUserOperation extends CommandOperation { // Error out if digestPassword set if (options.digestPassword != null) { return callback( - new MongoDriverError( + new MongoInvalidArgumentError( 'The digestPassword option is not supported via add_user. ' + "Please use db.command('createUser', ...) instead for this option." ) diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index 73b83821c83..8c9688203dd 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -5,7 +5,7 @@ import { CollationOptions } from './command'; import { ReadPreference } from '../read_preference'; -import { MongoDriverError } from '../error'; +import { MongoInvalidArgumentError } from '../error'; import { maxWireVersion } from '../utils'; import { Aspect, defineAspects, Hint } from './operation'; import type { Callback } from '../utils'; @@ -75,11 +75,13 @@ export class AggregateOperation extends CommandOperation { } if (this.explain && this.writeConcern) { - throw new MongoDriverError('"explain" cannot be used on an aggregate call with writeConcern'); + throw new MongoInvalidArgumentError( + '"explain" cannot be used on an aggregate call with writeConcern' + ); } if (options?.cursor != null && typeof options.cursor !== 'object') { - throw new MongoDriverError('cursor options must be an object'); + throw new MongoInvalidArgumentError('cursor options must be an object'); } } diff --git a/src/operations/command.ts b/src/operations/command.ts index 9945ba0aa6b..0d51b94f2d2 100644 --- a/src/operations/command.ts +++ b/src/operations/command.ts @@ -4,7 +4,7 @@ import { WriteConcern, WriteConcernOptions } from '../write_concern'; import { maxWireVersion, MongoDBNamespace, Callback, decorateWithExplain } from '../utils'; import type { ReadPreference } from '../read_preference'; import { ClientSession, commandSupportsReadConcern } from '../sessions'; -import { MongoDriverError } from '../error'; +import { MongoInvalidArgumentError, MongoCompatibilityError } from '../error'; import type { Logger } from '../logger'; import type { Server } from '../sdam/server'; import type { BSONSerializeOptions, Document } from '../bson'; @@ -98,7 +98,7 @@ export abstract class CommandOperation extends AbstractOperation { if (this.hasAspect(Aspect.EXPLAINABLE)) { this.explain = Explain.fromOptions(options); } else if (options?.explain !== undefined) { - throw new MongoDriverError(`explain is not supported on this command`); + throw new MongoInvalidArgumentError(`explain is not supported on this command`); } } @@ -131,7 +131,7 @@ export abstract class CommandOperation extends AbstractOperation { if (options.collation && serverWireVersion < SUPPORTS_WRITE_CONCERN_AND_COLLATION) { callback( - new MongoDriverError( + new MongoCompatibilityError( `Server ${server.name}, which reports wire version ${serverWireVersion}, does not support collation` ) ); diff --git a/src/operations/connect.ts b/src/operations/connect.ts index 739a3d632a5..5109782da6d 100644 --- a/src/operations/connect.ts +++ b/src/operations/connect.ts @@ -1,4 +1,4 @@ -import { MongoDriverError } from '../error'; +import { MongoDriverError, MongoInvalidArgumentError } from '../error'; import { Topology, TOPOLOGY_EVENTS } from '../sdam/topology'; import { resolveSRVRecord } from '../connection_string'; import type { Callback } from '../utils'; @@ -21,7 +21,7 @@ export function connect( callback: Callback ): void { if (!callback) { - throw new MongoDriverError('no callback function provided'); + throw new MongoInvalidArgumentError('no callback function provided'); } // If a connection already been established, we can terminate early diff --git a/src/operations/delete.ts b/src/operations/delete.ts index 664562741d6..517a2f9f1c0 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -5,7 +5,7 @@ import type { Document } from '../bson'; import type { Server } from '../sdam/server'; import type { Collection } from '../collection'; import type { ClientSession } from '../sessions'; -import { MongoDriverError, MongoServerError } from '../error'; +import { MongoServerError, MongoCompatibilityError } from '../error'; import type { WriteConcernOptions } from '../write_concern'; /** @public */ @@ -82,21 +82,23 @@ export class DeleteOperation extends CommandOperation { if (options.explain !== undefined && maxWireVersion(server) < 3) { return callback - ? callback(new MongoDriverError(`server ${server.name} does not support explain on delete`)) + ? callback( + new MongoCompatibilityError(`server ${server.name} does not support explain on delete`) + ) : undefined; } const unacknowledgedWrite = this.writeConcern && this.writeConcern.w === 0; if (unacknowledgedWrite || maxWireVersion(server) < 5) { if (this.statements.find((o: Document) => o.hint)) { - callback(new MongoDriverError(`servers < 3.4 do not support hint on delete`)); + callback(new MongoCompatibilityError(`servers < 3.4 do not support hint on delete`)); return; } } const statementWithCollation = this.statements.find(statement => !!statement.collation); if (statementWithCollation && collationNotSupported(server, statementWithCollation)) { - callback(new MongoDriverError(`server ${server.name} does not support collation`)); + callback(new MongoCompatibilityError(`server ${server.name} does not support collation`)); return; } diff --git a/src/operations/distinct.ts b/src/operations/distinct.ts index 247e41115a9..7beffc5132a 100644 --- a/src/operations/distinct.ts +++ b/src/operations/distinct.ts @@ -4,7 +4,7 @@ import { decorateWithCollation, decorateWithReadConcern, Callback, maxWireVersio import type { Document } from '../bson'; import type { Server } from '../sdam/server'; import type { Collection } from '../collection'; -import { MongoDriverError } from '../error'; +import { MongoCompatibilityError } from '../error'; import type { ClientSession } from '../sessions'; /** @public */ @@ -68,7 +68,9 @@ export class DistinctOperation extends CommandOperation { } if (this.explain && maxWireVersion(server) < 4) { - callback(new MongoDriverError(`server ${server.name} does not support explain on distinct`)); + callback( + new MongoCompatibilityError(`server ${server.name} does not support explain on distinct`) + ); return; } diff --git a/src/operations/execute_operation.ts b/src/operations/execute_operation.ts index b9880c4688b..37d0c7035ca 100644 --- a/src/operations/execute_operation.ts +++ b/src/operations/execute_operation.ts @@ -4,6 +4,7 @@ import { isRetryableError, MONGODB_ERROR_CODES, MongoDriverError, + MongoCompatibilityError, MongoServerError } from '../error'; import { Aspect, AbstractOperation } from './operation'; @@ -85,12 +86,13 @@ export function executeOperation< owner = Symbol(); session = topology.startSession({ owner, explicit: false }); } else if (session.hasEnded) { + // TODO: Change this out for MongoExpiredSessionError return cb(new MongoDriverError('Use of expired sessions is not permitted')); } } else if (session) { // If the user passed an explicit session and we are still, after server selection, // trying to run against a topology that doesn't support sessions we error out. - return cb(new MongoDriverError('Current topology does not support sessions')); + return cb(new MongoCompatibilityError('Current topology does not support sessions')); } try { @@ -126,6 +128,7 @@ function executeWithServerSelection( if (inTransaction && !readPreference.equals(ReadPreference.primary)) { callback( + // TODO: Change this out for MongoTransactionError new MongoDriverError( `Read preference in a transaction must be primary, not: ${readPreference.mode}` ) @@ -187,6 +190,7 @@ function executeWithServerSelection( session.inTransaction() ) { callback( + // TODO: Change this out for MongoTransactionError new MongoDriverError( `Read preference in a transaction must be primary, not: ${readPreference.mode}` ) diff --git a/src/operations/find.ts b/src/operations/find.ts index 2cc21ae75f0..70eff08523f 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -6,7 +6,7 @@ import { normalizeHintField, decorateWithExplain } from '../utils'; -import { MongoDriverError } from '../error'; +import { MongoInvalidArgumentError, MongoCompatibilityError } from '../error'; import type { Document } from '../bson'; import type { Server } from '../sdam/server'; import type { Collection } from '../collection'; @@ -84,14 +84,14 @@ export class FindOperation extends CommandOperation { this.ns = ns; if (typeof filter !== 'object' || Array.isArray(filter)) { - throw new MongoDriverError('Query filter must be a plain object or ObjectId'); + throw new MongoInvalidArgumentError('Query filter must be a plain object or ObjectId'); } // If the filter is a buffer, validate that is a valid BSON document if (Buffer.isBuffer(filter)) { const objectSize = filter[0] | (filter[1] << 8) | (filter[2] << 16) | (filter[3] << 24); if (objectSize !== filter.length) { - throw new MongoDriverError( + throw new MongoInvalidArgumentError( `query filter raw message size does not match message header size [${filter.length}] != [${objectSize}]` ); } @@ -107,13 +107,15 @@ export class FindOperation extends CommandOperation { const serverWireVersion = maxWireVersion(server); const options = this.options; if (typeof options.allowDiskUse !== 'undefined' && serverWireVersion < 4) { - callback(new MongoDriverError('The `allowDiskUse` option is not supported on MongoDB < 3.2')); + callback( + new MongoCompatibilityError('The `allowDiskUse` option is not supported on MongoDB < 3.2') + ); return; } if (options.collation && serverWireVersion < SUPPORTS_WRITE_CONCERN_AND_COLLATION) { callback( - new MongoDriverError( + new MongoCompatibilityError( `Server ${server.name}, which reports wire version ${serverWireVersion}, does not support collation` ) ); @@ -124,7 +126,7 @@ export class FindOperation extends CommandOperation { if (serverWireVersion < 4) { if (this.readConcern && this.readConcern.level !== 'local') { callback( - new MongoDriverError( + new MongoCompatibilityError( `server find command does not support a readConcern level of ${this.readConcern.level}` ) ); diff --git a/src/operations/find_and_modify.ts b/src/operations/find_and_modify.ts index dcc9cb9e987..796b57ec9a8 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -1,6 +1,6 @@ import { ReadPreference } from '../read_preference'; import { maxWireVersion, decorateWithCollation, hasAtomicOperators, Callback } from '../utils'; -import { MongoDriverError } from '../error'; +import { MongoInvalidArgumentError, MongoCompatibilityError } from '../error'; import { CommandOperation, CommandOperationOptions } from './command'; import { defineAspects, Aspect } from './operation'; import type { Document } from '../bson'; @@ -172,7 +172,7 @@ class FindAndModifyOperation extends CommandOperation { const unacknowledgedWrite = this.writeConcern?.w === 0; if (unacknowledgedWrite || maxWireVersion(server) < 8) { callback( - new MongoDriverError( + new MongoCompatibilityError( 'The current topology does not support a hint on findAndModify commands' ) ); @@ -185,7 +185,9 @@ class FindAndModifyOperation extends CommandOperation { if (this.explain && maxWireVersion(server) < 4) { callback( - new MongoDriverError(`server ${server.name} does not support explain on findAndModify`) + new MongoCompatibilityError( + `server ${server.name} does not support explain on findAndModify` + ) ); return; } @@ -203,7 +205,7 @@ export class FindOneAndDeleteOperation extends FindAndModifyOperation { constructor(collection: Collection, filter: Document, options: FindOneAndDeleteOptions) { // Basic validation if (filter == null || typeof filter !== 'object') { - throw new MongoDriverError('Filter parameter must be an object'); + throw new MongoInvalidArgumentError('Filter parameter must be an object'); } super(collection, filter, options); @@ -220,15 +222,15 @@ export class FindOneAndReplaceOperation extends FindAndModifyOperation { options: FindOneAndReplaceOptions ) { if (filter == null || typeof filter !== 'object') { - throw new MongoDriverError('Filter parameter must be an object'); + throw new MongoInvalidArgumentError('Filter parameter must be an object'); } if (replacement == null || typeof replacement !== 'object') { - throw new MongoDriverError('Replacement parameter must be an object'); + throw new MongoInvalidArgumentError('Replacement parameter must be an object'); } if (hasAtomicOperators(replacement)) { - throw new MongoDriverError('Replacement document must not contain atomic operators'); + throw new MongoInvalidArgumentError('Replacement document must not contain atomic operators'); } super(collection, filter, options); @@ -246,15 +248,15 @@ export class FindOneAndUpdateOperation extends FindAndModifyOperation { options: FindOneAndUpdateOptions ) { if (filter == null || typeof filter !== 'object') { - throw new MongoDriverError('Filter parameter must be an object'); + throw new MongoInvalidArgumentError('Filter parameter must be an object'); } if (update == null || typeof update !== 'object') { - throw new MongoDriverError('Update parameter must be an object'); + throw new MongoInvalidArgumentError('Update parameter must be an object'); } if (!hasAtomicOperators(update)) { - throw new MongoDriverError('Update document requires atomic operators'); + throw new MongoInvalidArgumentError('Update document requires atomic operators'); } super(collection, filter, options); diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index 6d9bd2fa158..a338b143a12 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -1,6 +1,6 @@ import { indexInformation, IndexInformationOptions } from './common_functions'; import { AbstractOperation, Aspect, defineAspects } from './operation'; -import { MONGODB_ERROR_CODES, MongoDriverError, MongoServerError } from '../error'; +import { MONGODB_ERROR_CODES, MongoServerError, MongoCompatibilityError } from '../error'; import { maxWireVersion, parseIndexOptions, @@ -188,7 +188,7 @@ export class CreateIndexesOperation< // Did the user pass in a collation, check if our write server supports it if (indexes[i].collation && serverWireVersion < 5) { callback( - new MongoDriverError( + new MongoCompatibilityError( `Server ${server.name}, which reports wire version ${serverWireVersion}, ` + 'does not support collation' ) @@ -213,7 +213,7 @@ export class CreateIndexesOperation< if (options.commitQuorum != null) { if (serverWireVersion < 9) { callback( - new MongoDriverError( + new MongoCompatibilityError( '`commitQuorum` option for `createIndexes` not supported on servers < 4.4' ) ); diff --git a/src/operations/insert.ts b/src/operations/insert.ts index 787e6faa98b..1f4c3453fb6 100644 --- a/src/operations/insert.ts +++ b/src/operations/insert.ts @@ -1,4 +1,4 @@ -import { MongoDriverError, MongoServerError } from '../error'; +import { MongoServerError, MongoInvalidArgumentError } from '../error'; import { defineAspects, Aspect, AbstractOperation } from './operation'; import { CommandOperation, CommandOperationOptions } from './command'; import { prepareDocs } from './common_functions'; @@ -100,7 +100,7 @@ export class InsertManyOperation extends AbstractOperation { super(options); if (!Array.isArray(docs)) { - throw new MongoDriverError('docs parameter must be an array of documents'); + throw new MongoInvalidArgumentError('docs parameter must be an array of documents'); } this.options = options; diff --git a/src/operations/map_reduce.ts b/src/operations/map_reduce.ts index 6f8540e736d..c8493b8e32c 100644 --- a/src/operations/map_reduce.ts +++ b/src/operations/map_reduce.ts @@ -12,7 +12,7 @@ import { CommandOperation, CommandOperationOptions } from './command'; import type { Server } from '../sdam/server'; import type { Collection } from '../collection'; import type { Sort } from '../sort'; -import { MongoDriverError, MongoServerError } from '../error'; +import { MongoServerError, MongoCompatibilityError } from '../error'; import type { ObjectId } from '../bson'; import { Aspect, defineAspects } from './operation'; import type { ClientSession } from '../sessions'; @@ -165,7 +165,9 @@ export class MapReduceOperation extends CommandOperation } if (this.explain && maxWireVersion(server) < 9) { - callback(new MongoDriverError(`server ${server.name} does not support explain on mapReduce`)); + callback( + new MongoCompatibilityError(`server ${server.name} does not support explain on mapReduce`) + ); return; } diff --git a/src/operations/set_profiling_level.ts b/src/operations/set_profiling_level.ts index ade1e6fe5da..93006ef9c48 100644 --- a/src/operations/set_profiling_level.ts +++ b/src/operations/set_profiling_level.ts @@ -3,7 +3,7 @@ import type { Callback } from '../utils'; import type { Server } from '../sdam/server'; import type { Db } from '../db'; import type { ClientSession } from '../sessions'; -import { MongoDriverError } from '../error'; +import { MongoDriverError, MongoInvalidArgumentError } from '../error'; const levelValues = new Set(['off', 'slow_only', 'all']); /** @public */ @@ -50,7 +50,7 @@ export class SetProfilingLevelOperation extends CommandOperation const level = this.level; if (!levelValues.has(level)) { - return callback(new MongoDriverError('Error: illegal profiling level value ' + level)); + return callback(new MongoInvalidArgumentError('Illegal profiling level value ' + level)); } super.executeCommand(server, session, { profile: this.profile }, (err, doc) => { diff --git a/src/operations/update.ts b/src/operations/update.ts index 1cecf4ea6da..2f78c1ecc35 100644 --- a/src/operations/update.ts +++ b/src/operations/update.ts @@ -11,7 +11,7 @@ import type { Server } from '../sdam/server'; import type { Collection } from '../collection'; import type { ObjectId, Document } from '../bson'; import type { ClientSession } from '../sessions'; -import { MongoDriverError, MongoServerError } from '../error'; +import { MongoServerError, MongoInvalidArgumentError, MongoCompatibilityError } from '../error'; /** @public */ export interface UpdateOptions extends CommandOperationOptions { @@ -108,25 +108,27 @@ export class UpdateOperation extends CommandOperation { collationNotSupported(server, options) || (statementWithCollation && collationNotSupported(server, statementWithCollation)) ) { - callback(new MongoDriverError(`server ${server.name} does not support collation`)); + callback(new MongoCompatibilityError(`server ${server.name} does not support collation`)); return; } const unacknowledgedWrite = this.writeConcern && this.writeConcern.w === 0; if (unacknowledgedWrite || maxWireVersion(server) < 5) { if (this.statements.find((o: Document) => o.hint)) { - callback(new MongoDriverError(`servers < 3.4 do not support hint on update`)); + callback(new MongoCompatibilityError(`servers < 3.4 do not support hint on update`)); return; } } if (this.explain && maxWireVersion(server) < 3) { - callback(new MongoDriverError(`server ${server.name} does not support explain on update`)); + callback( + new MongoCompatibilityError(`server ${server.name} does not support explain on update`) + ); return; } if (this.statements.some(statement => !!statement.arrayFilters) && maxWireVersion(server) < 6) { - callback(new MongoDriverError('arrayFilters are only supported on MongoDB 3.6+')); + callback(new MongoCompatibilityError('arrayFilters are only supported on MongoDB 3.6+')); return; } @@ -144,7 +146,7 @@ export class UpdateOneOperation extends UpdateOperation { ); if (!hasAtomicOperators(update)) { - throw new MongoDriverError('Update document requires atomic operators'); + throw new MongoInvalidArgumentError('Update document requires atomic operators'); } } @@ -181,7 +183,7 @@ export class UpdateManyOperation extends UpdateOperation { ); if (!hasAtomicOperators(update)) { - throw new MongoDriverError('Update document requires atomic operators'); + throw new MongoInvalidArgumentError('Update document requires atomic operators'); } } @@ -235,7 +237,7 @@ export class ReplaceOneOperation extends UpdateOperation { ); if (hasAtomicOperators(replacement)) { - throw new MongoDriverError('Replacement document must not contain atomic operators'); + throw new MongoInvalidArgumentError('Replacement document must not contain atomic operators'); } } @@ -268,11 +270,11 @@ export function makeUpdateStatement( options: UpdateOptions & { multi?: boolean } ): UpdateStatement { if (filter == null || typeof filter !== 'object') { - throw new MongoDriverError('selector must be a valid JavaScript object'); + throw new MongoInvalidArgumentError('selector must be a valid JavaScript object'); } if (update == null || typeof update !== 'object') { - throw new MongoDriverError('document must be a valid JavaScript object'); + throw new MongoInvalidArgumentError('document must be a valid JavaScript object'); } const op: UpdateStatement = { q: filter, u: update }; diff --git a/src/promise_provider.ts b/src/promise_provider.ts index 08d9b9231db..a6aeb548eb3 100644 --- a/src/promise_provider.ts +++ b/src/promise_provider.ts @@ -1,4 +1,4 @@ -import { MongoDriverError } from './error'; +import { MongoInvalidArgumentError } from './error'; /** @internal */ const kPromise = Symbol('promise'); @@ -19,7 +19,7 @@ export class PromiseProvider { /** Validates the passed in promise library */ static validate(lib: unknown): lib is PromiseConstructor { if (typeof lib !== 'function') - throw new MongoDriverError(`Promise must be a function, got ${lib}`); + throw new MongoInvalidArgumentError(`Promise must be a function, got ${lib}`); return !!lib; } diff --git a/src/read_preference.ts b/src/read_preference.ts index 1f2c8e0b2e8..fb8ea17ee55 100644 --- a/src/read_preference.ts +++ b/src/read_preference.ts @@ -1,7 +1,7 @@ import type { TagSet } from './sdam/server_description'; import type { Document } from './bson'; import type { ClientSession } from './sessions'; -import { MongoDriverError } from './error'; +import { MongoInvalidArgumentError } from './error'; /** @public */ export type ReadPreferenceLike = ReadPreference | ReadPreferenceMode; @@ -84,13 +84,13 @@ export class ReadPreference { */ constructor(mode: ReadPreferenceMode, tags?: TagSet[], options?: ReadPreferenceOptions) { if (!ReadPreference.isValid(mode)) { - throw new MongoDriverError(`Invalid read preference mode ${JSON.stringify(mode)}`); + throw new MongoInvalidArgumentError(`Invalid read preference mode ${JSON.stringify(mode)}`); } if (options === undefined && typeof tags === 'object' && !Array.isArray(tags)) { options = tags; tags = undefined; } else if (tags && !Array.isArray(tags)) { - throw new MongoDriverError('ReadPreference tags must be an array'); + throw new MongoInvalidArgumentError('ReadPreference tags must be an array'); } this.mode = mode; @@ -102,7 +102,7 @@ export class ReadPreference { options = options ?? {}; if (options.maxStalenessSeconds != null) { if (options.maxStalenessSeconds <= 0) { - throw new MongoDriverError('maxStalenessSeconds must be a positive integer'); + throw new MongoInvalidArgumentError('maxStalenessSeconds must be a positive integer'); } this.maxStalenessSeconds = options.maxStalenessSeconds; @@ -114,17 +114,19 @@ export class ReadPreference { if (this.mode === ReadPreference.PRIMARY) { if (this.tags && Array.isArray(this.tags) && this.tags.length > 0) { - throw new MongoDriverError('Primary read preference cannot be combined with tags'); + throw new MongoInvalidArgumentError('Primary read preference cannot be combined with tags'); } if (this.maxStalenessSeconds) { - throw new MongoDriverError( + throw new MongoInvalidArgumentError( 'Primary read preference cannot be combined with maxStalenessSeconds' ); } if (this.hedge) { - throw new MongoDriverError('Primary read preference cannot be combined with hedge'); + throw new MongoInvalidArgumentError( + 'Primary read preference cannot be combined with hedge' + ); } } } @@ -193,7 +195,7 @@ export class ReadPreference { }); } } else if (!(r instanceof ReadPreference)) { - throw new MongoDriverError('Invalid read preference: ' + r); + throw new MongoInvalidArgumentError('Invalid read preference: ' + r); } return options; diff --git a/src/sdam/server.ts b/src/sdam/server.ts index 76acdcd4613..d05eac34402 100644 --- a/src/sdam/server.ts +++ b/src/sdam/server.ts @@ -33,7 +33,9 @@ import { isRetryableWriteError, isNodeShuttingDownError, isNetworkErrorBeforeHandshake, - MongoDriverError + MongoDriverError, + MongoCompatibilityError, + MongoInvalidArgumentError } from '../error'; import { Connection, @@ -260,15 +262,15 @@ export class Server extends TypedEventEmitter { } if (callback == null) { - throw new MongoDriverError('callback must be provided'); + throw new MongoInvalidArgumentError('callback must be provided'); } if (ns.db == null || typeof ns === 'string') { - throw new MongoDriverError('ns must not be a string'); + throw new MongoInvalidArgumentError('ns must not be a string'); } if (this.s.state === STATE_CLOSING || this.s.state === STATE_CLOSED) { - callback(new MongoDriverError('server is closed')); + callback(new MongoInvalidArgumentError('server is closed')); return; } @@ -277,7 +279,7 @@ export class Server extends TypedEventEmitter { // error if collation not supported if (collationNotSupported(this, cmd)) { - callback(new MongoDriverError(`server ${this.name} does not support collation`)); + callback(new MongoCompatibilityError(`server ${this.name} does not support collation`)); return; } diff --git a/src/sdam/server_selection.ts b/src/sdam/server_selection.ts index 9f85c7686db..654416412be 100644 --- a/src/sdam/server_selection.ts +++ b/src/sdam/server_selection.ts @@ -1,6 +1,6 @@ import { ServerType, TopologyType } from './common'; import { ReadPreference } from '../read_preference'; -import { MongoDriverError } from '../error'; +import { MongoCompatibilityError, MongoInvalidArgumentError } from '../error'; import type { TopologyDescription } from './topology_description'; import type { ServerDescription, TagSet } from './server_description'; @@ -50,13 +50,13 @@ function maxStalenessReducer( const maxStalenessVariance = (topologyDescription.heartbeatFrequencyMS + IDLE_WRITE_PERIOD) / 1000; if (maxStaleness < maxStalenessVariance) { - throw new MongoDriverError( + throw new MongoInvalidArgumentError( `maxStalenessSeconds must be at least ${maxStalenessVariance} seconds` ); } if (maxStaleness < SMALLEST_MAX_STALENESS_SECONDS) { - throw new MongoDriverError( + throw new MongoInvalidArgumentError( `maxStalenessSeconds must be at least ${SMALLEST_MAX_STALENESS_SECONDS} seconds` ); } @@ -214,7 +214,7 @@ function knownFilter(server: ServerDescription): boolean { */ export function readPreferenceServerSelector(readPreference: ReadPreference): ServerSelector { if (!readPreference.isValid()) { - throw new MongoDriverError('Invalid read preference specified'); + throw new MongoInvalidArgumentError('Invalid read preference specified'); } return ( @@ -227,7 +227,7 @@ export function readPreferenceServerSelector(readPreference: ReadPreference): Se readPreference.minWireVersion && readPreference.minWireVersion > commonWireVersion ) { - throw new MongoDriverError( + throw new MongoCompatibilityError( `Minimum wire version '${readPreference.minWireVersion}' required, but found '${commonWireVersion}'` ); } diff --git a/src/sdam/srv_polling.ts b/src/sdam/srv_polling.ts index 40973c82052..106197ff77f 100644 --- a/src/sdam/srv_polling.ts +++ b/src/sdam/srv_polling.ts @@ -2,7 +2,7 @@ import * as dns from 'dns'; import { Logger, LoggerOptions } from '../logger'; import { HostAddress } from '../utils'; import { TypedEventEmitter } from '../mongo_types'; -import { MongoDriverError } from '../error'; +import { MongoInvalidArgumentError } from '../error'; /** * Determines whether a provided address matches the provided parent domain in order @@ -67,7 +67,7 @@ export class SrvPoller extends TypedEventEmitter { super(); if (!options || !options.srvHost) { - throw new MongoDriverError('options for SrvPoller must exist and include srvHost'); + throw new MongoInvalidArgumentError('options for SrvPoller must exist and include srvHost'); } this.srvHost = options.srvHost; diff --git a/src/sdam/topology.ts b/src/sdam/topology.ts index 835743d4c04..6043a42d34c 100644 --- a/src/sdam/topology.ts +++ b/src/sdam/topology.ts @@ -11,7 +11,7 @@ import { } from '../sessions'; import { SrvPoller, SrvPollingEvent } from './srv_polling'; import { CMAP_EVENTS, ConnectionPoolEvents } from '../cmap/connection_pool'; -import { MongoServerSelectionError, MongoDriverError } from '../error'; +import { MongoServerSelectionError, MongoCompatibilityError, MongoDriverError } from '../error'; import { readPreferenceServerSelector, ServerSelector } from './server_selection'; import { makeStateMachine, @@ -274,6 +274,8 @@ export class Topology extends TypedEventEmitter { } else if (seed instanceof HostAddress) { seedlist.push(seed); } else { + // NODE-3402 + // FIXME: May need to me a MongoParseError throw new MongoDriverError(`Topology cannot be constructed from ${JSON.stringify(seed)}`); } } @@ -692,7 +694,7 @@ export class Topology extends TypedEventEmitter { // first update the TopologyDescription this.s.description = this.s.description.update(serverDescription); if (this.s.description.compatibilityError) { - this.emit(Topology.ERROR, new MongoDriverError(this.s.description.compatibilityError)); + this.emit(Topology.ERROR, new MongoCompatibilityError(this.s.description.compatibilityError)); return; } diff --git a/src/sdam/topology_description.ts b/src/sdam/topology_description.ts index 4e9abdbf1a4..250c6c60944 100644 --- a/src/sdam/topology_description.ts +++ b/src/sdam/topology_description.ts @@ -3,7 +3,7 @@ import * as WIRE_CONSTANTS from '../cmap/wire_protocol/constants'; import { TopologyType, ServerType } from './common'; import type { ObjectId, Document } from '../bson'; import type { SrvPollingEvent } from './srv_polling'; -import { MongoDriverError, MongoError } from '../error'; +import { MongoError, MongoInvalidArgumentError } from '../error'; // constants related to compatibility checks const MIN_SUPPORTED_SERVER_VERSION = WIRE_CONSTANTS.MIN_SUPPORTED_SERVER_VERSION; @@ -431,7 +431,7 @@ function updateRsWithPrimaryFromMember( setName?: string ): TopologyType { if (setName == null) { - throw new MongoDriverError('setName is required'); + throw new MongoInvalidArgumentError('setName is required'); } if ( diff --git a/src/sessions.ts b/src/sessions.ts index aca5b7dd261..51c092059bb 100644 --- a/src/sessions.ts +++ b/src/sessions.ts @@ -6,6 +6,7 @@ import { resolveClusterTime, ClusterTime } from './sdam/common'; import { isSharded } from './cmap/wire_protocol/shared'; import { MongoError, + MongoInvalidArgumentError, isRetryableError, MongoNetworkError, MongoWriteConcernError, @@ -268,7 +269,7 @@ class ClientSession extends TypedEventEmitter { topologyMaxWireVersion != null && topologyMaxWireVersion < minWireVersionForShardedTransactions ) { - throw new MongoDriverError( + throw new MongoCompatibilityError( 'Transactions are not supported on sharded clusters in MongoDB < 4.2.' ); } @@ -433,7 +434,9 @@ function attemptTransaction( if (!isPromiseLike(promise)) { session.abortTransaction(); - throw new MongoDriverError('Function provided to `withTransaction` must return a Promise'); + throw new MongoInvalidArgumentError( + 'Function provided to `withTransaction` must return a Promise' + ); } return promise.then( diff --git a/src/sort.ts b/src/sort.ts index 7046b1b30bd..418c1ffe843 100644 --- a/src/sort.ts +++ b/src/sort.ts @@ -1,4 +1,4 @@ -import { MongoDriverError } from './error'; +import { MongoInvalidArgumentError } from './error'; /** @public */ export type SortDirection = @@ -45,7 +45,7 @@ function prepareDirection(direction: any = 1): SortDirectionForCmd { case '-1': return -1; default: - throw new MongoDriverError(`Invalid sort direction: ${JSON.stringify(direction)}`); + throw new MongoInvalidArgumentError(`Invalid sort direction: ${JSON.stringify(direction)}`); } } @@ -118,7 +118,7 @@ export function formatSort( if (sort == null) return undefined; if (typeof sort === 'string') return new Map([[sort, prepareDirection(direction)]]); if (typeof sort !== 'object') { - throw new MongoDriverError(`Invalid sort format: ${JSON.stringify(sort)}`); + throw new MongoInvalidArgumentError(`Invalid sort format: ${JSON.stringify(sort)}`); } if (!Array.isArray(sort)) { return isMap(sort) ? mapToMap(sort) : Object.keys(sort).length ? objectToMap(sort) : undefined; diff --git a/src/utils.ts b/src/utils.ts index 80bc3a09cef..1d4ef1c81ca 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,13 @@ import * as os from 'os'; import * as crypto from 'crypto'; import { PromiseProvider } from './promise_provider'; -import { AnyError, MongoParseError, MongoDriverError } from './error'; +import { + AnyError, + MongoParseError, + MongoDriverError, + MongoCompatibilityError, + MongoInvalidArgumentError +} from './error'; import { WriteConcern, WriteConcernOptions, W } from './write_concern'; import type { Server } from './sdam/server'; import type { Topology } from './sdam/topology'; @@ -54,27 +60,27 @@ export function getSingleProperty( */ export function checkCollectionName(collectionName: string): void { if ('string' !== typeof collectionName) { - throw new MongoDriverError('collection name must be a String'); + throw new MongoInvalidArgumentError('collection name must be a String'); } if (!collectionName || collectionName.indexOf('..') !== -1) { - throw new MongoDriverError('collection names cannot be empty'); + throw new MongoInvalidArgumentError('collection names cannot be empty'); } if ( collectionName.indexOf('$') !== -1 && collectionName.match(/((^\$cmd)|(oplog\.\$main))/) == null ) { - throw new MongoDriverError("collection names must not contain '$'"); + throw new MongoInvalidArgumentError("collection names must not contain '$'"); } if (collectionName.match(/^\.|\.$/) != null) { - throw new MongoDriverError("collection names must not start or end with '.'"); + throw new MongoInvalidArgumentError("collection names must not start or end with '.'"); } // Validate that we are not passing 0x00 in the collection name if (collectionName.indexOf('\x00') !== -1) { - throw new MongoDriverError('collection names cannot contain a null character'); + throw new MongoInvalidArgumentError('collection names cannot contain a null character'); } } @@ -228,7 +234,7 @@ export function executeLegacyOperation( const Promise = PromiseProvider.get(); if (!Array.isArray(args)) { - throw new MongoDriverError('This method requires an array of arguments to apply'); + throw new MongoInvalidArgumentError('This method requires an array of arguments to apply'); } options = options ?? {}; @@ -248,7 +254,7 @@ export function executeLegacyOperation( const optionsIndex = args.length - 2; args[optionsIndex] = Object.assign({}, args[optionsIndex], { session: session }); } else if (opOptions.session && opOptions.session.hasEnded) { - throw new MongoDriverError('Use of expired sessions is not permitted'); + throw new MongoInvalidArgumentError('Use of expired sessions is not permitted'); } } @@ -289,7 +295,9 @@ export function executeLegacyOperation( // Return a Promise if (args[args.length - 1] != null) { - throw new MongoDriverError('final argument to `executeLegacyOperation` must be a callback'); + throw new MongoInvalidArgumentError( + 'final argument to `executeLegacyOperation` must be a callback' + ); } return new Promise((resolve, reject) => { @@ -399,7 +407,7 @@ export function decorateWithCollation( if (capabilities && capabilities.commandsTakeCollation) { command.collation = options.collation; } else { - throw new MongoDriverError(`Current topology does not support collation`); + throw new MongoCompatibilityError(`Current topology does not support collation`); } } } @@ -921,7 +929,7 @@ export function now(): number { /** @internal */ export function calculateDurationInMs(started: number): number { if (typeof started !== 'number') { - throw new MongoDriverError('numeric value required to calculate duration'); + throw new MongoInvalidArgumentError('numeric value required to calculate duration'); } const elapsed = now() - started; @@ -1222,7 +1230,7 @@ export class BufferPool { /** Reads the requested number of bytes, optionally consuming them */ read(size: number, consume = true): Buffer { if (typeof size !== 'number' || size < 0) { - throw new MongoDriverError('Parameter size must be a non-negative number'); + throw new MongoInvalidArgumentError('Parameter size must be a non-negative number'); } if (size > this[kLength]) { @@ -1324,7 +1332,7 @@ export class HostAddress { throw new MongoParseError('Invalid port (zero) with hostname'); } } else { - throw new MongoDriverError('Either socketPath or host must be defined.'); + throw new MongoInvalidArgumentError('Either socketPath or host must be defined.'); } Object.freeze(this); }