Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
5 changes: 3 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,8 @@
"patterns": [
"**/../lib/**",
"mongodb-mock-server",
"node:*"
"node:*",
"os"
],
"paths": [
{
Expand Down Expand Up @@ -327,4 +328,4 @@
}
}
]
}
}
11 changes: 7 additions & 4 deletions src/cmap/auth/gssapi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as dns from 'dns';
import * as os from 'os';

import { getKerberos, type Kerberos, type KerberosClient } from '../../deps';
import { MongoInvalidArgumentError, MongoMissingCredentialsError } from '../../error';
Expand Down Expand Up @@ -69,9 +68,13 @@ export class GSSAPI extends AuthProvider {
}
}

async function makeKerberosClient(authContext: AuthContext): Promise<KerberosClient> {
const { hostAddress } = authContext.options;
const { credentials } = authContext;
async function makeKerberosClient({
options: {
hostAddress,
runtime: { os }
},
credentials
}: AuthContext): Promise<KerberosClient> {
if (!hostAddress || typeof hostAddress.host !== 'string' || !credentials) {
throw new MongoInvalidArgumentError(
'Connection must have host and port and credentials defined.'
Expand Down
3 changes: 3 additions & 0 deletions src/cmap/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { type MongoClientAuthProviders } from '../mongo_client_auth_providers';
import { MongoLoggableComponent, type MongoLogger, SeverityLevel } from '../mongo_logger';
import { type Abortable, type CancellationToken, TypedEventEmitter } from '../mongo_types';
import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
import { type Runtime } from '../runtime_adapters';
import { ServerType } from '../sdam/common';
import { applySession, type ClientSession, updateSessionFromResponse } from '../sessions';
import { type TimeoutContext, TimeoutError } from '../timeout';
Expand Down Expand Up @@ -143,6 +144,8 @@ export interface ConnectionOptions
metadata: Promise<ClientMetadata>;
/** @internal */
mongoLogger?: MongoLogger | undefined;
/** @internal */
runtime: Runtime;
}

/** @public */
Expand Down
6 changes: 3 additions & 3 deletions src/cmap/handshake/client_metadata.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as os from 'os';
import * as process from 'process';

import { BSON, type Document, Int32, NumberUtils } from '../../bson';
Expand Down Expand Up @@ -96,7 +95,8 @@ export class LimitedSizeDocument {
}
}

type MakeClientMetadataOptions = Pick<MongoOptions, 'appName'>;
type MakeClientMetadataOptions = Pick<MongoOptions, 'appName' | 'runtime'>;

/**
* From the specs:
* Implementors SHOULD cumulatively update fields in the following order until the document is under the size limit:
Expand All @@ -107,7 +107,7 @@ type MakeClientMetadataOptions = Pick<MongoOptions, 'appName'>;
*/
export async function makeClientMetadata(
driverInfoList: DriverInfo[],
{ appName = '' }: MakeClientMetadataOptions
{ appName = '', runtime: { os } }: MakeClientMetadataOptions
): Promise<ClientMetadata> {
const metadataDocument = new LimitedSizeDocument(512);

Expand Down
6 changes: 6 additions & 0 deletions src/connection_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import { MongoLoggableComponent, MongoLogger, SeverityLevel } from './mongo_logger';
import { ReadConcern, type ReadConcernLevel } from './read_concern';
import { ReadPreference, type ReadPreferenceMode } from './read_preference';
import { resolveRuntimeAdapters } from './runtime_adapters';
import { ServerMonitoringMode } from './sdam/monitor';
import type { TagSet } from './sdam/server_description';
import {
Expand Down Expand Up @@ -538,6 +539,8 @@ export function parseOptions(
}
);

mongoOptions.runtime = resolveRuntimeAdapters(options);

return mongoOptions;
}

Expand Down Expand Up @@ -1061,6 +1064,9 @@ export const OPTIONS = {
default: true,
type: 'boolean'
},
runtimeAdapters: {
type: 'record'
},
serializeFunctions: {
type: 'boolean'
},
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ export type {
ReadPreferenceLikeOptions,
ReadPreferenceOptions
} from './read_preference';
export type { OsAdapter, Runtime, RuntimeAdapters } from './runtime_adapters';
export type { ClusterTime } from './sdam/common';
export type {
Monitor,
Expand Down
10 changes: 10 additions & 0 deletions src/mongo_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { EndSessionsOperation } from './operations/end_sessions';
import { executeOperation } from './operations/execute_operation';
import type { ReadConcern, ReadConcernLevel, ReadConcernLike } from './read_concern';
import { ReadPreference, type ReadPreferenceMode } from './read_preference';
import { type Runtime, type RuntimeAdapters } from './runtime_adapters';
import type { ServerMonitoringMode } from './sdam/monitor';
import type { TagSet } from './sdam/server_description';
import { DeprioritizedServers, readPreferenceServerSelector } from './sdam/server_selection';
Expand Down Expand Up @@ -318,6 +319,12 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
connectionType?: typeof Connection;
/** @internal */
__skipPingOnConnect?: boolean;
/**
* @experimental
*
* If provided, any adapters provided will be used in place of the corresponding Node.js module.
*/
runtimeAdapters?: RuntimeAdapters;
}

/** @public */
Expand Down Expand Up @@ -1152,4 +1159,7 @@ export interface MongoOptions
timeoutMS?: number;
/** @internal */
__skipPingOnConnect?: boolean;

/** @internal */
runtime: Runtime;
}
48 changes: 48 additions & 0 deletions src/runtime_adapters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* eslint-disable no-restricted-imports */
// We squash the restricted import errors here because we are using type-only imports, which
// do not impact the driver's actual runtime dependencies.

import type * as os from 'os';

import { type MongoClientOptions } from './mongo_client';

/**
* @public
* @experimental
*
* Represents the set of dependencies that the driver uses from the [Node.js OS module](https://nodejs.org/api/os.html).
*/
export type OsAdapter = Pick<typeof os, 'release' | 'platform' | 'arch' | 'type'>;

/**
* @public
* @experimental
*
* This type represents the set of dependencies that the driver needs from the Javascript runtime in order to function.
*/
export interface RuntimeAdapters {
os?: OsAdapter;
}

/**
* @internal
*
* Represents a complete, parsed set of runtime adapters. After options parsing, all adapters
* are always present (either using the user's provided adapter, or defaulting to the Node.js module).
*/
export interface Runtime {
os: OsAdapter;
}

/**
* @internal
*
* Given a MongoClientOptions, this function resolves the set of runtime options, providing Nodejs implementations if
* not provided by in `options`, and returns a `Runtime`.
*/
export function resolveRuntimeAdapters(options: MongoClientOptions): Runtime {
return {
// eslint-disable-next-line @typescript-eslint/no-require-imports
Comment thread
PavelSafronov marked this conversation as resolved.
Outdated
os: options.runtimeAdapters?.os ?? require('os')
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { LEGACY_HELLO_COMMAND } from '../../../src/constants';
import { Topology } from '../../../src/sdam/topology';
import { HostAddress, ns } from '../../../src/utils';
import * as mock from '../../tools/mongodb-mock/index';
import { processTick, sleep } from '../../tools/utils';
import { processTick, runtime, sleep } from '../../tools/utils';
import { assert as test, setupDatabase } from '../shared';

const commonConnectOptions = {
Expand All @@ -49,7 +49,10 @@ describe('Connection', function () {
...commonConnectOptions,
connectionType: Connection,
...this.configuration.options,
metadata: makeClientMetadata([], {})
metadata: makeClientMetadata([], {
runtime
}),
runtime
};

let conn;
Expand All @@ -71,7 +74,8 @@ describe('Connection', function () {
connectionType: Connection,
...this.configuration.options,
monitorCommands: true,
metadata: makeClientMetadata([], {})
runtime,
metadata: makeClientMetadata([], { runtime })
};

let conn;
Expand Down Expand Up @@ -102,7 +106,10 @@ describe('Connection', function () {
connectionType: Connection,
...this.configuration.options,
monitorCommands: true,
metadata: makeClientMetadata([], {})
runtime,
metadata: makeClientMetadata([], {
runtime
})
};

let conn;
Expand Down
7 changes: 7 additions & 0 deletions test/tools/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import {
type HostAddress,
MongoClient,
type MongoClientOptions,
type Runtime,
type ServerApiVersion,
type TopologyOptions
} from '../../src';
import { OP_MSG } from '../../src/cmap/wire_protocol/constants';
import { resolveRuntimeAdapters } from '../../src/runtime_adapters';
import { Topology } from '../../src/sdam/topology';
import { processTimeMS } from '../../src/utils';
import { type TestConfiguration } from './runner/config';
Expand Down Expand Up @@ -604,3 +606,8 @@ export function configureMongocryptdSpawnHooks(
port
};
}

/**
* A `Runtime` that resolves to entirely Nodejs modules, useful when tests must provide a default `runtime` object to an API.
*/
export const runtime: Runtime = resolveRuntimeAdapters({});
Comment thread
PavelSafronov marked this conversation as resolved.
9 changes: 8 additions & 1 deletion test/unit/assorted/optional_require.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { GSSAPI } from '../../../src/cmap/auth/gssapi';
import { compress } from '../../../src/cmap/wire_protocol/compression';
import { MongoMissingDependencyError } from '../../../src/error';
import { HostAddress } from '../../../src/utils';
import { runtime } from '../../tools/utils';

function moduleExistsSync(moduleName) {
return existsSync(resolve(__dirname, `../../../node_modules/${moduleName}`));
Expand Down Expand Up @@ -41,7 +42,13 @@ describe('optionalRequire', function () {
const gssapi = new GSSAPI();

const error = await gssapi
.auth(new AuthContext(null, true, { hostAddress: new HostAddress('a'), credentials: true }))
.auth(
new AuthContext(null, true, {
hostAddress: new HostAddress('a'),
credentials: true,
runtime
})
)
.then(
() => null,
e => e
Expand Down
19 changes: 15 additions & 4 deletions test/unit/cmap/connect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { CancellationToken } from '../../../src/mongo_types';
import { HostAddress, isHello } from '../../../src/utils';
import { genClusterTime } from '../../tools/common';
import * as mock from '../../tools/mongodb-mock/index';
import { runtime } from '../../tools/utils';

const CONNECT_DEFAULTS = {
id: 1,
Expand Down Expand Up @@ -210,7 +211,9 @@ describe('Connect Tests', function () {
connection: {},
options: {
...CONNECT_DEFAULTS,
metadata: makeClientMetadata([], {})
metadata: makeClientMetadata([], {
runtime
})
}
};
});
Expand Down Expand Up @@ -239,7 +242,10 @@ describe('Connect Tests', function () {
name: 's'.repeat(128)
}
],
{ appName: longAppName }
{
appName: longAppName,
runtime
}
);
const longAuthContext = {
connection: {},
Expand Down Expand Up @@ -267,7 +273,9 @@ describe('Connect Tests', function () {
connection: {},
options: {
...CONNECT_DEFAULTS,
metadata: makeClientMetadata([], {})
metadata: makeClientMetadata([], {
runtime
})
}
};
});
Expand Down Expand Up @@ -296,7 +304,10 @@ describe('Connect Tests', function () {
name: 's'.repeat(128)
}
],
{ appName: longAppName }
{
appName: longAppName,
runtime
}
);
const longAuthContext = {
connection: {},
Expand Down
Loading