Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
319fcd0
refactor(NODE-3404): implement MongoRuntimeError children
andymina Jul 15, 2021
2629a60
refactor(NODE-3404): pruned more instances of MongoDriverError
andymina Jul 16, 2021
143f44a
refactor(NODE-3404): modifying errors in tests
andymina Jul 16, 2021
409afa2
test(NODE-3404): correct gridfs tests to wait for the right errors
andymina Jul 17, 2021
eeea005
refactor(NODE-3404): exported errors from src/
andymina Jul 19, 2021
3a13cb6
refactor(NODE-3404): export MongoRuntimeError from src/
andymina Jul 19, 2021
8a17031
test(NODE-3404): revert use of MongoChangeStreamError
andymina Jul 20, 2021
9cc26c8
refactor(NODE-3404): revert select usage of MongoGridFSStreamError
andymina Jul 21, 2021
68d440e
test(NODE-3404): fix GridFS test waiting for the wrong error
andymina Jul 21, 2021
04207b8
refactor(NODE-3404): use MongoRuntime children where appropriate
andymina Aug 4, 2021
6dab25a
test(NODE-3404): remove errors throws and replace with expect.fail
andymina Aug 5, 2021
34ec480
style(NODE-3404): remove unused import
andymina Aug 6, 2021
7ddb4aa
refactor(NODE-3404): use existing compressor enum
andymina Aug 6, 2021
3b5c9e1
refactor(NODE-3404): remove MongoURIError
andymina Aug 10, 2021
e175181
refactor(NODE-3405): revert use of MongoRuntimeError
andymina Aug 10, 2021
1b0ae05
style(NODE-3404): remove ununsed import
andymina Aug 10, 2021
5659654
test(NODE-3404): revert expect.fail use in ChangeStream test
andymina Aug 10, 2021
57420ce
fix(NODE-3404): export missing errors from src/
andymina Aug 10, 2021
84e92ca
refactor(NODE-3404): remove unused MongoCompressionError
andymina Aug 10, 2021
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
40 changes: 28 additions & 12 deletions src/change_stream.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import Denque = require('denque');
import { MongoError, AnyError, isResumableError, MongoDriverError } from './error';
import {
MongoError,
AnyError,
isResumableError,
MongoDriverError,
MongoAPIError,
MongoChangeStreamError
} from './error';
import { AggregateOperation, AggregateOptions } from './operations/aggregate';
import {
maxWireVersion,
Expand Down Expand Up @@ -259,9 +266,8 @@ export class ChangeStream<TSchema extends Document = Document> extends TypedEven
} else if (parent instanceof MongoClient) {
this.type = CHANGE_DOMAIN_TYPES.CLUSTER;
} else {
// TODO(NODE-3404): Replace this with MongoChangeStreamError
throw new MongoDriverError(
'Parent provided to ChangeStream constructor must an instance of Collection, Db, or MongoClient'
throw new MongoChangeStreamError(
'Parent provided to ChangeStream constructor must be an instance of Collection, Db, or MongoClient'
);
}

Expand Down Expand Up @@ -365,8 +371,7 @@ export class ChangeStream<TSchema extends Document = Document> extends TypedEven
*/
stream(options?: CursorStreamOptions): Readable {
this.streamOptions = options;
// TODO(NODE-3404): Replace this with MongoChangeStreamError
if (!this.cursor) throw new MongoDriverError(NO_CURSOR_ERROR);
if (!this.cursor) throw new MongoChangeStreamError(NO_CURSOR_ERROR);
return this.cursor.stream(options);
}

Expand Down Expand Up @@ -543,17 +548,18 @@ const CHANGE_STREAM_EVENTS = [

function setIsEmitter<TSchema>(changeStream: ChangeStream<TSchema>): void {
if (changeStream[kMode] === 'iterator') {
throw new MongoDriverError(
'Cannot use ChangeStream as an EventEmitter after using as an iterator'
// TODO(NODE-3485): Replace with MongoChangeStreamModeError
throw new MongoAPIError(
'ChangeStream cannot be used as an EventEmitter after being used as an iterator'
);
}
changeStream[kMode] = 'emitter';
}

function setIsIterator<TSchema>(changeStream: ChangeStream<TSchema>): void {
if (changeStream[kMode] === 'emitter') {
throw new MongoDriverError(
'Cannot use ChangeStream as iterator after using as an EventEmitter'
throw new MongoAPIError(
'ChangeStream cannot be used as an EventEmitter after being used as an iterator'
);
}
changeStream[kMode] = 'iterator';
Expand Down Expand Up @@ -630,6 +636,7 @@ function waitForTopologyConnected(
}

if (calculateDurationInMs(start) > timeout) {
// TODO(NODE-3497): Replace with MongoNetworkTimeoutError
return callback(new MongoDriverError('Timed out waiting for connection'));
}

Expand Down Expand Up @@ -676,17 +683,23 @@ function processNewChange<TSchema>(
callback?: Callback<ChangeStreamDocument<TSchema>>
) {
if (changeStream[kClosed]) {
// TODO(NODE-3405): Replace with MongoStreamClosedError
if (callback) callback(new MongoDriverError(CHANGESTREAM_CLOSED_ERROR));
return;
}

// a null change means the cursor has been notified, implicitly closing the change stream
if (change == null) {
// TODO(NODE-3405): Replace with MongoStreamClosedError
return closeWithError(changeStream, new MongoDriverError(CHANGESTREAM_CLOSED_ERROR), callback);
}

if (change && !change._id) {
return closeWithError(changeStream, new MongoDriverError(NO_RESUME_TOKEN_ERROR), callback);
return closeWithError(
changeStream,
new MongoChangeStreamError(NO_RESUME_TOKEN_ERROR),
callback
);
}

// cache the resume token
Expand All @@ -710,6 +723,7 @@ function processError<TSchema>(

// If the change stream has been closed explicitly, do not process error.
if (changeStream[kClosed]) {
// TODO(NODE-3405): Replace with MongoStreamClosedError
if (callback) callback(new MongoDriverError(CHANGESTREAM_CLOSED_ERROR));
return;
}
Expand Down Expand Up @@ -770,6 +784,7 @@ function processError<TSchema>(
*/
function getCursor<T>(changeStream: ChangeStream<T>, callback: Callback<ChangeStreamCursor<T>>) {
if (changeStream[kClosed]) {
// TODO(NODE-3405): Replace with MongoStreamClosedError
callback(new MongoDriverError(CHANGESTREAM_CLOSED_ERROR));
return;
}
Expand All @@ -795,11 +810,12 @@ function processResumeQueue<TSchema>(changeStream: ChangeStream<TSchema>, err?:
const request = changeStream[kResumeQueue].pop();
if (!err) {
if (changeStream[kClosed]) {
// TODO(NODE-3405): Replace with MongoStreamClosedError
request(new MongoDriverError(CHANGESTREAM_CLOSED_ERROR));
return;
}
if (!changeStream.cursor) {
request(new MongoDriverError(NO_CURSOR_ERROR));
request(new MongoChangeStreamError(NO_CURSOR_ERROR));
return;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/cmap/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import {
MongoNetworkError,
MongoNetworkTimeoutError,
AnyError,
MongoDriverError,
MongoCompatibilityError,
MongoInvalidArgumentError,
MongoServerError,
MongoInvalidArgumentError
MongoDriverError
} from '../error';
import { AUTH_PROVIDERS, AuthMechanism } from './auth/defaultAuthProviders';
import { AuthContext } from './auth/auth_provider';
Expand Down
6 changes: 2 additions & 4 deletions src/cmap/message_stream.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Duplex, DuplexOptions } from 'stream';
import { Response, Msg, BinMsg, Query, WriteProtocolMessageType, MessageHeader } from './commands';
import { MongoDriverError, MongoParseError } from '../error';
import { MongoDecompressionError, MongoParseError } from '../error';
import { OP_COMPRESSED, OP_MSG } from './wire_protocol/constants';
import {
compress,
Expand Down Expand Up @@ -191,9 +191,7 @@ function processIncomingData(stream: MessageStream, callback: Callback<Buffer>)

if (messageBody.length !== messageHeader.length) {
callback(
new MongoDriverError(
'Decompressing a compressed message from the server failed. The message is corrupt.'
)
new MongoDecompressionError('Message body and message header must be the same length')
);

return;
Expand Down
13 changes: 5 additions & 8 deletions src/cmap/wire_protocol/compression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Callback } from '../../utils';
import type { OperationDescription } from '../message_stream';

import { Snappy } from '../../deps';
import { MongoDriverError } from '../../error';
import { MongoDecompressionError, MongoInvalidArgumentError } from '../../error';

/** @public */
export const Compressor = Object.freeze({
Expand Down Expand Up @@ -53,10 +53,8 @@ export function compress(
zlib.deflate(dataToBeCompressed, zlibOptions, callback as zlib.CompressCallback);
break;
default:
throw new MongoDriverError(
'Attempt to compress message using unknown compressor "' +
self.options.agreedCompressor +
'".'
throw new MongoInvalidArgumentError(
`Unknown compressor ${self.options.agreedCompressor} failed to compress`
);
}
}
Expand All @@ -68,9 +66,8 @@ export function decompress(
callback: Callback<Buffer>
): void {
if (compressorID < 0 || compressorID > Math.max(2)) {
throw new MongoDriverError(
`Server sent message compressed using an unsupported compressor.` +
` (Received compressor ID ${compressorID})`
throw new MongoDecompressionError(
`Server sent message compressed using an unsupported compressor. (Received compressor ID ${compressorID})`
);
}

Expand Down
26 changes: 15 additions & 11 deletions src/connection_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { AuthMechanism } from './cmap/auth/defaultAuthProviders';
import { ReadPreference, ReadPreferenceMode } from './read_preference';
import { ReadConcern, ReadConcernLevel } from './read_concern';
import { W, WriteConcern } from './write_concern';
import { MongoParseError } from './error';
import { MongoAPIError, MongoInvalidArgumentError, MongoParseError } from './error';
import {
AnyOptions,
Callback,
Expand All @@ -32,6 +32,7 @@ import type { TagSet } from './sdam/server_description';
import { Logger, LoggerLevel } from './logger';
import { PromiseProvider } from './promise_provider';
import { Encrypter } from './encrypter';
import { Compressor } from './cmap/wire_protocol/compression';

const VALID_TXT_RECORDS = ['authSource', 'replicaSet', 'loadBalanced'];

Expand Down Expand Up @@ -64,11 +65,12 @@ function matchesParentDomain(srvAddress: string, parentDomain: string): boolean
*/
export function resolveSRVRecord(options: MongoOptions, callback: Callback<HostAddress[]>): void {
if (typeof options.srvHost !== 'string') {
return callback(new MongoParseError('Cannot resolve empty srv string'));
return callback(new MongoAPIError('Option "srvHost" must not be empty'));
}

if (options.srvHost.split('.').length < 3) {
return callback(new MongoParseError('URI does not have hostname, domain name and tld'));
// TODO(NODE-3484): Replace with MongoConnectionStringError
return callback(new MongoAPIError('URI must include hostname, domain name, and tld'));
}

// Resolve the SRV record and use the result as the list of hosts to connect to.
Expand All @@ -77,14 +79,12 @@ export function resolveSRVRecord(options: MongoOptions, callback: Callback<HostA
if (err) return callback(err);

if (addresses.length === 0) {
return callback(new MongoParseError('No addresses found at host'));
return callback(new MongoAPIError('No addresses found at host'));
}

for (const { name } of addresses) {
if (!matchesParentDomain(name, lookupAddress)) {
return callback(
new MongoParseError('Server record does not share hostname with parent URI')
);
return callback(new MongoAPIError('Server record does not share hostname with parent URI'));
}
}

Expand Down Expand Up @@ -286,7 +286,7 @@ export function parseOptions(
const values = [...url.searchParams.getAll(key)];

if (values.includes('')) {
throw new MongoParseError('URI cannot contain options with no value');
throw new MongoAPIError('URI cannot contain options with no value');
}

if (key.toLowerCase() === 'serverapi') {
Expand Down Expand Up @@ -401,7 +401,7 @@ export function parseOptions(
if (options.promiseLibrary) PromiseProvider.set(options.promiseLibrary);

if (mongoOptions.directConnection && typeof mongoOptions.srvHost === 'string') {
throw new MongoParseError('directConnection not supported with SRV URI');
throw new MongoAPIError('SRV URI does not support directConnection');
}

const lbError = validateLoadBalancedOptions(hosts, mongoOptions);
Expand Down Expand Up @@ -614,10 +614,14 @@ export const OPTIONS = {
const compressionList = new Set();
for (const compVal of values as string[]) {
for (const c of compVal.split(',')) {
if (['none', 'snappy', 'zlib'].includes(String(c))) {
if (Object.keys(Compressor).includes(String(c))) {
compressionList.add(String(c));
} else {
throw new MongoParseError(`${c} is not a valid compression mechanism`);
throw new MongoInvalidArgumentError(
`${c} is not a valid compression mechanism. Must be one of: ${Object.keys(
Compressor
)}.`
);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,7 @@ export class Db {
}
}

// TODO(NODE-3484): Refactor into MongoDBNamespace
// Validate the database name
function validateDatabaseName(databaseName: string) {
if (typeof databaseName !== 'string')
Expand All @@ -748,7 +749,6 @@ function validateDatabaseName(databaseName: string) {
const invalidChars = [' ', '.', '$', '/', '\\'];
for (let i = 0; i < invalidChars.length; i++) {
if (databaseName.indexOf(invalidChars[i]) !== -1)
// TODO(NODE-3405): Change this out for a child of MongoParseError
throw new MongoDriverError(
`database names cannot contain the character '${invalidChars[i]}'`
);
Expand Down
37 changes: 2 additions & 35 deletions src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export class MongoDriverError extends MongoError {
*/

export class MongoAPIError extends MongoDriverError {
protected constructor(message: string) {
constructor(message: string) {
super(message);
}

Expand All @@ -209,7 +209,7 @@ export class MongoAPIError extends MongoDriverError {
* @category Error
*/
export class MongoRuntimeError extends MongoDriverError {
protected constructor(message: string) {
constructor(message: string) {
super(message);
}

Expand All @@ -235,39 +235,6 @@ export class MongoBatchReExecutionError extends MongoAPIError {
}
}

/**
* An error generated when the user supplies an incorrect URI to the driver.
*
* @public
* @category Error
*/
export class MongoURIError extends MongoRuntimeError {
constructor(message: string) {
super(message);
}

get name(): string {
return 'MongoURIError';
}
}

/**
* An error generated when the driver fails to compress data
* before sending it to the server.
*
* @public
* @category Error
*/
export class MongoCompressionError extends MongoRuntimeError {
constructor(message: string) {
super(message);
}

get name(): string {
return 'MongoCompressionError';
}
}

/**
* An error generated when the driver fails to decompress
* data received from the server.
Expand Down
Loading