Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(NODE-5762): include cause and package name for all MongoMissingDependencyErrors #4067

Merged
merged 6 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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' }
nbbeeken marked this conversation as resolved.
Show resolved Hide resolved
);
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
Loading