diff --git a/src/db.ts b/src/db.ts index 4fc77994e4..4410e87480 100644 --- a/src/db.ts +++ b/src/db.ts @@ -34,7 +34,11 @@ import { DropDatabaseOptions, DropCollectionOptions } from './operations/drop'; -import { ListCollectionsCursor, ListCollectionsOptions } from './operations/list_collections'; +import { + CollectionInfo, + ListCollectionsCursor, + ListCollectionsOptions +} from './operations/list_collections'; import { ProfilingLevelOperation, ProfilingLevelOptions } from './operations/profiling_level'; import { RemoveUserOperation, RemoveUserOptions } from './operations/remove_user'; import { RenameOperation, RenameOptions } from './operations/rename'; @@ -358,8 +362,25 @@ export class Db { * @param filter - Query to filter collections by * @param options - Optional settings for the command */ - listCollections(filter?: Document, options?: ListCollectionsOptions): ListCollectionsCursor { - return new ListCollectionsCursor(this, filter || {}, resolveOptions(this, options)); + listCollections( + filter: Document, + options: Exclude & { nameOnly: true } + ): ListCollectionsCursor>; + listCollections( + filter: Document, + options: Exclude & { nameOnly: false } + ): ListCollectionsCursor; + listCollections< + T extends Pick | CollectionInfo = + | Pick + | CollectionInfo + >(filter?: Document, options?: ListCollectionsOptions): ListCollectionsCursor; + listCollections< + T extends Pick | CollectionInfo = + | Pick + | CollectionInfo + >(filter: Document = {}, options: ListCollectionsOptions = {}): ListCollectionsCursor { + return new ListCollectionsCursor(this, filter, resolveOptions(this, options)); } /** diff --git a/src/index.ts b/src/index.ts index 901323e972..3260960ea6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -269,7 +269,7 @@ export type { IndexDirection } from './operations/indexes'; export type { InsertOneResult, InsertOneOptions, InsertManyResult } from './operations/insert'; -export type { ListCollectionsOptions } from './operations/list_collections'; +export type { ListCollectionsOptions, CollectionInfo } from './operations/list_collections'; export type { ListDatabasesResult, ListDatabasesOptions } from './operations/list_databases'; export type { MapFunction, diff --git a/src/operations/is_capped.ts b/src/operations/is_capped.ts index 7fb87d6357..703d104324 100644 --- a/src/operations/is_capped.ts +++ b/src/operations/is_capped.ts @@ -22,7 +22,7 @@ export class IsCappedOperation extends AbstractOperation { coll.s.db .listCollections( { name: coll.collectionName }, - { ...this.options, readPreference: this.readPreference, session } + { ...this.options, nameOnly: false, readPreference: this.readPreference, session } ) .toArray((err, collections) => { if (err || !collections) return callback(err); diff --git a/src/operations/list_collections.ts b/src/operations/list_collections.ts index 8b9fe77589..0481a552e3 100644 --- a/src/operations/list_collections.ts +++ b/src/operations/list_collections.ts @@ -2,7 +2,7 @@ import { CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; import { maxWireVersion, Callback, getTopology, MongoDBNamespace } from '../utils'; import * as CONSTANTS from '../constants'; -import type { Document } from '../bson'; +import type { Binary, Document } from '../bson'; import type { Server } from '../sdam/server'; import type { Db } from '../db'; import { AbstractCursor } from '../cursor/abstract_cursor'; @@ -105,7 +105,21 @@ export class ListCollectionsOperation extends CommandOperation { } /** @public */ -export class ListCollectionsCursor extends AbstractCursor { +export interface CollectionInfo extends Document { + name: string; + type?: string; + options?: Document; + info?: { + readOnly?: false; + uuid?: Binary; + }; + idIndex?: Document; +} + +/** @public */ +export class ListCollectionsCursor< + T extends Pick | CollectionInfo = CollectionInfo +> extends AbstractCursor { parent: Db; filter: Document; options?: ListCollectionsOptions; @@ -117,7 +131,7 @@ export class ListCollectionsCursor extends AbstractCursor { this.options = options; } - clone(): ListCollectionsCursor { + clone(): ListCollectionsCursor { return new ListCollectionsCursor(this.parent, this.filter, { ...this.options, ...this.cursorOptions diff --git a/src/operations/options_operation.ts b/src/operations/options_operation.ts index af20101b67..4397718a77 100644 --- a/src/operations/options_operation.ts +++ b/src/operations/options_operation.ts @@ -23,7 +23,7 @@ export class OptionsOperation extends AbstractOperation { coll.s.db .listCollections( { name: coll.collectionName }, - { ...this.options, readPreference: this.readPreference, session } + { ...this.options, nameOnly: false, readPreference: this.readPreference, session } ) .toArray((err, collections) => { if (err || !collections) return callback(err); @@ -31,7 +31,7 @@ export class OptionsOperation extends AbstractOperation { return callback(new MongoDriverError(`collection ${coll.namespace} not found`)); } - callback(err, collections[0].options || null); + callback(err, collections[0].options); }); } } diff --git a/test/types/list_collections.test-d.ts b/test/types/list_collections.test-d.ts new file mode 100644 index 0000000000..8e6883107b --- /dev/null +++ b/test/types/list_collections.test-d.ts @@ -0,0 +1,45 @@ +import { expectType, expectNotType } from 'tsd'; + +import { MongoClient } from '../../src/mongo_client'; +import type { CollectionInfo, ListCollectionsCursor } from '../../src/operations/list_collections'; + +const db = new MongoClient('').db(); + +type EitherCollectionInfoResult = CollectionInfo | Pick; + +// We default to the CollectionInfo result type +expectType | CollectionInfo>>( + db.listCollections() +); +// By default it isn't narrowed to either type +expectNotType>>(db.listCollections()); +expectNotType>(db.listCollections()); + +// Testing each argument variation +db.listCollections(); +db.listCollections({ a: 2 }); +db.listCollections({ a: 2 }, { batchSize: 2 }); + +const collections = await db.listCollections().toArray(); +expectType(collections); + +const nameOnly = await db.listCollections({}, { nameOnly: true }).toArray(); +expectType[]>(nameOnly); + +const fullInfo = await db.listCollections({}, { nameOnly: false }).toArray(); +expectType(fullInfo); + +const couldBeEither = await db.listCollections({}, { nameOnly: Math.random() > 0.5 }).toArray(); +expectType(couldBeEither); + +// Showing here that: +// regardless of the option the generic parameter can be used to coerce the result if need be +// note the nameOnly: false, yet strings are returned +const overridden = await db + .listCollections>({}, { nameOnly: false }) + .toArray(); +expectType[]>(overridden); +const overriddenWithToArray = await db + .listCollections({}, { nameOnly: false }) + .toArray>(); +expectType[]>(overriddenWithToArray); diff --git a/test/types/community/tsconfig.json b/test/types/tsconfig.json similarity index 100% rename from test/types/community/tsconfig.json rename to test/types/tsconfig.json