From b1940e68917be24171ed46eadce81860027de3e1 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 26 Apr 2021 17:48:26 +0200 Subject: [PATCH 1/2] chore(shell-api): combine common Cursor + AbstractCursor code Create an `AbstractCursor` class (named like the driver equivalent) as a common base class for `Cursor` and `AggregationCursor` to reduce code duplication. --- packages/i18n/src/locales/en_US.ts | 20 +++ packages/shell-api/src/abstract-cursor.ts | 159 ++++++++++++++++++ .../shell-api/src/aggregation-cursor.spec.ts | 2 +- packages/shell-api/src/aggregation-cursor.ts | 110 +++--------- packages/shell-api/src/collection.spec.ts | 2 +- packages/shell-api/src/cursor.ts | 140 +++------------ packages/shell-api/src/database.spec.ts | 2 +- packages/shell-api/src/decorators.ts | 16 +- packages/shell-api/src/explainable-cursor.ts | 4 +- 9 files changed, 239 insertions(+), 216 deletions(-) create mode 100644 packages/shell-api/src/abstract-cursor.ts diff --git a/packages/i18n/src/locales/en_US.ts b/packages/i18n/src/locales/en_US.ts index b3358db159..237d8f2123 100644 --- a/packages/i18n/src/locales/en_US.ts +++ b/packages/i18n/src/locales/en_US.ts @@ -206,6 +206,11 @@ const translations: Catalog = { description: 'Applies the first argument, a function, to each document visited by the cursor and collects the return values from successive application into an array.', example: 'db.collection.aggregate(pipeline, options).map(function)' }, + maxTimeMS: { + link: 'https://docs.mongodb.com/manual/reference/method/cursor.maxTimeMS', + description: 'Specifies a cumulative time limit in milliseconds for processing operations on a cursor.', + example: 'db.collection.aggregate(pipeline, options).maxTimeMS(timeLimit)' + }, next: { link: 'https://docs.mongodb.com/manual/reference/method/cursor.next', description: 'The next document in the cursor returned by the db.collection.aggregate() method. NOTE: if the cursor is tailable with awaitData then next will block until a document is returned. To check if a document is in the cursor\'s batch without waiting, use tryNext instead', @@ -235,6 +240,21 @@ const translations: Catalog = { batchSize: { description: 'Specifies the number of documents that mongosh displays at once.', example: 'db.collection.aggregate(pipeline, options).batchSize(10)' + }, + projection: { + link: '', + description: 'Sets a field projection for the query.', + example: 'db.collection.aggregate(pipeline, options).projection(field)' + }, + sort: { + link: 'https://docs.mongodb.com/manual/reference/method/cursor.sort', + description: 'Specifies the order in which the query returns matching documents. You must apply sort() to the cursor before retrieving any documents from the database.', + example: 'db.collection.aggregate(pipeline, options).sort(sortDocument)' + }, + skip: { + link: 'https://docs.mongodb.com/manual/reference/method/cursor.skip', + description: 'Call the cursor.skip() method on a cursor to control where MongoDB begins returning results. This approach may be useful in implementing paginated results.', + example: 'db.collection.aggregate(pipeline, options).skip(offsetNumber)' } } } diff --git a/packages/shell-api/src/abstract-cursor.ts b/packages/shell-api/src/abstract-cursor.ts new file mode 100644 index 0000000000..f0d0e68bf0 --- /dev/null +++ b/packages/shell-api/src/abstract-cursor.ts @@ -0,0 +1,159 @@ +import { + shellApiClassNoHelp, + hasAsyncChild, + ShellApiClass, + returnsPromise, + toShellResult +} from './decorators'; +import type Mongo from './mongo'; +import type { + Document, + ExplainVerbosityLike, + FindCursor as ServiceProviderCursor, + AggregationCursor as ServiceProviderAggregationCursor, +} from '@mongosh/service-provider-core'; +import { asPrintable } from './enums'; +import { CursorIterationResult } from './result'; +import { iterate, validateExplainableVerbosity, markAsExplainOutput } from './helpers'; + +@shellApiClassNoHelp +@hasAsyncChild +export abstract class AbstractCursor extends ShellApiClass { + _mongo: Mongo; + abstract _cursor: ServiceProviderAggregationCursor | ServiceProviderCursor; + _currentIterationResult: CursorIterationResult | null = null; + _batchSize: number | null = null; + + constructor(mongo: Mongo) { + super(); + this._mongo = mongo; + } + + /** + * Internal method to determine what is printed for this class. + */ + async [asPrintable](): Promise { + return (await toShellResult(this._currentIterationResult ?? await this._it())).printable; + } + + async _it(): Promise { + const results = this._currentIterationResult = new CursorIterationResult(); + await iterate(results, this._cursor, this._batchSize ?? await this._mongo._batchSize()); + results.cursorHasMore = !this.isExhausted(); + return results; + } + + batchSize(size: number): this { + this._batchSize = size; + return this; + } + + @returnsPromise + async close(options: Document): Promise { + await this._cursor.close(options); + } + + @returnsPromise + async forEach(f: (doc: Document) => void): Promise { + return this._cursor.forEach(f); + } + + @returnsPromise + async hasNext(): Promise { + return this._cursor.hasNext(); + } + + @returnsPromise + async tryNext(): Promise { + return this._cursor.tryNext(); + } + + async* [Symbol.asyncIterator]() { + let doc; + while ((doc = await this.tryNext()) !== null) { + yield doc; + } + } + + isClosed(): boolean { + return this._cursor.closed; + } + + isExhausted(): boolean { + return this.isClosed() && this.objsLeftInBatch() === 0; + } + + @returnsPromise + async itcount(): Promise { + let count = 0; + while (await this.tryNext()) { + count++; + } + return count; + } + + map(f: (doc: Document) => Document): this { + this._cursor.map(f); + return this; + } + + maxTimeMS(value: number): this { + this._cursor.maxTimeMS(value); + return this; + } + + @returnsPromise + async next(): Promise { + return this._cursor.next(); + } + + projection(spec: Document): this { + this._cursor.project(spec); + return this; + } + + skip(value: number): this { + this._cursor.skip(value); + return this; + } + + sort(spec: Document): this { + this._cursor.sort(spec); + return this; + } + + objsLeftInBatch(): number { + return this._cursor.bufferedCount(); + } + + @returnsPromise + async explain(verbosity?: ExplainVerbosityLike): Promise { + // TODO: @maurizio we should probably move this in the Explain class? + // NOTE: the node driver always returns the full explain plan + // for Cursor and the queryPlanner explain for AggregationCursor. + if (verbosity !== undefined) { + verbosity = validateExplainableVerbosity(verbosity); + } + const fullExplain: any = await this._cursor.explain(verbosity); + + const explain: any = { + ...fullExplain + }; + + if ( + verbosity !== 'executionStats' && + verbosity !== 'allPlansExecution' && + explain.executionStats + ) { + delete explain.executionStats; + } + + if (verbosity === 'executionStats' && + explain.executionStats && + explain.executionStats.allPlansExecution) { + delete explain.executionStats.allPlansExecution; + } + + return markAsExplainOutput(explain); + } +} diff --git a/packages/shell-api/src/aggregation-cursor.spec.ts b/packages/shell-api/src/aggregation-cursor.spec.ts index 26e27a8e94..16a8009e1c 100644 --- a/packages/shell-api/src/aggregation-cursor.spec.ts +++ b/packages/shell-api/src/aggregation-cursor.spec.ts @@ -203,7 +203,7 @@ describe('AggregationCursor', () => { it('returns an ExplainOutput object', async() => { const explained = await shellApiCursor.explain(); - expect(spCursor.explain).to.have.been.calledWith('queryPlanner'); + expect(spCursor.explain).to.have.been.calledWith(); expect((await toShellResult(explained)).type).to.equal('ExplainOutput'); expect((await toShellResult(explained)).printable).to.deep.equal({ ok: 1 }); }); diff --git a/packages/shell-api/src/aggregation-cursor.ts b/packages/shell-api/src/aggregation-cursor.ts index 02d8b9acd9..3e1b5451fb 100644 --- a/packages/shell-api/src/aggregation-cursor.ts +++ b/packages/shell-api/src/aggregation-cursor.ts @@ -3,125 +3,61 @@ import { shellApiClassDefault, returnsPromise, returnType, - hasAsyncChild, - ShellApiClass, - toShellResult + hasAsyncChild } from './decorators'; import type { AggregationCursor as ServiceProviderAggregationCursor, - ExplainVerbosityLike, Document } from '@mongosh/service-provider-core'; -import { CursorIterationResult } from './result'; -import { asPrintable } from './enums'; -import { iterate, validateExplainableVerbosity, markAsExplainOutput } from './helpers'; +import { AbstractCursor } from './abstract-cursor'; @shellApiClassDefault @hasAsyncChild -export default class AggregationCursor extends ShellApiClass { - _mongo: Mongo; +export default class AggregationCursor extends AbstractCursor { _cursor: ServiceProviderAggregationCursor; - _currentIterationResult: CursorIterationResult | null = null; - _batchSize: number | null = null; constructor(mongo: Mongo, cursor: ServiceProviderAggregationCursor) { - super(); + super(mongo); this._cursor = cursor; - this._mongo = mongo; } - async _it(): Promise { - const results = this._currentIterationResult = new CursorIterationResult(); - await iterate(results, this._cursor, this._batchSize ?? await this._mongo._batchSize()); - results.cursorHasMore = !this.isExhausted(); - return results; - } - - /** - * Internal method to determine what is printed for this class. - */ - async [asPrintable](): Promise { - return (await toShellResult(this._currentIterationResult ?? await this._it())).printable; - } - - @returnsPromise - async close(options: Document): Promise { - await this._cursor.close(options); - } - - @returnsPromise - async forEach(f: (doc: Document) => void): Promise { - return this._cursor.forEach(f); - } - - @returnsPromise - async hasNext(): Promise { - return this._cursor.hasNext(); - } - - @returnsPromise - async tryNext(): Promise { - return this._cursor.tryNext(); - } - - async* [Symbol.asyncIterator]() { - let doc; - while ((doc = await this.tryNext()) !== null) { - yield doc; - } - } - - isClosed(): boolean { - return this._cursor.closed; - } - - isExhausted(): boolean { - return this.isClosed() && this.objsLeftInBatch() === 0; + @returnType('AggregationCursor') + map(f: (doc: Document) => Document): this { + return super.map(f); } - objsLeftInBatch(): number { - return this._cursor.bufferedCount(); + @returnType('AggregationCursor') + maxTimeMS(value: number): this { + return super.maxTimeMS(value); } @returnsPromise - async itcount(): Promise { - let count = 0; - while (await this.tryNext()) { - count++; - } - return count; + async toArray(): Promise { + return this._cursor.toArray(); } @returnType('AggregationCursor') - map(f: (doc: Document) => Document): AggregationCursor { - this._cursor.map(f); + pretty(): this { return this; } - @returnsPromise - async next(): Promise { - return this._cursor.next(); - } - - @returnsPromise - async toArray(): Promise { - return this._cursor.toArray(); + @returnType('AggregationCursor') + batchSize(size: number): this { + return super.batchSize(size); } - @returnsPromise - async explain(verbosity: ExplainVerbosityLike = 'queryPlanner'): Promise { - verbosity = validateExplainableVerbosity(verbosity); - return markAsExplainOutput(await this._cursor.explain(verbosity)); + @returnType('AggregationCursor') + projection(spec: Document): this { + return super.projection(spec); } @returnType('AggregationCursor') - pretty(): AggregationCursor { - return this; + skip(value: number): this { + return super.skip(value); } @returnType('AggregationCursor') - batchSize(size: number): AggregationCursor { - this._batchSize = size; - return this; + sort(spec: Document): this { + return super.sort(spec); } } diff --git a/packages/shell-api/src/collection.spec.ts b/packages/shell-api/src/collection.spec.ts index 48ea8e0dc2..398503f1ca 100644 --- a/packages/shell-api/src/collection.spec.ts +++ b/packages/shell-api/src/collection.spec.ts @@ -227,7 +227,7 @@ describe('Collection', () => { { explain: true } ); - expect(explainResult).to.equal(expectedExplainResult); + expect(explainResult).to.deep.equal(expectedExplainResult); expect((await toShellResult(explainResult)).type).to.equal('ExplainOutput'); expect(serviceProviderCursor.explain).to.have.been.calledOnce; }); diff --git a/packages/shell-api/src/cursor.ts b/packages/shell-api/src/cursor.ts index c28b805555..08032ece18 100644 --- a/packages/shell-api/src/cursor.ts +++ b/packages/shell-api/src/cursor.ts @@ -4,59 +4,36 @@ import { returnsPromise, returnType, serverVersions, - ShellApiClass, shellApiClassDefault, - toShellResult, deprecated } from './decorators'; import { ServerVersions, - asPrintable, CURSOR_FLAGS } from './enums'; -import { +import type { FindCursor as ServiceProviderCursor, CursorFlag, Document, CollationOptions, - ExplainVerbosityLike, ReadPreferenceLike, ReadConcernLevelId, TagSet, HedgeOptions } from '@mongosh/service-provider-core'; -import { iterate, validateExplainableVerbosity, markAsExplainOutput } from './helpers'; import Mongo from './mongo'; -import { CursorIterationResult } from './result'; import { printWarning } from './deprecation-warning'; +import { AbstractCursor } from './abstract-cursor'; @shellApiClassDefault @hasAsyncChild -export default class Cursor extends ShellApiClass { - _mongo: Mongo; +export default class Cursor extends AbstractCursor { _cursor: ServiceProviderCursor; - _currentIterationResult: CursorIterationResult | null = null; _tailable = false; - _batchSize: number | null = null; constructor(mongo: Mongo, cursor: ServiceProviderCursor) { - super(); + super(mongo); this._cursor = cursor; - this._mongo = mongo; - } - - /** - * Internal method to determine what is printed for this class. - */ - async [asPrintable](): Promise { - return (await toShellResult(this._currentIterationResult ?? await this._it())).printable; - } - - async _it(): Promise { - const results = this._currentIterationResult = new CursorIterationResult(); - await iterate(results, this._cursor, this._batchSize ?? await this._mongo._batchSize()); - results.cursorHasMore = !this.isExhausted(); - return results; } /** @@ -100,17 +77,12 @@ export default class Cursor extends ShellApiClass { } @returnType('Cursor') - batchSize(size: number): Cursor { - this._batchSize = size; + batchSize(size: number): this { + super.batchSize(size); this._cursor.batchSize(size); return this; } - @returnsPromise - async close(options: Document): Promise { - await this._cursor.close(options); - } - @returnType('Cursor') @serverVersions(['3.4.0', ServerVersions.latest]) collation(spec: CollationOptions): Cursor { @@ -131,42 +103,6 @@ export default class Cursor extends ShellApiClass { return this._cursor.count(); } - @returnsPromise - async explain(verbosity?: ExplainVerbosityLike): Promise { - // TODO: @maurizio we should probably move this in the Explain class? - // NOTE: the node driver always returns the full explain plan - // for Cursor and the queryPlanner explain for AggregationCursor. - if (verbosity !== undefined) { - verbosity = validateExplainableVerbosity(verbosity); - } - const fullExplain: any = await this._cursor.explain(verbosity); - - const explain: any = { - ...fullExplain - }; - - if ( - verbosity !== 'executionStats' && - verbosity !== 'allPlansExecution' && - explain.executionStats - ) { - delete explain.executionStats; - } - - if (verbosity === 'executionStats' && - explain.executionStats && - explain.executionStats.allPlansExecution) { - delete explain.executionStats.allPlansExecution; - } - - return markAsExplainOutput(explain); - } - - @returnsPromise - async forEach(f: (doc: Document) => void): Promise { - return this._cursor.forEach(f); - } - @returnsPromise async hasNext(): Promise { if (this._tailable) { @@ -176,19 +112,7 @@ export default class Cursor extends ShellApiClass { this._mongo._internalState.context.print ); } - return this._cursor.hasNext(); - } - - @returnsPromise - async tryNext(): Promise { - return this._cursor.tryNext(); - } - - async* [Symbol.asyncIterator]() { - let doc; - while ((doc = await this.tryNext()) !== null) { - yield doc; - } + return super.hasNext(); } @returnType('Cursor') @@ -197,23 +121,6 @@ export default class Cursor extends ShellApiClass { return this; } - isClosed(): boolean { - return this._cursor.closed; - } - - isExhausted(): boolean { - return this.isClosed() && this.objsLeftInBatch() === 0; - } - - @returnsPromise - async itcount(): Promise { - let count = 0; - while (await this.tryNext()) { - count++; - } - return count; - } - @returnType('Cursor') limit(value: number): Cursor { this._cursor.limit(value); @@ -221,9 +128,8 @@ export default class Cursor extends ShellApiClass { } @returnType('Cursor') - map(f: (doc: Document) => Document): Cursor { - this._cursor.map(f); - return this; + map(f: (doc: Document) => Document): this { + return super.map(f); } @returnType('Cursor') @@ -233,9 +139,8 @@ export default class Cursor extends ShellApiClass { } @returnType('Cursor') - maxTimeMS(value: number): Cursor { - this._cursor.maxTimeMS(value); - return this; + maxTimeMS(value: number): this { + return super.maxTimeMS(value); } @returnType('Cursor') @@ -260,7 +165,7 @@ export default class Cursor extends ShellApiClass { this._mongo._internalState.context.print ); } - return this._cursor.next(); + return super.next(); } @returnType('Cursor') @@ -276,9 +181,8 @@ export default class Cursor extends ShellApiClass { } @returnType('Cursor') - projection(spec: Document): Cursor { - this._cursor.project(spec); - return this; + projection(spec: Document): this { + return super.projection(spec); } @returnType('Cursor') @@ -312,15 +216,13 @@ export default class Cursor extends ShellApiClass { } @returnType('Cursor') - skip(value: number): Cursor { - this._cursor.skip(value); - return this; + skip(value: number): this { + return super.skip(value); } @returnType('Cursor') - sort(spec: Document): Cursor { - this._cursor.sort(spec); - return this; + sort(spec: Document): this { + return super.sort(spec); } @returnType('Cursor') @@ -340,7 +242,7 @@ export default class Cursor extends ShellApiClass { } @returnType('Cursor') - pretty(): Cursor { + pretty(): this { return this; } @@ -359,10 +261,6 @@ export default class Cursor extends ShellApiClass { return this; } - objsLeftInBatch(): number { - return this._cursor.bufferedCount(); - } - @returnType('Cursor') readConcern(level: ReadConcernLevelId): Cursor { this._cursor = this._cursor.withReadConcern({ level }); diff --git a/packages/shell-api/src/database.spec.ts b/packages/shell-api/src/database.spec.ts index 331d2cbe12..b710bfaa02 100644 --- a/packages/shell-api/src/database.spec.ts +++ b/packages/shell-api/src/database.spec.ts @@ -308,7 +308,7 @@ describe('Database', () => { { explain: true } ); - expect(explainResult).to.equal(expectedExplainResult); + expect(explainResult).to.deep.equal(expectedExplainResult); expect(serviceProviderCursor.explain).to.have.been.calledOnce; }); diff --git a/packages/shell-api/src/decorators.ts b/packages/shell-api/src/decorators.ts index d5917ad2e4..b84aad1a6c 100644 --- a/packages/shell-api/src/decorators.ts +++ b/packages/shell-api/src/decorators.ts @@ -179,8 +179,8 @@ type ClassHelp = { attr: { name: string; description: string }[]; }; -export const toIgnore = ['constructor']; -export function shellApiClassDefault(constructor: Function): void { +export const toIgnore = ['constructor', 'help']; +export function shellApiClassGeneric(constructor: Function, hasHelp: boolean): void { const className = constructor.name; const classHelpKeyPrefix = `shell-api.classes.${className}.help`; const classHelp: ClassHelp = { @@ -289,7 +289,17 @@ export function shellApiClassDefault(constructor: Function): void { constructor.prototype[asPrintable] || ShellApiClass.prototype[asPrintable]; addHiddenDataProperty(constructor.prototype, shellApiType, className); - signatures[className] = classSignature; + if (hasHelp) { + signatures[className] = classSignature; + } +} + +export function shellApiClassDefault(constructor: Function): void { + shellApiClassGeneric(constructor, true); +} + +export function shellApiClassNoHelp(constructor: Function): void { + shellApiClassGeneric(constructor, false); } function markImplicitlyAwaited Promise>(orig: T): ((...args: Parameters) => Promise) { diff --git a/packages/shell-api/src/explainable-cursor.ts b/packages/shell-api/src/explainable-cursor.ts index a6d4ca9ef2..2a2007aaaa 100644 --- a/packages/shell-api/src/explainable-cursor.ts +++ b/packages/shell-api/src/explainable-cursor.ts @@ -28,7 +28,7 @@ export default class ExplainableCursor extends Cursor { } @returnType('ExplainableCursor') - map(f: (doc: Document) => Document): ExplainableCursor { - return super.map(f) as ExplainableCursor; + map(f: (doc: Document) => Document): this { + return super.map(f); } } From d819c296be3c090d941b0e29533916b992c96f3b Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 26 Apr 2021 18:05:56 +0200 Subject: [PATCH 2/2] chore(shell-api): allow `@returnType('this')` This helps with more code deduplication, because it can make wrappers go away that were only needed to re-specify the return type in a subclass. --- packages/shell-api/src/abstract-cursor.ts | 19 +++++++- packages/shell-api/src/aggregation-cursor.ts | 47 +------------------- packages/shell-api/src/cursor.ts | 37 +-------------- packages/shell-api/src/decorators.ts | 11 +++-- packages/shell-api/src/explainable-cursor.ts | 11 ++--- 5 files changed, 31 insertions(+), 94 deletions(-) diff --git a/packages/shell-api/src/abstract-cursor.ts b/packages/shell-api/src/abstract-cursor.ts index f0d0e68bf0..049567ef5a 100644 --- a/packages/shell-api/src/abstract-cursor.ts +++ b/packages/shell-api/src/abstract-cursor.ts @@ -3,7 +3,8 @@ import { hasAsyncChild, ShellApiClass, returnsPromise, - toShellResult + toShellResult, + returnType } from './decorators'; import type Mongo from './mongo'; import type { @@ -43,6 +44,7 @@ export abstract class AbstractCursor extends ShellApiClass { return results; } + @returnType('this') batchSize(size: number): this { this._batchSize = size; return this; @@ -92,11 +94,23 @@ export abstract class AbstractCursor extends ShellApiClass { return count; } + @returnsPromise + async toArray(): Promise { + return this._cursor.toArray(); + } + + @returnType('this') + pretty(): this { + return this; + } + + @returnType('this') map(f: (doc: Document) => Document): this { this._cursor.map(f); return this; } + @returnType('this') maxTimeMS(value: number): this { this._cursor.maxTimeMS(value); return this; @@ -107,16 +121,19 @@ export abstract class AbstractCursor extends ShellApiClass { return this._cursor.next(); } + @returnType('this') projection(spec: Document): this { this._cursor.project(spec); return this; } + @returnType('this') skip(value: number): this { this._cursor.skip(value); return this; } + @returnType('this') sort(spec: Document): this { this._cursor.sort(spec); return this; diff --git a/packages/shell-api/src/aggregation-cursor.ts b/packages/shell-api/src/aggregation-cursor.ts index 3e1b5451fb..2aae7e3f37 100644 --- a/packages/shell-api/src/aggregation-cursor.ts +++ b/packages/shell-api/src/aggregation-cursor.ts @@ -1,13 +1,10 @@ -import Mongo from './mongo'; +import type Mongo from './mongo'; import { shellApiClassDefault, - returnsPromise, - returnType, hasAsyncChild } from './decorators'; import type { - AggregationCursor as ServiceProviderAggregationCursor, - Document + AggregationCursor as ServiceProviderAggregationCursor } from '@mongosh/service-provider-core'; import { AbstractCursor } from './abstract-cursor'; @@ -20,44 +17,4 @@ export default class AggregationCursor extends AbstractCursor { super(mongo); this._cursor = cursor; } - - @returnType('AggregationCursor') - map(f: (doc: Document) => Document): this { - return super.map(f); - } - - @returnType('AggregationCursor') - maxTimeMS(value: number): this { - return super.maxTimeMS(value); - } - - @returnsPromise - async toArray(): Promise { - return this._cursor.toArray(); - } - - @returnType('AggregationCursor') - pretty(): this { - return this; - } - - @returnType('AggregationCursor') - batchSize(size: number): this { - return super.batchSize(size); - } - - @returnType('AggregationCursor') - projection(spec: Document): this { - return super.projection(spec); - } - - @returnType('AggregationCursor') - skip(value: number): this { - return super.skip(value); - } - - @returnType('AggregationCursor') - sort(spec: Document): this { - return super.sort(spec); - } } diff --git a/packages/shell-api/src/cursor.ts b/packages/shell-api/src/cursor.ts index 08032ece18..da77c9ba6a 100644 --- a/packages/shell-api/src/cursor.ts +++ b/packages/shell-api/src/cursor.ts @@ -21,7 +21,7 @@ import type { TagSet, HedgeOptions } from '@mongosh/service-provider-core'; -import Mongo from './mongo'; +import type Mongo from './mongo'; import { printWarning } from './deprecation-warning'; import { AbstractCursor } from './abstract-cursor'; @@ -127,22 +127,12 @@ export default class Cursor extends AbstractCursor { return this; } - @returnType('Cursor') - map(f: (doc: Document) => Document): this { - return super.map(f); - } - @returnType('Cursor') max(indexBounds: Document): Cursor { this._cursor.max(indexBounds); return this; } - @returnType('Cursor') - maxTimeMS(value: number): this { - return super.maxTimeMS(value); - } - @returnType('Cursor') @serverVersions(['3.2.0', ServerVersions.latest]) maxAwaitTimeMS(value: number): Cursor { @@ -180,11 +170,6 @@ export default class Cursor extends AbstractCursor { return this; } - @returnType('Cursor') - projection(spec: Document): this { - return super.projection(spec); - } - @returnType('Cursor') readPref(mode: ReadPreferenceLike, tagSet?: TagSet[], hedgeOptions?: HedgeOptions): Cursor { let pref: ReadPreferenceLike; @@ -215,16 +200,6 @@ export default class Cursor extends AbstractCursor { return this._cursor.count(); } - @returnType('Cursor') - skip(value: number): this { - return super.skip(value); - } - - @returnType('Cursor') - sort(spec: Document): this { - return super.sort(spec); - } - @returnType('Cursor') @serverVersions(['3.2.0', ServerVersions.latest]) tailable(opts = { awaitData: false }): Cursor { @@ -236,16 +211,6 @@ export default class Cursor extends AbstractCursor { return this; } - @returnsPromise - async toArray(): Promise { - return this._cursor.toArray(); - } - - @returnType('Cursor') - pretty(): this { - return this; - } - @deprecated @serverVersions([ServerVersions.earliest, '4.0.0']) maxScan(): void { diff --git a/packages/shell-api/src/decorators.ts b/packages/shell-api/src/decorators.ts index b84aad1a6c..04ffc92193 100644 --- a/packages/shell-api/src/decorators.ts +++ b/packages/shell-api/src/decorators.ts @@ -222,7 +222,7 @@ export function shellApiClassGeneric(constructor: Function, hasHelp: boolean): v type: 'function', serverVersions: method.serverVersions, topologies: method.topologies, - returnType: method.returnType, + returnType: method.returnType === 'this' ? className : method.returnType, returnsPromise: method.returnsPromise, deprecated: method.deprecated, platforms: method.platforms @@ -250,8 +250,11 @@ export function shellApiClassGeneric(constructor: Function, hasHelp: boolean): v }); } - const superClass = Object.getPrototypeOf(constructor.prototype); - if (superClass.constructor.name !== 'ShellApiClass' && superClass.constructor !== Array) { + let superClass = constructor.prototype; + while ((superClass = Object.getPrototypeOf(superClass)) !== null) { + if (superClass.constructor.name === 'ShellApiClass' || superClass.constructor === Array) { + break; + } const superClassHelpKeyPrefix = `shell-api.classes.${superClass.constructor.name}.help`; for (const propertyName of Object.getOwnPropertyNames(superClass)) { const descriptor = Object.getOwnPropertyDescriptor(superClass, propertyName); @@ -268,7 +271,7 @@ export function shellApiClassGeneric(constructor: Function, hasHelp: boolean): v type: 'function', serverVersions: method.serverVersions, topologies: method.topologies, - returnType: method.returnType, + returnType: method.returnType === 'this' ? className : method.returnType, returnsPromise: method.returnsPromise, deprecated: method.deprecated, platforms: method.platforms diff --git a/packages/shell-api/src/explainable-cursor.ts b/packages/shell-api/src/explainable-cursor.ts index 2a2007aaaa..c1826d1165 100644 --- a/packages/shell-api/src/explainable-cursor.ts +++ b/packages/shell-api/src/explainable-cursor.ts @@ -1,8 +1,8 @@ -import { shellApiClassDefault, returnType } from './decorators'; +import { shellApiClassDefault } from './decorators'; import Cursor from './cursor'; -import Mongo from './mongo'; +import type Mongo from './mongo'; import { asPrintable } from './enums'; -import type { Document, ExplainVerbosityLike } from '@mongosh/service-provider-core'; +import type { ExplainVerbosityLike } from '@mongosh/service-provider-core'; @shellApiClassDefault export default class ExplainableCursor extends Cursor { @@ -26,9 +26,4 @@ export default class ExplainableCursor extends Cursor { this._explained ??= await this._baseCursor.explain(this._verbosity); return this._explained; } - - @returnType('ExplainableCursor') - map(f: (doc: Document) => Document): this { - return super.map(f); - } }