Skip to content

Commit e69d992

Browse files
authored
refactor(NODE-3404): implement MongoRuntimeError children (#2912)
1 parent 12eec9a commit e69d992

File tree

13 files changed

+129
-112
lines changed

13 files changed

+129
-112
lines changed

src/change_stream.ts

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import Denque = require('denque');
2-
import { MongoError, AnyError, isResumableError, MongoDriverError } from './error';
2+
import {
3+
MongoError,
4+
AnyError,
5+
isResumableError,
6+
MongoDriverError,
7+
MongoAPIError,
8+
MongoChangeStreamError
9+
} from './error';
310
import { AggregateOperation, AggregateOptions } from './operations/aggregate';
411
import {
512
maxWireVersion,
@@ -259,9 +266,8 @@ export class ChangeStream<TSchema extends Document = Document> extends TypedEven
259266
} else if (parent instanceof MongoClient) {
260267
this.type = CHANGE_DOMAIN_TYPES.CLUSTER;
261268
} else {
262-
// TODO(NODE-3404): Replace this with MongoChangeStreamError
263-
throw new MongoDriverError(
264-
'Parent provided to ChangeStream constructor must an instance of Collection, Db, or MongoClient'
269+
throw new MongoChangeStreamError(
270+
'Parent provided to ChangeStream constructor must be an instance of Collection, Db, or MongoClient'
265271
);
266272
}
267273

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

@@ -543,17 +548,18 @@ const CHANGE_STREAM_EVENTS = [
543548

544549
function setIsEmitter<TSchema>(changeStream: ChangeStream<TSchema>): void {
545550
if (changeStream[kMode] === 'iterator') {
546-
throw new MongoDriverError(
547-
'Cannot use ChangeStream as an EventEmitter after using as an iterator'
551+
// TODO(NODE-3485): Replace with MongoChangeStreamModeError
552+
throw new MongoAPIError(
553+
'ChangeStream cannot be used as an EventEmitter after being used as an iterator'
548554
);
549555
}
550556
changeStream[kMode] = 'emitter';
551557
}
552558

553559
function setIsIterator<TSchema>(changeStream: ChangeStream<TSchema>): void {
554560
if (changeStream[kMode] === 'emitter') {
555-
throw new MongoDriverError(
556-
'Cannot use ChangeStream as iterator after using as an EventEmitter'
561+
throw new MongoAPIError(
562+
'ChangeStream cannot be used as an EventEmitter after being used as an iterator'
557563
);
558564
}
559565
changeStream[kMode] = 'iterator';
@@ -630,6 +636,7 @@ function waitForTopologyConnected(
630636
}
631637

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

@@ -676,17 +683,23 @@ function processNewChange<TSchema>(
676683
callback?: Callback<ChangeStreamDocument<TSchema>>
677684
) {
678685
if (changeStream[kClosed]) {
686+
// TODO(NODE-3405): Replace with MongoStreamClosedError
679687
if (callback) callback(new MongoDriverError(CHANGESTREAM_CLOSED_ERROR));
680688
return;
681689
}
682690

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

688697
if (change && !change._id) {
689-
return closeWithError(changeStream, new MongoDriverError(NO_RESUME_TOKEN_ERROR), callback);
698+
return closeWithError(
699+
changeStream,
700+
new MongoChangeStreamError(NO_RESUME_TOKEN_ERROR),
701+
callback
702+
);
690703
}
691704

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

711724
// If the change stream has been closed explicitly, do not process error.
712725
if (changeStream[kClosed]) {
726+
// TODO(NODE-3405): Replace with MongoStreamClosedError
713727
if (callback) callback(new MongoDriverError(CHANGESTREAM_CLOSED_ERROR));
714728
return;
715729
}
@@ -770,6 +784,7 @@ function processError<TSchema>(
770784
*/
771785
function getCursor<T>(changeStream: ChangeStream<T>, callback: Callback<ChangeStreamCursor<T>>) {
772786
if (changeStream[kClosed]) {
787+
// TODO(NODE-3405): Replace with MongoStreamClosedError
773788
callback(new MongoDriverError(CHANGESTREAM_CLOSED_ERROR));
774789
return;
775790
}
@@ -795,11 +810,12 @@ function processResumeQueue<TSchema>(changeStream: ChangeStream<TSchema>, err?:
795810
const request = changeStream[kResumeQueue].pop();
796811
if (!err) {
797812
if (changeStream[kClosed]) {
813+
// TODO(NODE-3405): Replace with MongoStreamClosedError
798814
request(new MongoDriverError(CHANGESTREAM_CLOSED_ERROR));
799815
return;
800816
}
801817
if (!changeStream.cursor) {
802-
request(new MongoDriverError(NO_CURSOR_ERROR));
818+
request(new MongoChangeStreamError(NO_CURSOR_ERROR));
803819
return;
804820
}
805821
}

src/cmap/connect.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import {
55
MongoNetworkError,
66
MongoNetworkTimeoutError,
77
AnyError,
8-
MongoDriverError,
98
MongoCompatibilityError,
9+
MongoInvalidArgumentError,
1010
MongoServerError,
11-
MongoInvalidArgumentError
11+
MongoDriverError
1212
} from '../error';
1313
import { AUTH_PROVIDERS, AuthMechanism } from './auth/defaultAuthProviders';
1414
import { AuthContext } from './auth/auth_provider';

src/cmap/message_stream.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Duplex, DuplexOptions } from 'stream';
22
import { Response, Msg, BinMsg, Query, WriteProtocolMessageType, MessageHeader } from './commands';
3-
import { MongoDriverError, MongoParseError } from '../error';
3+
import { MongoDecompressionError, MongoParseError } from '../error';
44
import { OP_COMPRESSED, OP_MSG } from './wire_protocol/constants';
55
import {
66
compress,
@@ -191,9 +191,7 @@ function processIncomingData(stream: MessageStream, callback: Callback<Buffer>)
191191

192192
if (messageBody.length !== messageHeader.length) {
193193
callback(
194-
new MongoDriverError(
195-
'Decompressing a compressed message from the server failed. The message is corrupt.'
196-
)
194+
new MongoDecompressionError('Message body and message header must be the same length')
197195
);
198196

199197
return;

src/cmap/wire_protocol/compression.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Callback } from '../../utils';
33
import type { OperationDescription } from '../message_stream';
44

55
import { Snappy } from '../../deps';
6-
import { MongoDriverError } from '../../error';
6+
import { MongoDecompressionError, MongoInvalidArgumentError } from '../../error';
77

88
/** @public */
99
export const Compressor = Object.freeze({
@@ -53,10 +53,8 @@ export function compress(
5353
zlib.deflate(dataToBeCompressed, zlibOptions, callback as zlib.CompressCallback);
5454
break;
5555
default:
56-
throw new MongoDriverError(
57-
'Attempt to compress message using unknown compressor "' +
58-
self.options.agreedCompressor +
59-
'".'
56+
throw new MongoInvalidArgumentError(
57+
`Unknown compressor ${self.options.agreedCompressor} failed to compress`
6058
);
6159
}
6260
}
@@ -68,9 +66,8 @@ export function decompress(
6866
callback: Callback<Buffer>
6967
): void {
7068
if (compressorID < 0 || compressorID > Math.max(2)) {
71-
throw new MongoDriverError(
72-
`Server sent message compressed using an unsupported compressor.` +
73-
` (Received compressor ID ${compressorID})`
69+
throw new MongoDecompressionError(
70+
`Server sent message compressed using an unsupported compressor. (Received compressor ID ${compressorID})`
7471
);
7572
}
7673

src/connection_string.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { AuthMechanism } from './cmap/auth/defaultAuthProviders';
66
import { ReadPreference, ReadPreferenceMode } from './read_preference';
77
import { ReadConcern, ReadConcernLevel } from './read_concern';
88
import { W, WriteConcern } from './write_concern';
9-
import { MongoParseError } from './error';
9+
import { MongoAPIError, MongoInvalidArgumentError, MongoParseError } from './error';
1010
import {
1111
AnyOptions,
1212
Callback,
@@ -32,6 +32,7 @@ import type { TagSet } from './sdam/server_description';
3232
import { Logger, LoggerLevel } from './logger';
3333
import { PromiseProvider } from './promise_provider';
3434
import { Encrypter } from './encrypter';
35+
import { Compressor } from './cmap/wire_protocol/compression';
3536

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

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

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

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

7981
if (addresses.length === 0) {
80-
return callback(new MongoParseError('No addresses found at host'));
82+
return callback(new MongoAPIError('No addresses found at host'));
8183
}
8284

8385
for (const { name } of addresses) {
8486
if (!matchesParentDomain(name, lookupAddress)) {
85-
return callback(
86-
new MongoParseError('Server record does not share hostname with parent URI')
87-
);
87+
return callback(new MongoAPIError('Server record does not share hostname with parent URI'));
8888
}
8989
}
9090

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

288288
if (values.includes('')) {
289-
throw new MongoParseError('URI cannot contain options with no value');
289+
throw new MongoAPIError('URI cannot contain options with no value');
290290
}
291291

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

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

407407
const lbError = validateLoadBalancedOptions(hosts, mongoOptions);
@@ -614,10 +614,14 @@ export const OPTIONS = {
614614
const compressionList = new Set();
615615
for (const compVal of values as string[]) {
616616
for (const c of compVal.split(',')) {
617-
if (['none', 'snappy', 'zlib'].includes(String(c))) {
617+
if (Object.keys(Compressor).includes(String(c))) {
618618
compressionList.add(String(c));
619619
} else {
620-
throw new MongoParseError(`${c} is not a valid compression mechanism`);
620+
throw new MongoInvalidArgumentError(
621+
`${c} is not a valid compression mechanism. Must be one of: ${Object.keys(
622+
Compressor
623+
)}.`
624+
);
621625
}
622626
}
623627
}

src/db.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ export class Db {
737737
}
738738
}
739739

740+
// TODO(NODE-3484): Refactor into MongoDBNamespace
740741
// Validate the database name
741742
function validateDatabaseName(databaseName: string) {
742743
if (typeof databaseName !== 'string')
@@ -748,7 +749,6 @@ function validateDatabaseName(databaseName: string) {
748749
const invalidChars = [' ', '.', '$', '/', '\\'];
749750
for (let i = 0; i < invalidChars.length; i++) {
750751
if (databaseName.indexOf(invalidChars[i]) !== -1)
751-
// TODO(NODE-3405): Change this out for a child of MongoParseError
752752
throw new MongoDriverError(
753753
`database names cannot contain the character '${invalidChars[i]}'`
754754
);

src/error.ts

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ export class MongoDriverError extends MongoError {
189189
*/
190190

191191
export class MongoAPIError extends MongoDriverError {
192-
protected constructor(message: string) {
192+
constructor(message: string) {
193193
super(message);
194194
}
195195

@@ -209,7 +209,7 @@ export class MongoAPIError extends MongoDriverError {
209209
* @category Error
210210
*/
211211
export class MongoRuntimeError extends MongoDriverError {
212-
protected constructor(message: string) {
212+
constructor(message: string) {
213213
super(message);
214214
}
215215

@@ -235,39 +235,6 @@ export class MongoBatchReExecutionError extends MongoAPIError {
235235
}
236236
}
237237

238-
/**
239-
* An error generated when the user supplies an incorrect URI to the driver.
240-
*
241-
* @public
242-
* @category Error
243-
*/
244-
export class MongoURIError extends MongoRuntimeError {
245-
constructor(message: string) {
246-
super(message);
247-
}
248-
249-
get name(): string {
250-
return 'MongoURIError';
251-
}
252-
}
253-
254-
/**
255-
* An error generated when the driver fails to compress data
256-
* before sending it to the server.
257-
*
258-
* @public
259-
* @category Error
260-
*/
261-
export class MongoCompressionError extends MongoRuntimeError {
262-
constructor(message: string) {
263-
super(message);
264-
}
265-
266-
get name(): string {
267-
return 'MongoCompressionError';
268-
}
269-
}
270-
271238
/**
272239
* An error generated when the driver fails to decompress
273240
* data received from the server.

0 commit comments

Comments
 (0)