diff --git a/packages/e2e-tests/test/e2e-oidc.spec.ts b/packages/e2e-tests/test/e2e-oidc.spec.ts index 08935926c..50964a0fb 100644 --- a/packages/e2e-tests/test/e2e-oidc.spec.ts +++ b/packages/e2e-tests/test/e2e-oidc.spec.ts @@ -374,7 +374,7 @@ describe('OIDC auth e2e', function () { // Internal hack to get a state-share server as e.g. Compass or the VSCode extension would let handle = await shell.executeLine( - 'db.getMongo()._serviceProvider.currentClientOptions.parentState.getStateShareServer()' + 'db.getMongo()._serviceProvider._sp.currentClientOptions.parentState.getStateShareServer()' ); // `handle` can include the next prompt when returned by `shell.executeLine()`, // so look for the longest prefix of it that is valid JSON. diff --git a/packages/service-provider-core/src/admin.ts b/packages/service-provider-core/src/admin.ts index ef2745b0b..95bb6be76 100644 --- a/packages/service-provider-core/src/admin.ts +++ b/packages/service-provider-core/src/admin.ts @@ -13,7 +13,7 @@ import type { AutoEncryptionOptions, Collection, } from './all-transport-types'; -import type { ConnectionExtraInfo } from './index'; +import type { ConnectionExtraInfo, ServiceProvider } from './index'; import type { ReplPlatform } from './platform'; import type { AWSEncryptionKeyOptions, @@ -90,7 +90,10 @@ export default interface Admin { * @param uri * @param options */ - getNewConnection(uri: string, options: MongoClientOptions): Promise; // returns the ServiceProvider instance + getNewConnection( + uri: string, + options: MongoClientOptions + ): Promise; /** * Return the URI for the current connection, if this ServiceProvider is connected. diff --git a/packages/shell-api/src/custom-inspect.ts b/packages/shell-api/src/custom-inspect.ts new file mode 100644 index 000000000..c2ced94f6 --- /dev/null +++ b/packages/shell-api/src/custom-inspect.ts @@ -0,0 +1,55 @@ +import type { InspectOptions, inspect as _inspect } from 'util'; + +const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom'); + +function customDocumentInspect( + this: Document, + depth: number, + inspectOptions: InspectOptions, + inspect: typeof _inspect +) { + const newInspectOptions = { + ...inspectOptions, + depth: Infinity, + maxArrayLength: Infinity, + maxStringLength: Infinity, + }; + + // reuse the standard inpect logic for an object without causing infinite + // recursion + const copyToInspect: any = Array.isArray(this) ? this.slice() : { ...this }; + delete copyToInspect[customInspectSymbol]; + return inspect(copyToInspect, newInspectOptions); +} + +function addInspectSymbol(obj: any) { + if (!(obj as any)[customInspectSymbol]) { + Object.defineProperty(obj, customInspectSymbol, { + value: customDocumentInspect, + enumerable: false, + writable: true, + configurable: true, + }); + } +} + +export function addCustomInspect(obj: any) { + if (Array.isArray(obj)) { + addInspectSymbol(obj); + for (const item of obj) { + addCustomInspect(item); + } + } else if ( + obj && + typeof obj === 'object' && + obj !== null && + !obj._bsontype && + !(obj instanceof Date) && + !(obj instanceof RegExp) + ) { + addInspectSymbol(obj); + for (const value of Object.values(obj)) { + addCustomInspect(value); + } + } +} diff --git a/packages/shell-api/src/deep-inspect-aggregation-cursor-wrapper.ts b/packages/shell-api/src/deep-inspect-aggregation-cursor-wrapper.ts new file mode 100644 index 000000000..6812dd908 --- /dev/null +++ b/packages/shell-api/src/deep-inspect-aggregation-cursor-wrapper.ts @@ -0,0 +1,142 @@ +import type { + Document, + ReadConcernLike, + ReadPreferenceLike, + ServiceProviderAggregationCursor, +} from '@mongosh/service-provider-core'; +import type { PickMethodsByReturnType } from './pick-methods-by-return-type'; +import { addCustomInspect } from './custom-inspect'; + +export class DeepInspectAggregationCursorWrapper + implements ServiceProviderAggregationCursor +{ + _cursor: ServiceProviderAggregationCursor; + + constructor(cursor: ServiceProviderAggregationCursor) { + this._cursor = cursor; + } + + project = forwardedMethod('project'); + skip = forwardedMethod('skip'); + sort = forwardedMethod('sort'); + explain = forwardedMethod('explain'); + addCursorFlag = forwardedMethod('addCursorFlag'); + withReadPreference = (readPreference: ReadPreferenceLike) => { + this._cursor.withReadPreference(readPreference); + return this; + }; + withReadConcern(readConcern: ReadConcernLike) { + this._cursor.withReadConcern(readConcern); + return this; + } + batchSize = forwardedMethod('batchSize'); + hasNext = forwardedMethod('hasNext'); + close = forwardedMethod('close'); + maxTimeMS = forwardedMethod('maxTimeMS'); + bufferedCount = forwardedMethod('bufferedCount'); + + next = forwardResultPromise('next'); + tryNext = forwardResultPromise('tryNext'); + + toArray = forwardResultsPromise('toArray'); + readBufferedDocuments = forwardResults( + 'readBufferedDocuments' + ); + + get closed(): boolean { + return this._cursor.closed; + } + + async *[Symbol.asyncIterator]() { + yield* this._cursor; + return; + } +} + +function forwardResultPromise< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderAggregationCursor, + Promise + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return async function ( + this: DeepInspectAggregationCursorWrapper, + ...args: Parameters>[K]> + ): // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore The returntype already contains a promise + ReturnType>[K]> { + const result = await (this._cursor[key] as any)(...args); + if (result) { + addCustomInspect(result); + } + return result; + }; +} + +function forwardResultsPromise< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderAggregationCursor, + Promise + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return async function ( + this: DeepInspectAggregationCursorWrapper, + ...args: Parameters>[K]> + ): // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore The returntype already contains a promise + ReturnType>[K]> { + const results = await (this._cursor[key] as any)(...args); + addCustomInspect(results); + return results; + }; +} + +function forwardResults< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderAggregationCursor, + TSchema[] + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return function ( + this: DeepInspectAggregationCursorWrapper, + ...args: Parameters>[K]> + ): ReturnType>[K]> { + const results = (this._cursor[key] as any)(...args); + addCustomInspect(results); + return results; + }; +} + +function forwardedMethod< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderAggregationCursor, + any + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return function ( + this: DeepInspectAggregationCursorWrapper, + ...args: Parameters>[K]> + ): ReturnType>[K]> { + return (this._cursor[key] as any)(...args); + }; +} diff --git a/packages/shell-api/src/deep-inspect-change-stream-wrapper.ts b/packages/shell-api/src/deep-inspect-change-stream-wrapper.ts new file mode 100644 index 000000000..21b547488 --- /dev/null +++ b/packages/shell-api/src/deep-inspect-change-stream-wrapper.ts @@ -0,0 +1,80 @@ +import type { + Document, + ResumeToken, + ServiceProviderChangeStream, +} from '@mongosh/service-provider-core'; +import type { PickMethodsByReturnType } from './pick-methods-by-return-type'; +import { addCustomInspect } from './custom-inspect'; + +export class DeepInspectChangeStreamWrapper + implements ServiceProviderChangeStream +{ + _cursor: ServiceProviderChangeStream; + + constructor(cursor: ServiceProviderChangeStream) { + this._cursor = cursor; + } + + get resumeToken(): ResumeToken { + return this._cursor.resumeToken; + } + + hasNext = forwardedMethod('hasNext'); + close = forwardedMethod('close'); + + next = forwardResultPromise('next'); + tryNext = forwardResultPromise('tryNext'); + + get closed(): boolean { + return this._cursor.closed; + } + + async *[Symbol.asyncIterator]() { + yield* this._cursor; + return; + } +} + +function forwardResultPromise< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderChangeStream, + Promise + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return async function ( + this: DeepInspectChangeStreamWrapper, + ...args: Parameters>[K]> + ): // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore The returntype already contains a promise + ReturnType>[K]> { + const result = await (this._cursor[key] as any)(...args); + if (result) { + addCustomInspect(result); + } + return result; + }; +} + +function forwardedMethod< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderChangeStream, + any + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return function ( + this: DeepInspectChangeStreamWrapper, + ...args: Parameters>[K]> + ): ReturnType>[K]> { + return (this._cursor[key] as any)(...args); + }; +} diff --git a/packages/shell-api/src/deep-inspect-find-cursor-wrapper.ts b/packages/shell-api/src/deep-inspect-find-cursor-wrapper.ts new file mode 100644 index 000000000..f008d4587 --- /dev/null +++ b/packages/shell-api/src/deep-inspect-find-cursor-wrapper.ts @@ -0,0 +1,156 @@ +import type { + Document, + ReadConcernLike, + ReadPreferenceLike, + ServiceProviderFindCursor, +} from '@mongosh/service-provider-core'; +import type { PickMethodsByReturnType } from './pick-methods-by-return-type'; +import { addCustomInspect } from './custom-inspect'; + +export class DeepInspectFindCursorWrapper + implements ServiceProviderFindCursor +{ + _cursor: ServiceProviderFindCursor; + + constructor(cursor: ServiceProviderFindCursor) { + this._cursor = cursor; + } + + allowDiskUse = forwardedMethod('allowDiskUse'); + collation = forwardedMethod('collation'); + comment = forwardedMethod('comment'); + maxAwaitTimeMS = forwardedMethod('maxAwaitTimeMS'); + count = forwardedMethod('count'); + hint = forwardedMethod('hint'); + max = forwardedMethod('max'); + min = forwardedMethod('min'); + limit = forwardedMethod('limit'); + skip = forwardedMethod('skip'); + returnKey = forwardedMethod('returnKey'); + showRecordId = forwardedMethod('showRecordId'); + project = forwardedMethod('project'); + sort = forwardedMethod('sort'); + explain = forwardedMethod('explain'); + addCursorFlag = forwardedMethod('addCursorFlag'); + + withReadPreference = (readPreference: ReadPreferenceLike) => { + this._cursor.withReadPreference(readPreference); + return this; + }; + + withReadConcern(readConcern: ReadConcernLike) { + this._cursor.withReadConcern(readConcern); + return this; + } + + batchSize = forwardedMethod('batchSize'); + hasNext = forwardedMethod('hasNext'); + close = forwardedMethod('close'); + maxTimeMS = forwardedMethod('maxTimeMS'); + bufferedCount = forwardedMethod('bufferedCount'); + + next = forwardResultPromise('next'); + tryNext = forwardResultPromise('tryNext'); + + toArray = forwardResultsPromise('toArray'); + readBufferedDocuments = forwardResults( + 'readBufferedDocuments' + ); + + get closed(): boolean { + return this._cursor.closed; + } + + async *[Symbol.asyncIterator]() { + yield* this._cursor; + return; + } +} + +function forwardResultPromise< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderFindCursor, + Promise + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return async function ( + this: DeepInspectFindCursorWrapper, + ...args: Parameters>[K]> + ): // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore The returntype already contains a promise + ReturnType>[K]> { + const result = await (this._cursor[key] as any)(...args); + if (result) { + addCustomInspect(result); + } + return result; + }; +} + +function forwardResultsPromise< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderFindCursor, + Promise + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return async function ( + this: DeepInspectFindCursorWrapper, + ...args: Parameters>[K]> + ): // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore The returntype already contains a promise + ReturnType>[K]> { + const results = await (this._cursor[key] as any)(...args); + addCustomInspect(results); + return results; + }; +} + +function forwardResults< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderFindCursor, + TSchema[] + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return function ( + this: DeepInspectFindCursorWrapper, + ...args: Parameters>[K]> + ): ReturnType>[K]> { + const results = (this._cursor[key] as any)(...args); + addCustomInspect(results); + return results; + }; +} + +function forwardedMethod< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderFindCursor, + any + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return function ( + this: DeepInspectFindCursorWrapper, + ...args: Parameters>[K]> + ): ReturnType>[K]> { + return (this._cursor[key] as any)(...args); + }; +} diff --git a/packages/shell-api/src/deep-inspect-run-command-cursor-wrapper.ts b/packages/shell-api/src/deep-inspect-run-command-cursor-wrapper.ts new file mode 100644 index 000000000..a918f4ae3 --- /dev/null +++ b/packages/shell-api/src/deep-inspect-run-command-cursor-wrapper.ts @@ -0,0 +1,127 @@ +import type { + Document, + ServiceProviderRunCommandCursor, +} from '@mongosh/service-provider-core'; +import type { PickMethodsByReturnType } from './pick-methods-by-return-type'; +import { addCustomInspect } from './custom-inspect'; + +export class DeepInspectRunCommandCursorWrapper + implements ServiceProviderRunCommandCursor +{ + _cursor: ServiceProviderRunCommandCursor; + + constructor(cursor: ServiceProviderRunCommandCursor) { + this._cursor = cursor; + } + + batchSize = forwardedMethod('batchSize'); + hasNext = forwardedMethod('hasNext'); + close = forwardedMethod('close'); + maxTimeMS = forwardedMethod('maxTimeMS'); + bufferedCount = forwardedMethod('bufferedCount'); + + next = forwardResultPromise('next'); + tryNext = forwardResultPromise('tryNext'); + + toArray = forwardResultsPromise('toArray'); + readBufferedDocuments = forwardResults( + 'readBufferedDocuments' + ); + + get closed(): boolean { + return this._cursor.closed; + } + + async *[Symbol.asyncIterator]() { + yield* this._cursor; + return; + } +} + +function forwardResultPromise< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderRunCommandCursor, + Promise + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return async function ( + this: DeepInspectRunCommandCursorWrapper, + ...args: Parameters>[K]> + ): // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore The returntype already contains a promise + ReturnType>[K]> { + const result = await (this._cursor[key] as any)(...args); + if (result) { + addCustomInspect(result); + } + return result; + }; +} + +function forwardResultsPromise< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderRunCommandCursor, + Promise + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return async function ( + this: DeepInspectRunCommandCursorWrapper, + ...args: Parameters>[K]> + ): // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore The returntype already contains a promise + ReturnType>[K]> { + const results = await (this._cursor[key] as any)(...args); + addCustomInspect(results); + return results; + }; +} + +function forwardResults< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderRunCommandCursor, + TSchema[] + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return function ( + this: DeepInspectRunCommandCursorWrapper, + ...args: Parameters>[K]> + ): ReturnType>[K]> { + const results = (this._cursor[key] as any)(...args); + addCustomInspect(results); + return results; + }; +} + +function forwardedMethod< + TSchema, + K extends keyof PickMethodsByReturnType< + ServiceProviderRunCommandCursor, + any + > +>( + key: K +): ( + ...args: Parameters>[K]> +) => ReturnType>[K]> { + return function ( + this: DeepInspectRunCommandCursorWrapper, + ...args: Parameters>[K]> + ): ReturnType>[K]> { + return (this._cursor[key] as any)(...args); + }; +} diff --git a/packages/shell-api/src/deep-inspect-service-provider-wrapper.ts b/packages/shell-api/src/deep-inspect-service-provider-wrapper.ts new file mode 100644 index 000000000..efc68edb9 --- /dev/null +++ b/packages/shell-api/src/deep-inspect-service-provider-wrapper.ts @@ -0,0 +1,139 @@ +import type { ServiceProvider } from '@mongosh/service-provider-core'; +import { DeepInspectAggregationCursorWrapper } from './deep-inspect-aggregation-cursor-wrapper'; +import { DeepInspectFindCursorWrapper } from './deep-inspect-find-cursor-wrapper'; +import { addCustomInspect } from './custom-inspect'; +import type { PickMethodsByReturnType } from './pick-methods-by-return-type'; +import { DeepInspectRunCommandCursorWrapper } from './deep-inspect-run-command-cursor-wrapper'; +import { DeepInspectChangeStreamWrapper } from './deep-inspect-change-stream-wrapper'; + +export class DeepInspectServiceProviderWrapper implements ServiceProvider { + _sp: ServiceProvider; + + constructor(sp: ServiceProvider) { + this._sp = sp; + } + get bsonLibrary() { + return this._sp.bsonLibrary; + } + + aggregate = (...args: Parameters) => { + const cursor = this._sp.aggregate(...args); + return new DeepInspectAggregationCursorWrapper(cursor); + }; + aggregateDb = (...args: Parameters) => { + const cursor = this._sp.aggregateDb(...args); + return new DeepInspectAggregationCursorWrapper(cursor); + }; + count = forwardedMethod('count'); + estimatedDocumentCount = forwardedMethod('estimatedDocumentCount'); + countDocuments = forwardedMethod('countDocuments'); + distinct = bsonMethod('distinct'); + find = (...args: Parameters) => { + const cursor = this._sp.find(...args); + return new DeepInspectFindCursorWrapper(cursor); + }; + findOneAndDelete = bsonMethod('findOneAndDelete'); + findOneAndReplace = bsonMethod('findOneAndReplace'); + findOneAndUpdate = bsonMethod('findOneAndUpdate'); + getTopologyDescription = forwardedMethod('getTopologyDescription'); + getIndexes = bsonMethod('getIndexes'); + listCollections = bsonMethod('listCollections'); + readPreferenceFromOptions = forwardedMethod('readPreferenceFromOptions'); + watch = (...args: Parameters) => { + const cursor = this._sp.watch(...args); + return new DeepInspectChangeStreamWrapper(cursor); + }; + getSearchIndexes = bsonMethod('getSearchIndexes'); + runCommand = bsonMethod('runCommand'); + runCommandWithCheck = bsonMethod('runCommandWithCheck'); + runCursorCommand = ( + ...args: Parameters + ) => { + const cursor = this._sp.runCursorCommand(...args); + return new DeepInspectRunCommandCursorWrapper(cursor); + }; + dropDatabase = bsonMethod('dropDatabase'); + dropCollection = forwardedMethod('dropCollection'); + bulkWrite = bsonMethod('bulkWrite'); + clientBulkWrite = bsonMethod('clientBulkWrite'); + deleteMany = bsonMethod('deleteMany'); + updateMany = bsonMethod('updateMany'); + updateOne = bsonMethod('updateOne'); + deleteOne = bsonMethod('deleteOne'); + createIndexes = bsonMethod('createIndexes'); + insertMany = bsonMethod('insertMany'); + insertOne = bsonMethod('insertOne'); + replaceOne = bsonMethod('replaceOne'); + initializeBulkOp = forwardedMethod('initializeBulkOp'); // you cannot extend the return value here + createSearchIndexes = forwardedMethod('createSearchIndexes'); + close = forwardedMethod('close'); + suspend = forwardedMethod('suspend'); + renameCollection = forwardedMethod('renameCollection'); + dropSearchIndex = forwardedMethod('dropSearchIndex'); + updateSearchIndex = forwardedMethod('updateSearchIndex'); + listDatabases = bsonMethod('listDatabases'); + authenticate = forwardedMethod('authenticate'); + createCollection = forwardedMethod('createCollection'); + getReadPreference = forwardedMethod('getReadPreference'); + getReadConcern = forwardedMethod('getReadConcern'); + getWriteConcern = forwardedMethod('getWriteConcern'); + + get platform() { + return this._sp.platform; + } + get initialDb() { + return this._sp.initialDb; + } + + getURI = forwardedMethod('getURI'); + getConnectionInfo = forwardedMethod('getConnectionInfo'); + resetConnectionOptions = forwardedMethod('resetConnectionOptions'); + startSession = forwardedMethod('startSession'); + getRawClient = forwardedMethod('getRawClient'); + createClientEncryption = forwardedMethod('createClientEncryption'); + getFleOptions = forwardedMethod('getFleOptions'); + createEncryptedCollection = forwardedMethod('createEncryptedCollection'); + + async getNewConnection( + ...args: Parameters + ): Promise { + const sp = await this._sp.getNewConnection(...args); + return new DeepInspectServiceProviderWrapper(sp); + } +} + +function bsonMethod< + K extends keyof PickMethodsByReturnType> +>( + key: K +): ( + ...args: Parameters[K]> +) => ReturnType[K]> { + return async function ( + this: DeepInspectServiceProviderWrapper, + ...args: Parameters[K]> + ): // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore The returntype already contains a promise + ReturnType[K]> { + const result = await (this._sp[key] as any)(...args); + addCustomInspect(result); + return result; + }; +} + +function forwardedMethod< + K extends keyof PickMethodsByReturnType +>( + key: K +): ( + ...args: Parameters[K]> +) => ReturnType[K]> { + return function ( + this: DeepInspectServiceProviderWrapper, + ...args: Parameters[K]> + ): ReturnType[K]> { + // not wrapping the result at all because forwardedMethod() is for simple + // values only + return (this._sp[key] as any)(...args); + }; +} diff --git a/packages/shell-api/src/pick-methods-by-return-type.ts b/packages/shell-api/src/pick-methods-by-return-type.ts new file mode 100644 index 000000000..20e8cf205 --- /dev/null +++ b/packages/shell-api/src/pick-methods-by-return-type.ts @@ -0,0 +1,5 @@ +export type PickMethodsByReturnType = { + [k in keyof T as NonNullable extends (...args: any[]) => R + ? k + : never]: T[k]; +}; diff --git a/packages/shell-api/src/runtime-independence.spec.ts b/packages/shell-api/src/runtime-independence.spec.ts index a19500e95..a150a987b 100644 --- a/packages/shell-api/src/runtime-independence.spec.ts +++ b/packages/shell-api/src/runtime-independence.spec.ts @@ -59,11 +59,13 @@ describe('Runtime independence', function () { platform: 'CLI', close: sinon.spy(), bsonLibrary: absolutePathRequire(require.resolve('bson')).exports, + getURI: sinon.stub().returns('mongodb://localhost:27017'), + getFleOptions: sinon.stub().returns(undefined), }; const evaluationListener = { onExit: sinon.spy() }; const instanceState = new shellApi.ShellInstanceState(sp as any); instanceState.setEvaluationListener(evaluationListener); - expect(instanceState.initialServiceProvider).to.equal(sp); + expect((instanceState.initialServiceProvider as any)._sp).to.equal(sp); const bsonObj = instanceState.shellBson.ISODate( '2025-01-09T20:43:51+01:00' ); diff --git a/packages/shell-api/src/shell-api.spec.ts b/packages/shell-api/src/shell-api.spec.ts index ac93c6844..702b68c11 100644 --- a/packages/shell-api/src/shell-api.spec.ts +++ b/packages/shell-api/src/shell-api.spec.ts @@ -543,6 +543,8 @@ describe('ShellApi', function () { bus = new EventEmitter(); const newSP = stubInterface(); newSP.initialDb = 'test'; + newSP.platform = 'CLI'; + newSP.bsonLibrary = bson; serviceProvider = stubInterface({ getNewConnection: newSP, }); diff --git a/packages/shell-api/src/shell-instance-state.ts b/packages/shell-api/src/shell-instance-state.ts index 17bd39dd1..f948c94a7 100644 --- a/packages/shell-api/src/shell-instance-state.ts +++ b/packages/shell-api/src/shell-instance-state.ts @@ -51,6 +51,7 @@ import type { AutocompletionContext } from '@mongodb-js/mongodb-ts-autocomplete' import type { JSONSchema } from 'mongodb-schema'; import { analyzeDocuments } from 'mongodb-schema'; import type { BaseCursor } from './abstract-cursor'; +import { DeepInspectServiceProviderWrapper } from './deep-inspect-service-provider-wrapper'; /** * The subset of CLI options that is relevant for the shell API's behavior itself. @@ -203,7 +204,9 @@ export class ShellInstanceState { cliOptions: ShellCliOptions = {}, bsonLibrary: BSONLibrary = initialServiceProvider.bsonLibrary ) { - this.initialServiceProvider = initialServiceProvider; + this.initialServiceProvider = new DeepInspectServiceProviderWrapper( + initialServiceProvider + ); this.bsonLibrary = bsonLibrary; this.messageBus = messageBus; this.shellApi = new ShellApi(this); @@ -220,11 +223,11 @@ export class ShellInstanceState { undefined, undefined, undefined, - initialServiceProvider + this.initialServiceProvider ); this.mongos.push(mongo); this.currentDb = mongo.getDB( - initialServiceProvider.initialDb || DEFAULT_DB + this.initialServiceProvider.initialDb || DEFAULT_DB ); } else { this.currentDb = new NoDatabase() as DatabaseWithSchema;