Skip to content

Commit

Permalink
feat(NODE-5762): include cause and package name for all `MongoMissi…
Browse files Browse the repository at this point in the history
…ngDependencyError`s (#4067)

Co-authored-by: Anna Henningsen <github@addaleax.net>
  • Loading branch information
baileympearson and addaleax committed Apr 5, 2024
1 parent ce55ca9 commit 62ea94b
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 45 deletions.
2 changes: 1 addition & 1 deletion src/cmap/auth/gssapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async function externalCommand(
}>);
}

let krb: typeof Kerberos;
let krb: Kerberos;

export class GSSAPI extends AuthProvider {
override async auth(authContext: AuthContext): Promise<void> {
Expand Down
6 changes: 5 additions & 1 deletion src/cmap/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,11 @@ export class CryptoConnection extends Connection {
): Promise<Document> {
const { autoEncrypter } = this;
if (!autoEncrypter) {
throw new MongoMissingDependencyError('No AutoEncrypter available for encryption');
// TODO(NODE-6065): throw a MongoRuntimeError in Node V7
// @ts-expect-error No cause provided because there is no underlying error.
throw new MongoMissingDependencyError('No AutoEncrypter available for encryption', {
dependencyName: 'n/a'
});
}

const serverWireVersion = maxWireVersion(this);
Expand Down
2 changes: 1 addition & 1 deletion src/cmap/wire_protocol/compression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const ZSTD_COMPRESSION_LEVEL = 3;
const zlibInflate = promisify(zlib.inflate.bind(zlib));
const zlibDeflate = promisify(zlib.deflate.bind(zlib));

let zstd: typeof ZStandard;
let zstd: ZStandard;
let Snappy: SnappyLib | null = null;
function loadSnappy() {
if (Snappy == null) {
Expand Down
92 changes: 52 additions & 40 deletions src/deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,22 @@ function makeErrorModule(error: any) {
});
}

export let Kerberos: typeof import('kerberos') | { kModuleError: MongoMissingDependencyError } =
makeErrorModule(
new MongoMissingDependencyError(
'Optional module `kerberos` not found. Please install it to enable kerberos authentication'
)
);
export type Kerberos = typeof import('kerberos') | { kModuleError: MongoMissingDependencyError };

export function getKerberos(): typeof Kerberos | { kModuleError: MongoMissingDependencyError } {
export function getKerberos(): Kerberos {
let kerberos: Kerberos;
try {
// Ensure you always wrap an optional require in the try block NODE-3199
Kerberos = require('kerberos');
return Kerberos;
} catch {
return Kerberos;
kerberos = require('kerberos');
} catch (error) {
kerberos = makeErrorModule(
new MongoMissingDependencyError(
'Optional module `kerberos` not found. Please install it to enable kerberos authentication',
{ cause: error, dependencyName: 'kerberos' }
)
);
}
return kerberos;
}

export interface KerberosClient {
Expand All @@ -57,20 +58,22 @@ type ZStandardLib = {
decompress(buf: Buffer): Promise<Buffer>;
};

export let ZStandard: ZStandardLib | { kModuleError: MongoMissingDependencyError } =
makeErrorModule(
new MongoMissingDependencyError(
'Optional module `@mongodb-js/zstd` not found. Please install it to enable zstd compression'
)
);
export type ZStandard = ZStandardLib | { kModuleError: MongoMissingDependencyError };

export function getZstdLibrary(): typeof ZStandard | { kModuleError: MongoMissingDependencyError } {
export function getZstdLibrary(): ZStandardLib | { kModuleError: MongoMissingDependencyError } {
let ZStandard: ZStandardLib | { kModuleError: MongoMissingDependencyError };
try {
ZStandard = require('@mongodb-js/zstd');
return ZStandard;
} catch {
return ZStandard;
} catch (error) {
ZStandard = makeErrorModule(
new MongoMissingDependencyError(
'Optional module `@mongodb-js/zstd` not found. Please install it to enable zstd compression',
{ cause: error, dependencyName: 'zstd' }
)
);
}

return ZStandard;
}

/**
Expand Down Expand Up @@ -100,11 +103,12 @@ export function getAwsCredentialProvider():
// Ensure you always wrap an optional require in the try block NODE-3199
const credentialProvider = require('@aws-sdk/credential-providers');
return credentialProvider;
} catch {
} catch (error) {
return makeErrorModule(
new MongoMissingDependencyError(
'Optional module `@aws-sdk/credential-providers` not found.' +
' Please install it to enable getting aws credentials via the official sdk.'
' Please install it to enable getting aws credentials via the official sdk.',
{ cause: error, dependencyName: '@aws-sdk/credential-providers' }
)
);
}
Expand All @@ -120,11 +124,12 @@ export function getGcpMetadata(): GcpMetadata {
// Ensure you always wrap an optional require in the try block NODE-3199
const credentialProvider = require('gcp-metadata');
return credentialProvider;
} catch {
} catch (error) {
return makeErrorModule(
new MongoMissingDependencyError(
'Optional module `gcp-metadata` not found.' +
' Please install it to enable getting gcp credentials via the official sdk.'
' Please install it to enable getting gcp credentials via the official sdk.',
{ cause: error, dependencyName: 'gcp-metadata' }
)
);
}
Expand All @@ -150,10 +155,10 @@ export function getSnappy(): SnappyLib | { kModuleError: MongoMissingDependencyE
// Ensure you always wrap an optional require in the try block NODE-3199
const value = require('snappy');
return value;
} catch (cause) {
} catch (error) {
const kModuleError = new MongoMissingDependencyError(
'Optional module `snappy` not found. Please install it to enable snappy compression',
{ cause }
{ cause: error, dependencyName: 'snappy' }
);
return { kModuleError };
}
Expand Down Expand Up @@ -184,10 +189,10 @@ export function getSocks(): SocksLib | { kModuleError: MongoMissingDependencyErr
// Ensure you always wrap an optional require in the try block NODE-3199
const value = require('socks');
return value;
} catch (cause) {
} catch (error) {
const kModuleError = new MongoMissingDependencyError(
'Optional module `socks` not found. Please install it to connections over a SOCKS5 proxy',
{ cause }
{ cause: error, dependencyName: 'socks' }
);
return { kModuleError };
}
Expand Down Expand Up @@ -234,16 +239,23 @@ interface AWS4 {
};
}

export let aws4: AWS4 | { kModuleError: MongoMissingDependencyError } = makeErrorModule(
new MongoMissingDependencyError(
'Optional module `aws4` not found. Please install it to enable AWS authentication'
)
);
export const aws4: AWS4 | { kModuleError: MongoMissingDependencyError } = loadAws4();

try {
// Ensure you always wrap an optional require in the try block NODE-3199
aws4 = require('aws4');
} catch {} // eslint-disable-line
function loadAws4() {
let aws4: AWS4 | { kModuleError: MongoMissingDependencyError };
try {
aws4 = require('aws4');
} catch (error) {
aws4 = makeErrorModule(
new MongoMissingDependencyError(
'Optional module `aws4` not found. Please install it to enable AWS authentication',
{ cause: error, dependencyName: 'aws4' }
)
);
}

return aws4;
}

/** A utility function to get the instance of mongodb-client-encryption, if it exists. */
export function getMongoDBClientEncryption():
Expand All @@ -256,10 +268,10 @@ export function getMongoDBClientEncryption():
// Cannot be moved to helper utility function, bundlers search and replace the actual require call
// in a way that makes this line throw at bundle time, not runtime, catching here will make bundling succeed
mongodbClientEncryption = require('mongodb-client-encryption');
} catch (cause) {
} catch (error) {
const kModuleError = new MongoMissingDependencyError(
'Optional module `mongodb-client-encryption` not found. Please install it to use auto encryption or ClientEncryption.',
{ cause }
{ cause: error, dependencyName: 'mongodb-client-encryption' }
);
return { kModuleError };
}
Expand Down
6 changes: 5 additions & 1 deletion src/encrypter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,11 @@ export class Encrypter {
if ('kModuleError' in mongodbClientEncryption) {
throw new MongoMissingDependencyError(
'Auto-encryption requested, but the module is not installed. ' +
'Please add `mongodb-client-encryption` as a dependency of your project'
'Please add `mongodb-client-encryption` as a dependency of your project',
{
cause: mongodbClientEncryption['kModuleError'],
dependencyName: 'mongodb-client-encryption'
}
);
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,11 @@ export class MongoMissingCredentialsError extends MongoAPIError {
* @category Error
*/
export class MongoMissingDependencyError extends MongoAPIError {
dependencyName: string;

/** @remarks This property is assigned in the `Error` constructor. */
declare cause: Error;

/**
* **Do not use this constructor!**
*
Expand All @@ -1019,8 +1024,9 @@ export class MongoMissingDependencyError extends MongoAPIError {
*
* @public
**/
constructor(message: string, options: { cause?: Error } = {}) {
constructor(message: string, options: { cause: Error; dependencyName: string }) {
super(message, options);
this.dependencyName = options.dependencyName;
}

override get name(): string {
Expand Down

0 comments on commit 62ea94b

Please sign in to comment.