diff --git a/packages/autocomplete/index.spec.ts b/packages/autocomplete/index.spec.ts index af48df22b9..f8ba358033 100644 --- a/packages/autocomplete/index.spec.ts +++ b/packages/autocomplete/index.spec.ts @@ -7,6 +7,7 @@ let collections: string[]; let databases: string[]; const standalone440 = { topology: () => Topologies.Standalone, + apiVersionInfo: () => undefined, connectionInfo: () => ({ is_atlas: false, is_data_lake: false, @@ -15,8 +16,16 @@ const standalone440 = { getCollectionCompletionsForCurrentDb: () => collections, getDatabaseCompletions: () => databases }; +const apiStrictParams = { + topology: () => Topologies.Standalone, + apiVersionInfo: () => ({ version: '1', strict: true, deprecationErrors: false }), + connectionInfo: () => undefined, + getCollectionCompletionsForCurrentDb: () => collections, + getDatabaseCompletions: () => databases +}; const sharded440 = { topology: () => Topologies.Sharded, + apiVersionInfo: () => undefined, connectionInfo: () => ({ is_atlas: false, is_data_lake: false, @@ -28,6 +37,7 @@ const sharded440 = { const standalone300 = { topology: () => Topologies.Standalone, + apiVersionInfo: () => undefined, connectionInfo: () => ({ is_atlas: false, is_data_lake: false, @@ -38,6 +48,7 @@ const standalone300 = { }; const datalake440 = { topology: () => Topologies.Sharded, + apiVersionInfo: () => undefined, connectionInfo: () => ({ is_atlas: true, is_data_lake: true, @@ -49,6 +60,7 @@ const datalake440 = { const noParams = { topology: () => Topologies.Standalone, + apiVersionInfo: () => undefined, connectionInfo: () => undefined, getCollectionCompletionsForCurrentDb: () => collections, getDatabaseCompletions: () => databases @@ -56,6 +68,7 @@ const noParams = { const emptyConnectionInfoParams = { topology: () => Topologies.Standalone, + apiVersionInfo: () => undefined, connectionInfo: () => ({}), getCollectionCompletionsForCurrentDb: () => collections, getDatabaseCompletions: () => databases @@ -558,4 +571,36 @@ describe('completer.completer', () => { .to.deep.equal([['show databases'], i, 'exclusive']); }); }); + + context('with apiStrict', () => { + it('completes supported methods like db.test.findOneAndReplace', async() => { + const i = 'db.test.findOneAndR'; + expect(await completer(apiStrictParams, i)) + .to.deep.equal([['db.test.findOneAndReplace'], i]); + }); + + it('completes common methods like db.test.getName', async() => { + const i = 'db.test.getNam'; + expect(await completer(apiStrictParams, i)) + .to.deep.equal([['db.test.getName'], i]); + }); + + it('does not complete unsupported methods like db.test.renameCollection', async() => { + const i = 'db.test.renameC'; + expect(await completer(apiStrictParams, i)) + .to.deep.equal([[], i]); + }); + + it('completes supported aggregation stages', async() => { + const i = 'db.test.aggregate([{$mat'; + expect(await completer(apiStrictParams, i)) + .to.deep.equal([['db.test.aggregate([{$match'], i]); + }); + + it('does not complete unsupported aggregation stages', async() => { + const i = 'db.test.aggregate([{$indexSta'; + expect(await completer(apiStrictParams, i)) + .to.deep.equal([[], i]); + }); + }); }); diff --git a/packages/autocomplete/index.ts b/packages/autocomplete/index.ts index f908310528..aeaddb0c14 100644 --- a/packages/autocomplete/index.ts +++ b/packages/autocomplete/index.ts @@ -23,6 +23,7 @@ export interface AutocompleteParameters { is_data_lake: boolean; server_version: string; }, + apiVersionInfo: () => { version: string, strict: boolean } | undefined; getCollectionCompletionsForCurrentDb: (collName: string) => string[] | Promise; getDatabaseCompletions: (dbName: string) => string[] | Promise; } @@ -170,14 +171,20 @@ async function completer(params: AutocompleteParameters, line: string): Promise< function isAcceptable( params: AutocompleteParameters, - entry: { version?: string; projectVersion?: string; env?: string[]; }, + entry: { version?: string; projectVersion?: string; env?: string[]; apiVersions?: number[] }, versionKey: 'version' | 'projectVersion') { const connectionInfo = params.connectionInfo(); - const isAcceptableVersion = - !entry[versionKey] || - // TODO: when https://jira.mongodb.org/browse/WRITING-8170 is done we can rely on server_version being present - !connectionInfo?.server_version || - semver.gte(connectionInfo.server_version, entry[versionKey] as string); + const apiVersionInfo = params.apiVersionInfo(); + let isAcceptableVersion; + if (apiVersionInfo?.strict && entry.apiVersions) { + isAcceptableVersion = entry.apiVersions.includes(+apiVersionInfo.version); + } else { + isAcceptableVersion = + !entry[versionKey] || + // TODO: when https://jira.mongodb.org/browse/PM-2327 is done we can rely on server_version being present + !connectionInfo?.server_version || + semver.gte(connectionInfo.server_version, entry[versionKey] as string); + } const isAcceptableEnvironment = !entry.env || !connectionInfo || @@ -217,14 +224,23 @@ function filterShellAPI( if (!c.startsWith(prefix)) return false; if (completions[c].deprecated) return false; - const serverVersion = params.connectionInfo()?.server_version; - if (!serverVersion) return true; + const apiVersionInfo = params.apiVersionInfo(); + let isAcceptableVersion; + let acceptableApiVersions; + if (apiVersionInfo?.strict && (acceptableApiVersions = completions[c].apiVersions)) { + isAcceptableVersion = + +apiVersionInfo.version >= acceptableApiVersions[0] && + +apiVersionInfo.version <= acceptableApiVersions[1]; + } else { + const serverVersion = params.connectionInfo()?.server_version; + if (!serverVersion) return true; - const acceptableVersions = completions[c].serverVersions; - const isAcceptableVersion = - !acceptableVersions || - (semver.gte(serverVersion, acceptableVersions[0]) && - semver.lte(serverVersion, acceptableVersions[1])); + const acceptableVersions = completions[c].serverVersions; + isAcceptableVersion = + !acceptableVersions || + (semver.gte(serverVersion, acceptableVersions[0]) && + semver.lte(serverVersion, acceptableVersions[1])); + } const acceptableTopologies = completions[c].topologies; const isAcceptableTopology = diff --git a/packages/autocomplete/package-lock.json b/packages/autocomplete/package-lock.json index 49e6af126c..82e1258606 100644 --- a/packages/autocomplete/package-lock.json +++ b/packages/autocomplete/package-lock.json @@ -606,9 +606,9 @@ } }, "mongodb-ace-autocompleter": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/mongodb-ace-autocompleter/-/mongodb-ace-autocompleter-0.4.14.tgz", - "integrity": "sha512-j49THhGVBCwTR0IP/98SrS4DftEi3bsFnQPJ6g/d3l0P6uaOLo4B4aIueVZz4gGFr/Mv1o2tJDLiBVdgCAbjVQ==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mongodb-ace-autocompleter/-/mongodb-ace-autocompleter-0.5.0.tgz", + "integrity": "sha512-5U+dgqFgBBmzIPqMXFp2JzViw9CxIkl+Ex0l8Wyu0L2A2ryP7z7JUh3vplOp/i+zDFVoZGqv3ljE6tmDOej4OQ==", "requires": { "semver": "^7.1.1" } diff --git a/packages/autocomplete/package.json b/packages/autocomplete/package.json index 05af798cc6..c64d236d41 100644 --- a/packages/autocomplete/package.json +++ b/packages/autocomplete/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@mongosh/shell-api": "0.0.0-dev.0", - "mongodb-ace-autocompleter": "^0.4.14", + "mongodb-ace-autocompleter": "^0.5.0", "semver": "^7.3.2" } } diff --git a/packages/browser-runtime-core/src/autocompleter/shell-api-autocompleter.spec.ts b/packages/browser-runtime-core/src/autocompleter/shell-api-autocompleter.spec.ts index 178eac5f09..f6a7c88661 100644 --- a/packages/browser-runtime-core/src/autocompleter/shell-api-autocompleter.spec.ts +++ b/packages/browser-runtime-core/src/autocompleter/shell-api-autocompleter.spec.ts @@ -4,6 +4,7 @@ import { Topologies } from '@mongosh/shell-api'; const standalone440 = { topology: () => Topologies.Standalone, + apiVersionInfo: () => undefined, connectionInfo: () => ({ is_atlas: false, is_data_lake: false, diff --git a/packages/shell-api/src/abstract-cursor.ts b/packages/shell-api/src/abstract-cursor.ts index 8400c33411..0532b8496a 100644 --- a/packages/shell-api/src/abstract-cursor.ts +++ b/packages/shell-api/src/abstract-cursor.ts @@ -3,7 +3,8 @@ import { toShellResult, returnType, ShellApiWithMongoClass, - returnsPromise + returnsPromise, + apiVersions } from './decorators'; import type Mongo from './mongo'; import type { @@ -194,6 +195,7 @@ export abstract class AbstractCursor extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) 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 diff --git a/packages/shell-api/src/aggregation-cursor.spec.ts b/packages/shell-api/src/aggregation-cursor.spec.ts index d401951da2..f2365e9992 100644 --- a/packages/shell-api/src/aggregation-cursor.spec.ts +++ b/packages/shell-api/src/aggregation-cursor.spec.ts @@ -2,7 +2,7 @@ import { expect } from 'chai'; import sinon, { StubbedInstance, stubInterface } from 'ts-sinon'; import { signatures, toShellResult } from './index'; import AggregationCursor from './aggregation-cursor'; -import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES } from './enums'; +import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES, ALL_API_VERSIONS } from './enums'; import { ReplPlatform, AggregationCursor as SPAggregationCursor } from '@mongosh/service-provider-core'; describe('AggregationCursor', () => { @@ -27,6 +27,7 @@ describe('AggregationCursor', () => { returnType: 'AggregationCursor', platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/bulk.spec.ts b/packages/shell-api/src/bulk.spec.ts index 3e58611199..47bb85ea07 100644 --- a/packages/shell-api/src/bulk.spec.ts +++ b/packages/shell-api/src/bulk.spec.ts @@ -39,6 +39,7 @@ describe('Bulk API', () => { returnType: 'BulkFindOp', platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: [ 1, Infinity ], serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined @@ -240,6 +241,7 @@ describe('Bulk API', () => { returnType: 'BulkFindOp', platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: [ 1, Infinity ], serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/bulk.ts b/packages/shell-api/src/bulk.ts index 42267fcdcc..134c142bca 100644 --- a/packages/shell-api/src/bulk.ts +++ b/packages/shell-api/src/bulk.ts @@ -1,4 +1,4 @@ -import { returnsPromise, shellApiClassDefault, returnType, deprecated, ShellApiWithMongoClass } from './decorators'; +import { returnsPromise, shellApiClassDefault, returnType, deprecated, apiVersions, ShellApiWithMongoClass } from './decorators'; import Mongo from './mongo'; import { CommonErrors, MongoshInvalidInputError, MongoshUnimplementedError } from '@mongosh/errors'; import { @@ -37,12 +37,14 @@ export class BulkFindOp extends ShellApiWithMongoClass { } @returnType('BulkFindOp') + @apiVersions([1]) collation(spec: CollationOptions): BulkFindOp { this._serviceProviderBulkFindOp.collation(spec); return this; } // Blocked by NODE-2751, bulk arrayFilters + @apiVersions([1]) arrayFilters(): BulkFindOp { throw new MongoshUnimplementedError( 'arrayFilters method on fluent Bulk API is not currently supported.', @@ -52,6 +54,7 @@ export class BulkFindOp extends ShellApiWithMongoClass { } @returnType('BulkFindOp') + @apiVersions([1]) hint(hintDoc: Document): BulkFindOp { assertArgsDefinedType([hintDoc], [true], 'BulkFindOp.hint'); this._hint = hintDoc; @@ -59,6 +62,7 @@ export class BulkFindOp extends ShellApiWithMongoClass { } @returnType('Bulk') + @apiVersions([1]) delete(): Bulk { this._parentBulk._batchCounts.nRemoveOps++; this._serviceProviderBulkFindOp.delete(); @@ -66,6 +70,7 @@ export class BulkFindOp extends ShellApiWithMongoClass { } @returnType('Bulk') + @apiVersions([1]) deleteOne(): Bulk { this._parentBulk._batchCounts.nRemoveOps++; this._serviceProviderBulkFindOp.deleteOne(); @@ -73,18 +78,21 @@ export class BulkFindOp extends ShellApiWithMongoClass { } @returnType('Bulk') + @apiVersions([1]) @deprecated remove(): Bulk { return this.delete(); } @returnType('Bulk') + @apiVersions([1]) @deprecated removeOne(): Bulk { return this.deleteOne(); } @returnType('Bulk') + @apiVersions([1]) replaceOne(replacement: Document): Bulk { this._parentBulk._batchCounts.nUpdateOps++; assertArgsDefinedType([replacement], [true], 'BulkFindOp.replacement'); @@ -97,6 +105,7 @@ export class BulkFindOp extends ShellApiWithMongoClass { } @returnType('Bulk') + @apiVersions([1]) updateOne(update: Document): Bulk { this._parentBulk._batchCounts.nUpdateOps++; assertArgsDefinedType([update], [true], 'BulkFindOp.update'); @@ -182,6 +191,7 @@ export default class Bulk extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) async execute(writeConcern?: WriteConcern): Promise { const { result } = await this._serviceProviderBulkOp.execute() as any; this._executed = true; @@ -199,12 +209,14 @@ export default class Bulk extends ShellApiWithMongoClass { } @returnType('BulkFindOp') + @apiVersions([1]) find(query: Document): BulkFindOp { assertArgsDefinedType([query], [true], 'Bulk.find'); return new BulkFindOp(this._serviceProviderBulkOp.find(query), this); } @returnType('Bulk') + @apiVersions([1]) insert(document: Document): Bulk { this._batchCounts.nInsertOps++; assertArgsDefinedType([document], [true], 'Bulk.insert'); diff --git a/packages/shell-api/src/change-stream-cursor.spec.ts b/packages/shell-api/src/change-stream-cursor.spec.ts index 257d3d7030..6ea977e4d4 100644 --- a/packages/shell-api/src/change-stream-cursor.spec.ts +++ b/packages/shell-api/src/change-stream-cursor.spec.ts @@ -2,7 +2,7 @@ import { expect } from 'chai'; import sinon, { StubbedInstance, stubInterface } from 'ts-sinon'; import { signatures, toShellResult } from './index'; import ChangeStreamCursor from './change-stream-cursor'; -import { ADMIN_DB, ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES } from './enums'; +import { ADMIN_DB, ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES, ALL_API_VERSIONS } from './enums'; import { ChangeStream, Document } from '@mongosh/service-provider-core'; import { startTestCluster } from '../../../testing/integration-testing-hooks'; import { CliServiceProvider } from '../../service-provider-server/lib'; @@ -33,6 +33,7 @@ describe('ChangeStreamCursor', () => { returnType: { type: 'unknown', attributes: {} }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/collection.spec.ts b/packages/shell-api/src/collection.spec.ts index 66d97ae24d..78361ccca0 100644 --- a/packages/shell-api/src/collection.spec.ts +++ b/packages/shell-api/src/collection.spec.ts @@ -44,6 +44,7 @@ describe('Collection', () => { returnType: 'AggregationCursor', platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: [ 1, Infinity ], serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/collection.ts b/packages/shell-api/src/collection.ts index bf55ce72b3..284dfa08c4 100644 --- a/packages/shell-api/src/collection.ts +++ b/packages/shell-api/src/collection.ts @@ -6,6 +6,7 @@ import { returnsPromise, returnType, serverVersions, + apiVersions, shellApiClassDefault, topologies, deprecated, @@ -147,6 +148,7 @@ export default class Collection extends ShellApiWithMongoClass { async aggregate(...stages: Document[]): Promise @returnsPromise @returnType('AggregationCursor') + @apiVersions([1]) async aggregate(...args: any[]): Promise { let options; let pipeline; @@ -198,6 +200,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async bulkWrite( operations: AnyBulkWriteOperation[], options: BulkWriteOptions = {} @@ -240,6 +243,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @deprecated @serverVersions([ServerVersions.earliest, '4.0.0']) + @apiVersions([]) async count(query = {}, options: CountOptions = {}): Promise { this._emitCollectionApiCall( 'count', @@ -265,6 +269,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['4.0.3', ServerVersions.latest]) + @apiVersions([1]) async countDocuments(query?: Document, options: CountDocumentsOptions = {}): Promise { this._emitCollectionApiCall('countDocuments', { query, options }); return this._mongo._serviceProvider.countDocuments( @@ -288,6 +293,7 @@ export default class Collection extends ShellApiWithMongoClass { * @returns {DeleteResult} The promise of the result. */ @returnsPromise + @apiVersions([1]) async deleteMany(filter: Document, options: DeleteOptions = {}): Promise { assertArgsDefinedType([filter], [true], 'Collection.deleteMany'); this._emitCollectionApiCall('deleteMany', { filter, options }); @@ -321,6 +327,7 @@ export default class Collection extends ShellApiWithMongoClass { * @returns {DeleteResult} The promise of the result. */ @returnsPromise + @apiVersions([1]) async deleteOne(filter: Document, options: DeleteOptions = {}): Promise { assertArgsDefinedType([filter], [true], 'Collection.deleteOne'); this._emitCollectionApiCall('deleteOne', { filter, options }); @@ -357,6 +364,7 @@ export default class Collection extends ShellApiWithMongoClass { async distinct(field: string, query: Document): Promise async distinct(field: string, query: Document, options: DistinctOptions): Promise @returnsPromise + @apiVersions([]) async distinct(field: string, query?: Document, options: DistinctOptions = {}): Promise { this._emitCollectionApiCall('distinct', { field, query, options }); return maybeMarkAsExplainOutput( @@ -379,6 +387,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['4.0.3', ServerVersions.latest]) + @apiVersions([1]) async estimatedDocumentCount(options: EstimatedDocumentCountOptions = {}): Promise { this._emitCollectionApiCall('estimatedDocumentCount', { options }); return this._mongo._serviceProvider.estimatedDocumentCount(this._database._name, this._name, { ...this._database._baseOptions, ...options }); @@ -396,6 +405,7 @@ export default class Collection extends ShellApiWithMongoClass { * @returns {Cursor} The promise of the cursor. */ @returnType('Cursor') + @apiVersions([1]) find(query?: Document, projection?: Document): Cursor { const options: FindOptions = {}; if (projection) { @@ -414,6 +424,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @deprecated + @apiVersions([1]) async findAndModify(options: FindAndModifyMethodShellOptions): Promise { assertArgsDefinedType([options], [true], 'Collection.findAndModify'); assertKeysDefined(options, ['query']); @@ -451,6 +462,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @returnType('Document') + @apiVersions([1]) async findOne(query: Document = {}, projection?: Document): Promise { const options: any = {}; if (projection) { @@ -465,6 +477,7 @@ export default class Collection extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async renameCollection( newName: string, dropTarget?: boolean @@ -509,6 +522,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @returnType('Document') @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async findOneAndDelete(filter: Document, options: FindOneAndDeleteOptions = {}): Promise { assertArgsDefinedType([filter], [true], 'Collection.findOneAndDelete'); this._emitCollectionApiCall('findOneAndDelete', { filter, options }); @@ -542,6 +556,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @returnType('Document') @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async findOneAndReplace(filter: Document, replacement: Document, options: FindAndModifyShellOptions = {}): Promise { assertArgsDefinedType([filter], [true], 'Collection.findOneAndReplace'); const findOneAndReplaceOptions = processFindAndModifyOptions({ @@ -580,6 +595,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @returnType('Document') @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async findOneAndUpdate(filter: Document, update: Document | Document[], options: FindAndModifyShellOptions = {}): Promise { assertArgsDefinedType([filter], [true], 'Collection.findOneAndUpdate'); const findOneAndUpdateOptions = processFindAndModifyOptions({ @@ -616,6 +632,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @deprecated @serverVersions([ServerVersions.earliest, '3.6.0']) + @apiVersions([1]) async insert(docs: Document | Document[], options: BulkWriteOptions = {}): Promise { printDeprecationWarning( 'Collection.insert() is deprecated. Use insertOne, insertMany, or bulkWrite.', @@ -658,6 +675,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async insertMany(docs: Document[], options: BulkWriteOptions = {}): Promise { assertArgsDefinedType([docs], [true], 'Collection.insertMany'); const docsToInsert: Document[] = Array.isArray(docs) ? docs.map((doc) => ({ ...doc })) : docs; @@ -691,6 +709,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async insertOne(doc: Document, options: InsertOneOptions = {}): Promise { assertArgsDefinedType([doc], [true], 'Collection.insertOne'); @@ -714,6 +733,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Boolean} */ @returnsPromise + @apiVersions([1]) async isCapped(): Promise { this._emitCollectionApiCall('isCapped'); return this._mongo._serviceProvider.isCapped(this._database._name, this._name); @@ -734,6 +754,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @deprecated @serverVersions([ServerVersions.earliest, '3.2.0']) + @apiVersions([1]) async remove(query: Document, options: boolean | RemoveShellOptions = {}): Promise { printDeprecationWarning( 'Collection.remove() is deprecated. Use deleteOne, deleteMany, findOneAndDelete, or bulkWrite.', @@ -784,6 +805,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async replaceOne(filter: Document, replacement: Document, options: ReplaceOptions = {}): Promise { assertArgsDefinedType([filter], [true], 'Collection.replaceOne'); @@ -807,6 +829,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @deprecated @serverVersions([ServerVersions.earliest, '3.2.0']) + @apiVersions([1]) async update(filter: Document, update: Document, options: UpdateOptions & { multi?: boolean } = {}): Promise { printDeprecationWarning( 'Collection.update() is deprecated. Use updateOne, updateMany, or bulkWrite.', @@ -860,6 +883,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async updateMany(filter: Document, update: Document, options: UpdateOptions = {}): Promise { assertArgsDefinedType([filter], [true], 'Collection.updateMany'); this._emitCollectionApiCall('updateMany', { filter, options }); @@ -898,6 +922,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async updateOne( filter: Document, update: Document, @@ -933,6 +958,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} */ @returnsPromise + @apiVersions([]) async convertToCapped(size: number): Promise { this._emitCollectionApiCall('convertToCapped', { size }); return await this._mongo._serviceProvider.runCommandWithCheck( @@ -956,6 +982,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async createIndexes( keyPatterns: Document[], options: CreateIndexesOptions = {} @@ -990,6 +1017,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} */ @returnsPromise + @apiVersions([1]) async createIndex( keys: Document, options: CreateIndexesOptions = {} @@ -1025,6 +1053,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} */ @returnsPromise + @apiVersions([1]) async ensureIndex( keys: Document, options: CreateIndexesOptions = {} @@ -1050,6 +1079,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async getIndexes(): Promise { this._emitCollectionApiCall('getIndexes'); return await this._mongo._serviceProvider.getIndexes(this._database._name, this._name, this._database._baseOptions); @@ -1063,6 +1093,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async getIndexSpecs(): Promise { this._emitCollectionApiCall('getIndexSpecs'); return await this._mongo._serviceProvider.getIndexes(this._database._name, this._name, this._database._baseOptions); @@ -1075,6 +1106,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} */ @returnsPromise + @apiVersions([1]) async getIndices(): Promise { this._emitCollectionApiCall('getIndices'); return await this._mongo._serviceProvider.getIndexes(this._database._name, this._name, this._database._baseOptions); @@ -1087,6 +1119,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([1]) async getIndexKeys(): Promise { this._emitCollectionApiCall('getIndexKeys'); const indexes = await this._mongo._serviceProvider.getIndexes(this._database._name, this._name, this._database._baseOptions); @@ -1101,6 +1134,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} */ @returnsPromise + @apiVersions([1]) async dropIndexes(indexes: string|string[]|Document|Document[] = '*'): Promise { this._emitCollectionApiCall('dropIndexes', { indexes }); try { @@ -1149,6 +1183,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} */ @returnsPromise + @apiVersions([1]) async dropIndex(index: string|Document): Promise { assertArgsDefinedType([index], [true], 'Collection.dropIndex'); this._emitCollectionApiCall('dropIndex', { index }); @@ -1174,6 +1209,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} */ @returnsPromise + @apiVersions([]) async totalIndexSize(...args: any[]): Promise { this._emitCollectionApiCall('totalIndexSize'); if (args.length) { @@ -1194,6 +1230,7 @@ export default class Collection extends ShellApiWithMongoClass { */ @returnsPromise @topologies([Topologies.Standalone]) + @apiVersions([]) async reIndex(): Promise { this._emitCollectionApiCall('reIndex'); return await this._mongo._serviceProvider.runCommandWithCheck(this._database._name, { @@ -1229,6 +1266,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} returns Promise */ @returnsPromise + @apiVersions([]) async dataSize(): Promise { this._emitCollectionApiCall('dataSize'); const stats = await this._mongo._serviceProvider.stats(this._database._name, this._name, this._database._baseOptions); @@ -1241,6 +1279,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} returns Promise */ @returnsPromise + @apiVersions([]) async storageSize(): Promise { this._emitCollectionApiCall('storageSize'); const stats = await this._mongo._serviceProvider.stats(this._database._name, this._name, this._database._baseOptions); @@ -1253,6 +1292,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} returns Promise */ @returnsPromise + @apiVersions([]) async totalSize(): Promise { this._emitCollectionApiCall('totalSize'); const stats = await this._mongo._serviceProvider.stats(this._database._name, this._name, this._database._baseOptions); @@ -1265,6 +1305,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} returns Promise */ @returnsPromise + @apiVersions([1]) async drop(): Promise { this._emitCollectionApiCall('drop'); @@ -1296,6 +1337,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Promise} returns Promise */ @returnsPromise + @apiVersions([1]) async exists(): Promise { this._emitCollectionApiCall('exists'); const collectionInfos = await this._mongo._serviceProvider.listCollections( @@ -1320,6 +1362,7 @@ export default class Collection extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) async runCommand(commandName: string | Document, options?: RunCommandOptions): Promise { assertArgsDefinedType([commandName], [['string', 'object']], 'Collection.runCommand'); if (options) { @@ -1352,6 +1395,7 @@ export default class Collection extends ShellApiWithMongoClass { } @returnType('Explainable') + @apiVersions([1]) explain(verbosity: ExplainVerbosityLike = 'queryPlanner'): Explainable { verbosity = validateExplainableVerbosity(verbosity); this._emitCollectionApiCall('explain', { verbosity }); @@ -1359,6 +1403,7 @@ export default class Collection extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async stats(originalOptions: CollStatsShellOptions | number = {}): Promise { const options: CollStatsShellOptions = typeof originalOptions === 'number' ? { scale: originalOptions } : originalOptions; @@ -1439,6 +1484,7 @@ export default class Collection extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async latencyStats(options: Document = {}): Promise { this._emitCollectionApiCall('latencyStats', { options }); const pipeline = [{ $collStats: { latencyStats: options } }]; @@ -1453,6 +1499,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @returnType('Bulk') + @apiVersions([1]) async initializeOrderedBulkOp(): Promise { this._emitCollectionApiCall('initializeOrderedBulkOp'); const innerBulk = await this._mongo._serviceProvider.initializeBulkOp( @@ -1466,6 +1513,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @returnType('Bulk') + @apiVersions([1]) async initializeUnorderedBulkOp(): Promise { this._emitCollectionApiCall('initializeUnorderedBulkOp'); const innerBulk = await this._mongo._serviceProvider.initializeBulkOp( @@ -1478,12 +1526,14 @@ export default class Collection extends ShellApiWithMongoClass { } @returnType('PlanCache') + @apiVersions([]) getPlanCache(): PlanCache { this._emitCollectionApiCall('getPlanCache'); return new PlanCache(this); } @returnsPromise + @apiVersions([]) async mapReduce(map: Function | string, reduce: Function | string, optionsOrOutString: MapReduceShellOptions): Promise { assertArgsDefinedType([map, reduce, optionsOrOutString], [true, true, true], 'Collection.mapReduce'); this._emitCollectionApiCall('mapReduce', { map, reduce, out: optionsOrOutString }); @@ -1512,6 +1562,7 @@ export default class Collection extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async validate(full = false): Promise { this._emitCollectionApiCall('validate', { full }); return await this._mongo._serviceProvider.runCommandWithCheck( @@ -1526,6 +1577,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @topologies([Topologies.Sharded]) + @apiVersions([]) async getShardVersion(): Promise { this._emitCollectionApiCall('getShardVersion', {}); return await this._mongo._serviceProvider.runCommandWithCheck( @@ -1539,6 +1591,7 @@ export default class Collection extends ShellApiWithMongoClass { @returnsPromise @topologies([Topologies.Sharded]) + @apiVersions([]) async getShardDistribution(): Promise { this._emitCollectionApiCall('getShardDistribution', {}); @@ -1645,6 +1698,7 @@ export default class Collection extends ShellApiWithMongoClass { @serverVersions(['3.1.0', ServerVersions.latest]) @topologies([Topologies.ReplSet, Topologies.Sharded]) + @apiVersions([1]) watch(pipeline: Document[] = [], options: ChangeStreamOptions = {}): ChangeStreamCursor { this._emitCollectionApiCall('watch', { pipeline, options }); const cursor = new ChangeStreamCursor( @@ -1658,6 +1712,7 @@ export default class Collection extends ShellApiWithMongoClass { @serverVersions(['4.4.0', ServerVersions.latest]) @returnsPromise + @apiVersions([1]) async hideIndex(index: string | Document): Promise { this._emitCollectionApiCall('hideIndex'); return setHideIndex(this, index, true); @@ -1665,6 +1720,7 @@ export default class Collection extends ShellApiWithMongoClass { @serverVersions(['4.4.0', ServerVersions.latest]) @returnsPromise + @apiVersions([1]) async unhideIndex(index: string | Document): Promise { this._emitCollectionApiCall('unhideIndex'); return setHideIndex(this, index, false); diff --git a/packages/shell-api/src/cursor.spec.ts b/packages/shell-api/src/cursor.spec.ts index 22fdc0733a..f5dff595ac 100644 --- a/packages/shell-api/src/cursor.spec.ts +++ b/packages/shell-api/src/cursor.spec.ts @@ -1,7 +1,7 @@ import { signatures, toShellResult } from './index'; import Cursor from './cursor'; import { ReplPlatform, FindCursor as ServiceProviderCursor } from '@mongosh/service-provider-core'; -import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES, ServerVersions } from './enums'; +import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES, ALL_API_VERSIONS, ServerVersions } from './enums'; import chai from 'chai'; import sinonChai from 'sinon-chai'; import sinon, { stubInterface, StubbedInstance } from 'ts-sinon'; @@ -31,6 +31,7 @@ describe('Cursor', () => { returnType: 'Cursor', platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/cursor.ts b/packages/shell-api/src/cursor.ts index 7175d15465..bc69ba0a4f 100644 --- a/packages/shell-api/src/cursor.ts +++ b/packages/shell-api/src/cursor.ts @@ -2,6 +2,7 @@ import { CommonErrors, MongoshDeprecatedError, MongoshInvalidInputError, Mongosh import { returnsPromise, returnType, + apiVersions, serverVersions, shellApiClassDefault, deprecated @@ -70,7 +71,7 @@ export default class Cursor extends AbstractCursor { @returnType('Cursor') allowPartialResults(): Cursor { - this._addFlag('partial' as CursorFlag); + this._addFlag('partial'); return this; } @@ -158,13 +159,13 @@ export default class Cursor extends AbstractCursor { @returnType('Cursor') noCursorTimeout(): Cursor { - this._addFlag('noCursorTimeout' as CursorFlag); + this._addFlag('noCursorTimeout'); return this; } @returnType('Cursor') oplogReplay(): Cursor { - this._addFlag('oplogReplay' as CursorFlag); + this._addFlag('oplogReplay'); return this; } @@ -200,11 +201,12 @@ export default class Cursor extends AbstractCursor { @returnType('Cursor') @serverVersions(['3.2.0', ServerVersions.latest]) + @apiVersions([]) tailable(opts = { awaitData: false }): Cursor { this._tailable = true; - this._addFlag('tailable' as CursorFlag); + this._addFlag('tailable'); if (opts.awaitData) { - this._addFlag('awaitData' as CursorFlag); + this._addFlag('awaitData'); } return this; } diff --git a/packages/shell-api/src/database.spec.ts b/packages/shell-api/src/database.spec.ts index a7facb4d83..8954196843 100644 --- a/packages/shell-api/src/database.spec.ts +++ b/packages/shell-api/src/database.spec.ts @@ -84,6 +84,7 @@ describe('Database', () => { returnType: 'AggregationCursor', platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: [ 1, Infinity ], serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined @@ -2440,6 +2441,7 @@ describe('Database', () => { updateUser: { a: ['username', { roles: [] }] }, createRole: { a: [{ role: 'a', privileges: [], roles: [] }] }, updateRole: { a: ['role', {}] }, + commandHelp: { a: ['ping'] }, getUser: { a: ['username'] }, getRole: { a: ['rolename'] }, dropUser: { a: ['username'] }, @@ -2470,7 +2472,7 @@ describe('Database', () => { serviceProvider.bsonLibrary = bson; internalSession = stubInterface(); serviceProvider.startSession.returns(internalSession); - serviceProvider.runCommandWithCheck.resolves({ ok: 1, version: 1, bits: 1, commands: 1, users: [], roles: [], logComponentVerbosity: 1 }); + serviceProvider.runCommandWithCheck.resolves({ ok: 1, version: 1, bits: 1, commands: 1, users: [], roles: [], logComponentVerbosity: 1, help: 'blah' }); serviceProvider.runCommand.resolves({ ok: 1 }); serviceProvider.listCollections.resolves([]); const internalState = new ShellInternalState(serviceProvider, bus); diff --git a/packages/shell-api/src/database.ts b/packages/shell-api/src/database.ts index aa94b5e067..137679aae9 100644 --- a/packages/shell-api/src/database.ts +++ b/packages/shell-api/src/database.ts @@ -5,6 +5,7 @@ import { returnsPromise, returnType, serverVersions, + apiVersions, shellApiClassDefault, topologies, deprecated, @@ -221,6 +222,7 @@ export default class Database extends ShellApiWithMongoClass { * @return {Promise} */ @returnsPromise + @apiVersions([1]) async getCollectionNames(): Promise { this._emitDatabaseApiCall('getCollectionNames'); return this._getCollectionNames(); @@ -236,6 +238,7 @@ export default class Database extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.0.0', ServerVersions.latest]) + @apiVersions([1]) async getCollectionInfos(filter: Document = {}, options: ListCollectionsOptions = {}): Promise { this._emitDatabaseApiCall('getCollectionInfos', { filter, options }); return await this._listCollections( @@ -252,6 +255,7 @@ export default class Database extends ShellApiWithMongoClass { * @returns The promise of command results. */ @returnsPromise + @apiVersions([1]) async runCommand(cmd: string | Document): Promise { assertArgsDefinedType([cmd], [['string', 'object']], 'Database.runCommand'); if (typeof cmd === 'string') { @@ -274,6 +278,7 @@ export default class Database extends ShellApiWithMongoClass { */ @returnsPromise @serverVersions(['3.4.0', ServerVersions.latest]) + @apiVersions([1]) async adminCommand(cmd: string | Document): Promise { assertArgsDefinedType([cmd], [['string', 'object']], 'Database.adminCommand'); if (typeof cmd === 'string') { @@ -296,6 +301,7 @@ export default class Database extends ShellApiWithMongoClass { */ @returnsPromise @returnType('AggregationCursor') + @apiVersions([1]) async aggregate(pipeline: Document[], options?: Document): Promise { assertArgsDefinedType([pipeline], [true], 'Database.aggregate'); this._emitDatabaseApiCall('aggregate', { options, pipeline }); @@ -350,6 +356,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) async dropDatabase(writeConcern?: WriteConcern): Promise { return await this._mongo._serviceProvider.dropDatabase( this._name, @@ -358,6 +365,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async createUser(user: Document, writeConcern?: WriteConcern): Promise { assertArgsDefinedType([user], ['object'], 'Database.createUser'); assertKeysDefined(user, ['user', 'roles']); @@ -418,6 +426,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async changeUserPassword(username: string, password: string, writeConcern?: WriteConcern): Promise { assertArgsDefinedType([username, password], ['string', 'string'], 'Database.changeUserPassword'); this._emitDatabaseApiCall('changeUserPassword', {}); @@ -440,12 +449,14 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async logout(): Promise { this._emitDatabaseApiCall('logout', {}); return await this._runCommand({ logout: 1 }); } @returnsPromise + @apiVersions([]) async dropUser(username: string, writeConcern?: WriteConcern): Promise { assertArgsDefinedType([username], ['string'], 'Database.dropUser'); this._emitDatabaseApiCall('dropUser', {}); @@ -457,6 +468,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async dropAllUsers(writeConcern?: WriteConcern): Promise { this._emitDatabaseApiCall('dropAllUsers', {}); const cmd = { dropAllUsersFromDatabase: 1 } as Document; @@ -508,6 +520,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async grantRolesToUser(username: string, roles: any[], writeConcern?: WriteConcern): Promise { assertArgsDefinedType([username, roles], ['string', true], 'Database.grantRolesToUser'); this._emitDatabaseApiCall('grantRolesToUser', {}); @@ -519,6 +532,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async revokeRolesFromUser(username: string, roles: any[], writeConcern?: WriteConcern): Promise { assertArgsDefinedType([username, roles], ['string', true], 'Database.revokeRolesFromUser'); this._emitDatabaseApiCall('revokeRolesFromUser', {}); @@ -530,6 +544,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async getUser(username: string, options: Document = {}): Promise { assertArgsDefinedType([username], ['string'], 'Database.getUser'); this._emitDatabaseApiCall('getUser', { username: username }); @@ -551,6 +566,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async getUsers(options: Document = {}): Promise { this._emitDatabaseApiCall('getUsers', { options: options }); const command = adaptOptions( @@ -564,6 +580,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) async createCollection(name: string, options: CreateCollectionOptions = {}): Promise<{ ok: number }> { assertArgsDefinedType([name], ['string'], 'Database.createCollection'); this._emitDatabaseApiCall('createCollection', { name: name, options: options }); @@ -575,6 +592,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) async createView(name: string, source: string, pipeline: Document[], options: CreateCollectionOptions = {}): Promise<{ ok: number }> { assertArgsDefinedType([name, source, pipeline], ['string', 'string', true], 'Database.createView'); this._emitDatabaseApiCall('createView', { name, source, pipeline, options }); @@ -594,6 +612,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async createRole(role: Document, writeConcern?: WriteConcern): Promise { assertArgsDefinedType([role], ['object'], 'Database.createRole'); assertKeysDefined(role, ['role', 'privileges', 'roles']); @@ -619,6 +638,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async updateRole(rolename: string, roleDoc: Document, writeConcern?: WriteConcern): Promise { assertArgsDefinedType([rolename, roleDoc], ['string', 'object'], 'Database.updateRole'); this._emitDatabaseApiCall('updateRole', {}); @@ -639,6 +659,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async dropRole(rolename: string, writeConcern?: WriteConcern): Promise { assertArgsDefinedType([rolename], ['string'], 'Database.dropRole'); this._emitDatabaseApiCall('dropRole', {}); @@ -660,6 +681,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async grantRolesToRole(rolename: string, roles: any[], writeConcern?: WriteConcern): Promise { assertArgsDefinedType([rolename, roles], ['string', true], 'Database.grantRolesToRole'); this._emitDatabaseApiCall('grantRolesToRole', {}); @@ -671,6 +693,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async revokeRolesFromRole(rolename: string, roles: any[], writeConcern?: WriteConcern): Promise { assertArgsDefinedType([rolename, roles], ['string', true], 'Database.revokeRolesFromRole'); this._emitDatabaseApiCall('revokeRolesFromRole', {}); @@ -693,6 +716,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async revokePrivilegesFromRole(rolename: string, privileges: any[], writeConcern?: WriteConcern): Promise { assertArgsDefinedType([rolename, privileges], ['string', true], 'Database.revokePrivilegesFromRole'); this._emitDatabaseApiCall('revokePrivilegesFromRole', {}); @@ -705,6 +729,7 @@ export default class Database extends ShellApiWithMongoClass { @returnsPromise + @apiVersions([]) async getRole(rolename: string, options: Document = {}): Promise { assertArgsDefinedType([rolename], ['string'], 'Database.getRole'); this._emitDatabaseApiCall('getRole', { rolename: rolename }); @@ -728,6 +753,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async getRoles(options: Document = {}): Promise { this._emitDatabaseApiCall('getRoles', { options: options }); const command = adaptOptions( @@ -741,6 +767,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async currentOp(opts: Document = {}): Promise { this._emitDatabaseApiCall('currentOp', { opts: opts }); return await this._runAdminCommand( @@ -752,6 +779,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async killOp(opId: number): Promise { assertArgsDefinedType([opId], ['number'], 'Database.killOp'); this._emitDatabaseApiCall('killOp', { opId }); @@ -764,6 +792,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async shutdownServer(opts: Document = {}): Promise { this._emitDatabaseApiCall('shutdownServer', { opts: opts }); return await this._runAdminCommand( @@ -775,6 +804,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async fsyncLock(): Promise { this._emitDatabaseApiCall('fsyncLock', {}); return await this._runAdminCommand( @@ -786,6 +816,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async fsyncUnlock(): Promise { this._emitDatabaseApiCall('fsyncUnlock', {}); return await this._runAdminCommand( @@ -796,6 +827,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) // TODO: Update this after https://jira.mongodb.org/browse/PM-2327 async version(): Promise { this._emitDatabaseApiCall('version', {}); const info: Document = await this._runAdminCommand( @@ -813,6 +845,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) // TODO: Maybe update this after https://jira.mongodb.org/browse/PM-2327 async serverBits(): Promise { this._emitDatabaseApiCall('serverBits', {}); const info: Document = await this._runAdminCommand( @@ -830,6 +863,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async isMaster(): Promise { this._emitDatabaseApiCall('isMaster', {}); const result = await this._runCommand( @@ -842,6 +876,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) @serverVersions(['5.0.0', ServerVersions.latest]) async hello(): Promise { this._emitDatabaseApiCall('hello', {}); @@ -862,6 +897,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async serverBuildInfo(): Promise { this._emitDatabaseApiCall('serverBuildInfo', {}); return await this._runAdminCommand( @@ -872,6 +908,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async serverStatus(opts = {}): Promise { this._emitDatabaseApiCall('serverStatus', { options: opts }); return await this._runAdminCommand( @@ -882,6 +919,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async stats(scale = 1): Promise { this._emitDatabaseApiCall('stats', { scale: scale }); return await this._runCommand( @@ -893,6 +931,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async hostInfo(): Promise { this._emitDatabaseApiCall('hostInfo', {}); return await this._runAdminCommand( @@ -903,6 +942,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async serverCmdLineOpts(): Promise { this._emitDatabaseApiCall('serverCmdLineOpts', {}); return await this._runAdminCommand( @@ -914,6 +954,7 @@ export default class Database extends ShellApiWithMongoClass { @returnsPromise @serverVersions(['5.0.0', ServerVersions.latest]) + @apiVersions([]) async rotateCertificates(message?: string): Promise { this._emitDatabaseApiCall('rotateCertificates', { message }); return await this._runAdminCommand( @@ -924,6 +965,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async printCollectionStats(scale = 1): Promise { if (typeof scale !== 'number' || scale < 1) { throw new MongoshInvalidInputError( @@ -945,6 +987,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async getFreeMonitoringStatus(): Promise { this._emitDatabaseApiCall('getFreeMonitoringStatus', {}); return await this._runAdminCommand( @@ -955,6 +998,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async disableFreeMonitoring(): Promise { this._emitDatabaseApiCall('disableFreeMonitoring', {}); return await this._runAdminCommand( @@ -966,6 +1010,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async enableFreeMonitoring(): Promise { this._emitDatabaseApiCall('enableFreeMonitoring', {}); const helloResult = await this.hello(); @@ -1019,6 +1064,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async getProfilingStatus(): Promise { this._emitDatabaseApiCall('getProfilingStatus', {}); return await this._runCommand( @@ -1029,6 +1075,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async setProfilingLevel(level: number, opts: number | Document = {}): Promise { assertArgsDefinedType([level], ['number'], 'Database.setProfilingLevel'); if (level < 0 || level > 2) { @@ -1050,6 +1097,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async setLogLevel(logLevel: number, component?: Document | string): Promise { assertArgsDefinedType([logLevel], ['number'], 'Database.setLogLevel'); this._emitDatabaseApiCall('setLogLevel', { logLevel: logLevel, component: component }); @@ -1078,6 +1126,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async getLogComponents(): Promise { this._emitDatabaseApiCall('getLogComponents', {}); const cmdObj = { getParameter: 1, logComponentVerbosity: 1 }; @@ -1109,6 +1158,8 @@ export default class Database extends ShellApiWithMongoClass { throw new MongoshDeprecatedError('`copyDatabase()` was removed because it was deprecated in MongoDB 4.0'); } + @returnsPromise + @apiVersions([1]) async commandHelp(name: string): Promise { assertArgsDefinedType([name], ['string'], 'Database.commandHelp'); this._emitDatabaseApiCall('commandHelp', { name: name }); @@ -1129,6 +1180,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async listCommands(): Promise { this._emitDatabaseApiCall('listCommands', {}); const result = await this._runCommand( @@ -1157,12 +1209,14 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async getLastErrorObj(w?: number|string, wTimeout?: number, j?: boolean): Promise { this._emitDatabaseApiCall('getLastErrorObj', { w: w, wTimeout: wTimeout, j: j }); return await this._getLastErrorObj(w, wTimeout, j); } @returnsPromise + @apiVersions([]) async getLastError(w?: number|string, wTimeout?: number): Promise { this._emitDatabaseApiCall('getLastError', { w: w, wTimeout: wTimeout }); const result = await this._getLastErrorObj(w, wTimeout); @@ -1171,6 +1225,7 @@ export default class Database extends ShellApiWithMongoClass { @returnsPromise @topologies([Topologies.Sharded]) + @apiVersions([1]) async printShardingStatus(verbose = false): Promise { this._emitDatabaseApiCall('printShardingStatus', { verbose }); const result = await getPrintableShardStatus(this, verbose); @@ -1178,7 +1233,9 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @topologies([Topologies.ReplSet]) + @apiVersions([]) async printSecondaryReplicationInfo(): Promise { let startOptimeDate = null; const local = this.getSiblingDB('local'); @@ -1248,6 +1305,7 @@ export default class Database extends ShellApiWithMongoClass { @returnsPromise @topologies([Topologies.ReplSet]) + @apiVersions([]) async getReplicationInfo(): Promise { const localdb = this.getSiblingDB('local'); @@ -1305,6 +1363,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @topologies([Topologies.ReplSet]) async printReplicationInfo(): Promise { const result = {} as any; @@ -1340,6 +1399,7 @@ export default class Database extends ShellApiWithMongoClass { @serverVersions(['3.1.0', ServerVersions.latest]) @topologies([Topologies.ReplSet, Topologies.Sharded]) + @apiVersions([1]) watch(pipeline: Document[] = [], options: ChangeStreamOptions = {}): ChangeStreamCursor { this._emitDatabaseApiCall('watch', { pipeline, options }); const cursor = new ChangeStreamCursor( diff --git a/packages/shell-api/src/decorators.ts b/packages/shell-api/src/decorators.ts index ecabf2ba04..9e6ae5db87 100644 --- a/packages/shell-api/src/decorators.ts +++ b/packages/shell-api/src/decorators.ts @@ -5,6 +5,7 @@ import { Mongo, ShellInternalState } from '.'; import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, + ALL_API_VERSIONS, ALL_TOPOLOGIES, asPrintable, namespaceInfo, shellApiType, Topologies @@ -21,6 +22,7 @@ export interface ShellApiInterface { [shellApiType]: string; [asPrintable]?: () => any; serverVersions?: [string, string]; + apiVersions?: [number, number]; topologies?: Topologies[]; help?: Help; [key: string]: any; @@ -230,6 +232,7 @@ export type ShellCommandCompleter = export interface TypeSignature { type: string; serverVersions?: [ string, string ]; + apiVersions?: [ number, number ]; topologies?: Topologies[]; returnsPromise?: boolean; deprecated?: boolean; @@ -258,6 +261,7 @@ type ClassSignature = { [methodName: string]: { type: 'function'; serverVersions: [ string, string ]; + apiVersions: [ number, number ]; topologies: Topologies[]; returnType: ClassSignature; returnsPromise: boolean; @@ -308,6 +312,7 @@ export function shellApiClassGeneric(constructor: Function, hasHelp: boolean): v method = wrapWithApiChecks(method, className); method.serverVersions = method.serverVersions || ALL_SERVER_VERSIONS; + method.apiVersions = method.apiVersions || ALL_API_VERSIONS; method.topologies = method.topologies || ALL_TOPOLOGIES; method.returnType = method.returnType || { type: 'unknown', attributes: {} }; method.returnsPromise = method.returnsPromise || false; @@ -319,6 +324,7 @@ export function shellApiClassGeneric(constructor: Function, hasHelp: boolean): v classSignature.attributes[propertyName] = { type: 'function', serverVersions: method.serverVersions, + apiVersions: method.apiVersions, topologies: method.topologies, returnType: method.returnType === 'this' ? className : method.returnType, returnsPromise: method.returnsPromise, @@ -370,6 +376,7 @@ export function shellApiClassGeneric(constructor: Function, hasHelp: boolean): v classSignature.attributes[propertyName] = { type: 'function', serverVersions: method.serverVersions, + apiVersions: method.apiVersions, topologies: method.topologies, returnType: method.returnType === 'this' ? className : method.returnType, returnsPromise: method.returnsPromise, @@ -427,6 +434,20 @@ export function serverVersions(versionArray: [ string, string ]): Function { descriptor.value.serverVersions = versionArray; }; } +export function apiVersions(versionArray: [] | [ number ] | [ number, number ]): Function { + return function( + _target: any, + _propertyKey: string, + descriptor: PropertyDescriptor + ): void { + if (versionArray.length === 0) { + versionArray = [ 0, 0 ]; + } else if (versionArray.length === 1) { + versionArray = [ versionArray[0], Infinity ]; + } + descriptor.value.apiVersions = versionArray; + }; +} export function deprecated(_target: any, _propertyKey: string, descriptor: PropertyDescriptor): void { descriptor.value.deprecated = true; } diff --git a/packages/shell-api/src/enums.ts b/packages/shell-api/src/enums.ts index 30e9adb616..6f9ec36e07 100644 --- a/packages/shell-api/src/enums.ts +++ b/packages/shell-api/src/enums.ts @@ -14,6 +14,7 @@ import { ReplPlatform } from '@mongosh/service-provider-core'; export const ALL_SERVER_VERSIONS = [ ServerVersions.earliest, ServerVersions.latest ]; export const ALL_TOPOLOGIES = [ Topologies.ReplSet, Topologies.Sharded, Topologies.Standalone ]; export const ALL_PLATFORMS = [ ReplPlatform.Compass, ReplPlatform.Browser, ReplPlatform.CLI ]; +export const ALL_API_VERSIONS = [ 0, Infinity ]; export const CURSOR_FLAGS = { 2: 'tailable', diff --git a/packages/shell-api/src/explainable-cursor.spec.ts b/packages/shell-api/src/explainable-cursor.spec.ts index d240ec5222..79f4baa7f5 100644 --- a/packages/shell-api/src/explainable-cursor.spec.ts +++ b/packages/shell-api/src/explainable-cursor.spec.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import sinon from 'ts-sinon'; -import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES, ServerVersions } from './enums'; +import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES, ALL_API_VERSIONS, ServerVersions } from './enums'; import { signatures, toShellResult } from './index'; import ExplainableCursor from './explainable-cursor'; @@ -24,6 +24,7 @@ describe('ExplainableCursor', () => { returnType: 'ExplainableCursor', platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/explainable.spec.ts b/packages/shell-api/src/explainable.spec.ts index ec4d2ec9fd..c1918b016a 100644 --- a/packages/shell-api/src/explainable.spec.ts +++ b/packages/shell-api/src/explainable.spec.ts @@ -32,6 +32,7 @@ describe('Explainable', () => { returnType: 'ExplainableCursor', platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: [ 1, Infinity ], serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/explainable.ts b/packages/shell-api/src/explainable.ts index 3a9755728b..6287dce4d2 100644 --- a/packages/shell-api/src/explainable.ts +++ b/packages/shell-api/src/explainable.ts @@ -4,6 +4,7 @@ import ExplainableCursor from './explainable-cursor'; import { returnsPromise, returnType, + apiVersions, shellApiClassDefault, serverVersions, ShellApiWithMongoClass, @@ -83,6 +84,7 @@ export default class Explainable extends ShellApiWithMongoClass { } @returnType('ExplainableCursor') + @apiVersions([1]) find(query?: Document, projection?: Document): ExplainableCursor { this._emitExplainableApiCall('find', { query, projection }); @@ -93,6 +95,7 @@ export default class Explainable extends ShellApiWithMongoClass { async aggregate(pipeline: Document[], options: Document): Promise async aggregate(...stages: Document[]): Promise @returnsPromise + @apiVersions([1]) async aggregate(...args: any[]): Promise { this._emitExplainableApiCall('aggregate', { args }); let options: Document; @@ -112,6 +115,7 @@ export default class Explainable extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) async count(query = {}, options: CountOptions = {}): Promise { this._emitExplainableApiCall('count', { query, options }); // This is the only one that currently lacks explicit driver support. @@ -129,36 +133,42 @@ export default class Explainable extends ShellApiWithMongoClass { async distinct(field: string, query: Document): Promise async distinct(field: string, query: Document, options: DistinctOptions): Promise @returnsPromise + @apiVersions([1]) async distinct(field: string, query?: Document, options: DistinctOptions = {}): Promise { this._emitExplainableApiCall('distinct', { field, query, options }); return this._collection.distinct(field, query ?? {}, { ...options, explain: this._verbosity }); } @returnsPromise + @apiVersions([1]) async findAndModify(options: FindAndModifyMethodShellOptions): Promise { this._emitExplainableApiCall('findAndModify', { options }); return this._collection.findAndModify({ ...options, explain: this._verbosity }); } @returnsPromise + @apiVersions([1]) async findOneAndDelete(filter: Document, options: FindOneAndDeleteOptions = {}): Promise { this._emitExplainableApiCall('findOneAndDelete', { filter, options }); return this._collection.findOneAndDelete(filter, { ...options, explain: this._verbosity }); } @returnsPromise + @apiVersions([1]) async findOneAndReplace(filter: Document, replacement: Document, options: FindAndModifyShellOptions = {}): Promise { this._emitExplainableApiCall('findOneAndReplace', { filter, options }); return this._collection.findOneAndReplace(filter, replacement, { ...options, explain: this._verbosity }); } @returnsPromise + @apiVersions([1]) async findOneAndUpdate(filter: Document, update: Document, options: FindAndModifyShellOptions = {}): Promise { this._emitExplainableApiCall('findOneAndUpdate', { filter, options }); return this._collection.findOneAndUpdate(filter, update, { ...options, explain: this._verbosity }); } @returnsPromise + @apiVersions([1]) async remove(query: Document, options: boolean | RemoveShellOptions = {}): Promise { this._emitExplainableApiCall('remove', { query, options }); options = { ...processRemoveOptions(options), explain: this._verbosity }; @@ -166,6 +176,7 @@ export default class Explainable extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) async update(filter: Document, update: Document, options: UpdateOptions = {}): Promise { this._emitExplainableApiCall('update', { filter, update, options }); return this._collection.update(filter, update, { ...options, explain: this._verbosity }); @@ -173,6 +184,7 @@ export default class Explainable extends ShellApiWithMongoClass { @returnsPromise @serverVersions(['4.4.0', ServerVersions.latest]) + @apiVersions([]) async mapReduce( map: Function | string, reduce: Function | string, diff --git a/packages/shell-api/src/field-level-encryption.spec.ts b/packages/shell-api/src/field-level-encryption.spec.ts index 5a9012bdc1..e3adca7550 100644 --- a/packages/shell-api/src/field-level-encryption.spec.ts +++ b/packages/shell-api/src/field-level-encryption.spec.ts @@ -5,7 +5,7 @@ import { EventEmitter } from 'events'; import sinon, { StubbedInstance, stubInterface } from 'ts-sinon'; import Database from './database'; import { signatures, toShellResult } from './decorators'; -import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES } from './enums'; +import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES, ALL_API_VERSIONS } from './enums'; import { ClientEncryption, ClientSideFieldLevelEncryptionOptions, ClientSideFieldLevelEncryptionKmsProvider as KMSProvider, KeyVault } from './field-level-encryption'; import Mongo from './mongo'; import { DeleteResult } from './result'; @@ -98,6 +98,7 @@ describe('Field Level Encryption', () => { returnType: { attributes: {}, type: 'unknown' }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: [ 1, Infinity ], serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined @@ -109,6 +110,7 @@ describe('Field Level Encryption', () => { returnType: { attributes: {}, type: 'unknown' }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/field-level-encryption.ts b/packages/shell-api/src/field-level-encryption.ts index 57af46bb8a..75ad840c6c 100644 --- a/packages/shell-api/src/field-level-encryption.ts +++ b/packages/shell-api/src/field-level-encryption.ts @@ -2,6 +2,7 @@ import { classPlatforms, returnsPromise, returnType, + apiVersions, shellApiClassDefault, ShellApiWithMongoClass } from './decorators'; @@ -120,6 +121,7 @@ export class KeyVault extends ShellApiWithMongoClass { createKey(kms: ClientEncryptionDataKeyProvider, options: AWSEncryptionKeyOptions | AzureEncryptionKeyOptions | GCPEncryptionKeyOptions | undefined): Promise createKey(kms: ClientEncryptionDataKeyProvider, options: AWSEncryptionKeyOptions | AzureEncryptionKeyOptions | GCPEncryptionKeyOptions | undefined, keyAltNames: string[]): Promise @returnsPromise + @apiVersions([1]) // eslint-disable-next-line complexity async createKey( kms: ClientEncryptionDataKeyProvider, @@ -184,29 +186,34 @@ export class KeyVault extends ShellApiWithMongoClass { } @returnType('Cursor') + @apiVersions([1]) getKey(keyId: BinaryType): Cursor { assertArgsDefinedType([keyId], [true], 'KeyVault.getKey'); return this._keyColl.find({ '_id': keyId }); } @returnType('Cursor') + @apiVersions([1]) getKeyByAltName(keyAltName: string): Cursor { assertArgsDefinedType([keyAltName], ['string'], 'KeyVault.getKeyByAltName'); return this._keyColl.find({ 'keyAltNames': keyAltName }); } @returnType('Cursor') + @apiVersions([1]) getKeys(): Cursor { return this._keyColl.find({}); } @returnsPromise + @apiVersions([1]) async deleteKey(keyId: BinaryType): Promise { assertArgsDefinedType([keyId], [true], 'KeyVault.deleteKey'); return this._keyColl.deleteOne({ '_id': keyId }); } @returnsPromise + @apiVersions([1]) async addKeyAlternateName(keyId: BinaryType, keyAltName: string): Promise { assertArgsDefinedType([keyId, keyAltName], [true, 'string'], 'KeyVault.addKeyAlternateName'); return this._keyColl.findAndModify({ @@ -216,6 +223,7 @@ export class KeyVault extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) async removeKeyAlternateName(keyId: BinaryType, keyAltName: string): Promise { assertArgsDefinedType([keyId, keyAltName], [true, 'string'], 'KeyVault.removeKeyAlternateName'); const ret = await this._keyColl.findAndModify({ diff --git a/packages/shell-api/src/mongo.spec.ts b/packages/shell-api/src/mongo.spec.ts index 302fd46263..aa0745ef0c 100644 --- a/packages/shell-api/src/mongo.spec.ts +++ b/packages/shell-api/src/mongo.spec.ts @@ -48,6 +48,7 @@ describe('Mongo', () => { returnType: { attributes: {}, type: 'unknown' }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: [ 1, Infinity ], serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/mongo.ts b/packages/shell-api/src/mongo.ts index 975bf5ac85..869dd9e930 100644 --- a/packages/shell-api/src/mongo.ts +++ b/packages/shell-api/src/mongo.ts @@ -14,6 +14,7 @@ import { returnsPromise, returnType, serverVersions, + apiVersions, ShellApiClass, shellApiClassDefault, topologies, @@ -267,18 +268,21 @@ export default class Mongo extends ShellApiClass { } @returnsPromise + @apiVersions([1]) async getDBs(options: ListDatabasesOptions = {}): Promise<{ databases: {name: string, sizeOnDisk: number, empty: boolean}[] }> { this._emitMongoApiCall('getDBs', { options }); return await this._listDatabases(options); } @returnsPromise + @apiVersions([1]) async getDBNames(options: ListDatabasesOptions = {}): Promise { this._emitMongoApiCall('getDBNames', { options }); return (await this._listDatabases(options)).databases.map(db => db.name); } @returnsPromise + @apiVersions([1]) async show(cmd: string, arg?: string): Promise { this._internalState.messageBus.emit('mongosh:show', { method: `show ${cmd}` }); @@ -322,6 +326,7 @@ export default class Mongo extends ShellApiClass { throw err; } } + async close(force: boolean): Promise { const index = this._internalState.mongos.indexOf(this); if (index === -1) { @@ -491,6 +496,7 @@ export default class Mongo extends ShellApiClass { @serverVersions(['3.1.0', ServerVersions.latest]) @topologies([Topologies.ReplSet, Topologies.Sharded]) + @apiVersions([1]) watch(pipeline: Document[] = [], options: ChangeStreamOptions = {}): ChangeStreamCursor { this._emitMongoApiCall('watch', { pipeline, options }); const cursor = new ChangeStreamCursor( diff --git a/packages/shell-api/src/plan-cache.spec.ts b/packages/shell-api/src/plan-cache.spec.ts index 138c2b68b1..863e087ad4 100644 --- a/packages/shell-api/src/plan-cache.spec.ts +++ b/packages/shell-api/src/plan-cache.spec.ts @@ -25,6 +25,7 @@ describe('PlanCache', () => { deprecated: false, returnType: { attributes: {}, type: 'unknown' }, platforms: ALL_PLATFORMS, + apiVersions: [ 0, 0 ], topologies: ALL_TOPOLOGIES, serverVersions: ['4.4.0', ServerVersions.latest], isDirectShellCommand: false, diff --git a/packages/shell-api/src/plan-cache.ts b/packages/shell-api/src/plan-cache.ts index 9b51d23a06..ce36a35f18 100644 --- a/packages/shell-api/src/plan-cache.ts +++ b/packages/shell-api/src/plan-cache.ts @@ -1,5 +1,6 @@ import { returnsPromise, + apiVersions, serverVersions, shellApiClassDefault, deprecated, @@ -32,11 +33,13 @@ export default class PlanCache extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async clear(): Promise { return await this._collection.runCommand('planCacheClear'); } @returnsPromise + @apiVersions([]) async clearPlansByQuery(query: Document, projection?: Document, sort?: Document): Promise { const cmd = { query } as any; if (projection) { @@ -50,7 +53,8 @@ export default class PlanCache extends ShellApiWithMongoClass { @serverVersions(['4.4.0', ServerVersions.latest]) @returnsPromise - async list(pipeline?: Document[]): Promise { + @apiVersions([]) + async list(pipeline?: Document[]): Promise { const p = pipeline || []; const agg = await this._collection.aggregate([{ $planCacheStats: {} }, ...p]); return await agg.toArray(); diff --git a/packages/shell-api/src/replica-set.spec.ts b/packages/shell-api/src/replica-set.spec.ts index 41a7afe138..59ab13395f 100644 --- a/packages/shell-api/src/replica-set.spec.ts +++ b/packages/shell-api/src/replica-set.spec.ts @@ -13,6 +13,7 @@ import { ADMIN_DB, ALL_PLATFORMS, ALL_SERVER_VERSIONS, + ALL_API_VERSIONS, ALL_TOPOLOGIES } from './enums'; import { signatures, toShellResult } from './index'; @@ -53,6 +54,7 @@ describe('ReplicaSet', () => { returnType: { type: 'unknown', attributes: {} }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/replica-set.ts b/packages/shell-api/src/replica-set.ts index 294441dd7f..3d1904aa94 100644 --- a/packages/shell-api/src/replica-set.ts +++ b/packages/shell-api/src/replica-set.ts @@ -6,6 +6,7 @@ import Database from './database'; import { deprecated, returnsPromise, + apiVersions, shellApiClassDefault, ShellApiWithMongoClass } from './decorators'; @@ -79,6 +80,7 @@ export default class ReplicaSet extends ShellApiWithMongoClass { * Returns a document that contains the current replica set configuration. */ @returnsPromise + @apiVersions([1]) async config(): Promise { this._emitReplicaSetApiCall('config', {}); return this._getConfig(); @@ -88,6 +90,7 @@ export default class ReplicaSet extends ShellApiWithMongoClass { * Alias, conf is documented but config is not */ @returnsPromise + @apiVersions([1]) async conf(): Promise { this._emitReplicaSetApiCall('conf', {}); return this._getConfig(); @@ -100,6 +103,7 @@ export default class ReplicaSet extends ShellApiWithMongoClass { * @param options */ @returnsPromise + @apiVersions([]) async reconfig(config: Partial, options = {}): Promise { assertArgsDefinedType([ config, options ], ['object', [undefined, 'object']], 'ReplicaSet.reconfig'); this._emitReplicaSetApiCall('reconfig', { config, options }); @@ -144,6 +148,7 @@ export default class ReplicaSet extends ShellApiWithMongoClass { * to a Primary-Secondary-Arbiter set (PA to PSA for short). */ @returnsPromise + @apiVersions([]) async reconfigForPSASet(newMemberIndex: number, config: Partial, options = {}): Promise { assertArgsDefinedType( [ newMemberIndex, config, options ], @@ -214,6 +219,7 @@ export default class ReplicaSet extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async status(): Promise { this._emitReplicaSetApiCall('status', {}); return this._database._runAdminCommand( @@ -224,18 +230,21 @@ export default class ReplicaSet extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async isMaster(): Promise { this._emitReplicaSetApiCall('isMaster', {}); return this._database.getSiblingDB('admin').isMaster(); } @returnsPromise + @apiVersions([1]) async hello(): Promise { this._emitReplicaSetApiCall('hello', {}); return this._database.getSiblingDB('admin').hello(); } @returnsPromise + @apiVersions([]) async printSecondaryReplicationInfo(): Promise { this._emitReplicaSetApiCall('printSecondaryReplicationInfo', {}); return this._database.printSecondaryReplicationInfo(); @@ -243,17 +252,20 @@ export default class ReplicaSet extends ShellApiWithMongoClass { @deprecated @returnsPromise + @apiVersions([]) async printSlaveReplicationInfo(): Promise { throw new MongoshDeprecatedError('printSlaveReplicationInfo has been deprecated. Use printSecondaryReplicationInfo instead'); } @returnsPromise + @apiVersions([]) async printReplicationInfo(): Promise { this._emitReplicaSetApiCall('printReplicationInfo', {}); return this._database.printReplicationInfo(); } @returnsPromise + @apiVersions([]) async add(hostport: string | Partial, arb?: boolean): Promise { assertArgsDefinedType([hostport, arb], [['string', 'object'], [undefined, 'boolean']], 'ReplicaSet.add'); this._emitReplicaSetApiCall('add', { hostport, arb }); @@ -290,12 +302,14 @@ export default class ReplicaSet extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async addArb(hostname: string): Promise { this._emitReplicaSetApiCall('addArb', { hostname }); return this.add(hostname, true); } @returnsPromise + @apiVersions([]) async remove(hostname: string): Promise { assertArgsDefinedType([hostname], ['string'], 'ReplicaSet.remove'); this._emitReplicaSetApiCall('remove', { hostname }); @@ -319,6 +333,7 @@ export default class ReplicaSet extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async freeze(secs: number): Promise { assertArgsDefinedType([secs], ['number'], 'ReplicaSet.freeze'); this._emitReplicaSetApiCall('freeze', { secs }); @@ -330,6 +345,7 @@ export default class ReplicaSet extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async stepDown(stepdownSecs?: number, catchUpSecs?: number): Promise { assertArgsDefinedType([stepdownSecs, catchUpSecs], [[undefined, 'number'], [undefined, 'number']], 'ReplicaSet.stepDown'); this._emitReplicaSetApiCall('stepDown', { stepdownSecs, catchUpSecs }); @@ -345,6 +361,7 @@ export default class ReplicaSet extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async syncFrom(host: string): Promise { assertArgsDefinedType([host], ['string'], 'ReplicaSet.syncFrom'); this._emitReplicaSetApiCall('syncFrom', { host }); diff --git a/packages/shell-api/src/session.spec.ts b/packages/shell-api/src/session.spec.ts index ab2dc70578..ebcd7a58c5 100644 --- a/packages/shell-api/src/session.spec.ts +++ b/packages/shell-api/src/session.spec.ts @@ -8,6 +8,7 @@ import Mongo from './mongo'; import { ADMIN_DB, ALL_PLATFORMS, + ALL_API_VERSIONS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES } from './enums'; @@ -38,6 +39,7 @@ describe('Session', () => { returnType: { type: 'unknown', attributes: {} }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/shard.spec.ts b/packages/shell-api/src/shard.spec.ts index 93b8ab0be1..0bbffcb2e1 100644 --- a/packages/shell-api/src/shard.spec.ts +++ b/packages/shell-api/src/shard.spec.ts @@ -37,6 +37,7 @@ describe('Shard', () => { returnType: { type: 'unknown', attributes: {} }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: [ 0, 0 ], serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/shard.ts b/packages/shell-api/src/shard.ts index 6179940642..0b21015edb 100644 --- a/packages/shell-api/src/shard.ts +++ b/packages/shell-api/src/shard.ts @@ -1,7 +1,7 @@ import Database from './database'; import { shellApiClassDefault, - returnsPromise, serverVersions, ShellApiWithMongoClass + returnsPromise, serverVersions, apiVersions, ShellApiWithMongoClass } from './decorators'; import type { Document } from '@mongosh/service-provider-core'; @@ -47,6 +47,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async enableSharding(database: string, primaryShard?: string): Promise { assertArgsDefinedType([database, primaryShard], ['string', [undefined, 'string']], 'Shard.enableSharding'); this._emitShardApiCall('enableSharding', { database, primaryShard }); @@ -68,6 +69,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @serverVersions(['5.0.0', ServerVersions.latest]) async commitReshardCollection(namespace: string): Promise { assertArgsDefinedType([namespace], ['string'], 'Shard.commitReshardCollection'); @@ -78,6 +80,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @serverVersions(['5.0.0', ServerVersions.latest]) async abortReshardCollection(namespace: string): Promise { assertArgsDefinedType([namespace], ['string'], 'Shard.abortReshardCollection'); @@ -88,11 +91,13 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async shardCollection(namespace: string, key: Document, unique?: boolean | Document, options?: Document): Promise { return await this._runShardCollection('shardCollection', namespace, key, unique, options); } @returnsPromise + @apiVersions([]) @serverVersions(['5.0.0', ServerVersions.latest]) async reshardCollection(namespace: string, key: Document, unique?: boolean | Document, options?: Document): Promise { return await this._runShardCollection('reshardCollection', namespace, key, unique, options); @@ -130,12 +135,14 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) async status(verbose = false): Promise { const result = await getPrintableShardStatus(this._database, verbose); return new CommandResult('StatsResult', result); } @returnsPromise + @apiVersions([]) async addShard(url: string): Promise { assertArgsDefinedType([url], ['string'], 'Shard.addShard'); await getConfigDB(this._database); @@ -146,6 +153,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @serverVersions(['3.4.0', ServerVersions.latest]) async addShardToZone(shard: string, zone: string): Promise { assertArgsDefinedType([shard, zone], ['string', 'string'], 'Shard.addShardToZone'); @@ -158,6 +166,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @serverVersions(['3.4.0', ServerVersions.latest]) async addShardTag(shard: string, tag: string): Promise { assertArgsDefinedType([shard, tag], ['string', 'string'], 'Shard.addShardTag'); @@ -173,6 +182,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async updateZoneKeyRange(namespace: string, min: Document, max: Document, zone: string | null): Promise { assertArgsDefinedType([namespace, min, max, zone], ['string', 'object', 'object', true], 'Shard.updateZoneKeyRange'); this._emitShardApiCall('updateZoneKeyRange', { namespace, min, max, zone }); @@ -187,6 +197,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @serverVersions(['3.4.0', ServerVersions.latest]) async addTagRange(namespace: string, min: Document, max: Document, zone: string): Promise { assertArgsDefinedType([namespace, min, max, zone], ['string', 'object', 'object', true], 'Shard.addTagRange'); @@ -208,6 +219,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @serverVersions(['3.4.0', ServerVersions.latest]) async removeRangeFromZone(ns: string, min: Document, max: Document): Promise { assertArgsDefinedType([ns, min, max], ['string', 'object', 'object'], 'Shard.removeRangeFromZone'); @@ -216,6 +228,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @serverVersions(['3.4.0', ServerVersions.latest]) async removeTagRange(ns: string, min: Document, max: Document): Promise { assertArgsDefinedType([ns, min, max], ['string', 'object', 'object'], 'Shard.removeTagRange'); @@ -231,6 +244,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @serverVersions(['3.4.0', ServerVersions.latest]) async removeShardFromZone(shard: string, zone: string): Promise { assertArgsDefinedType([shard, zone], ['string', 'string'], 'Shard.removeShardFromZone'); @@ -244,6 +258,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @serverVersions(['3.4.0', ServerVersions.latest]) async removeShardTag(shard: string, tag: string): Promise { assertArgsDefinedType([shard, tag], ['string', 'string'], 'Shard.removeShardTag'); @@ -259,6 +274,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) @serverVersions(['3.4.0', ServerVersions.latest]) async enableAutoSplit(): Promise { this._emitShardApiCall('enableAutoSplit', {}); @@ -271,6 +287,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([1]) @serverVersions(['3.4.0', ServerVersions.latest]) async disableAutoSplit(): Promise { this._emitShardApiCall('disableAutoSplit', {}); @@ -283,6 +300,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async splitAt(ns: string, query: Document): Promise { assertArgsDefinedType([ns, query], ['string', 'object'], 'Shard.splitAt'); this._emitShardApiCall('splitAt', { ns, query }); @@ -293,6 +311,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async splitFind(ns: string, query: Document): Promise { assertArgsDefinedType([ns, query], ['string', 'object'], 'Shard.splitFind'); this._emitShardApiCall('splitFind', { ns, query }); @@ -303,6 +322,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async moveChunk(ns: string, query: Document, destination: string | undefined): Promise { assertArgsDefinedType([ns, query, destination], ['string', 'object', 'string'], 'Shard.moveChunk'); this._emitShardApiCall('moveChunk', { ns, query, destination }); @@ -314,6 +334,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) @serverVersions(['4.4.0', ServerVersions.latest]) async balancerCollectionStatus(ns: string): Promise { assertArgsDefinedType([ns], ['string'], 'Shard.balancerCollectionStatus'); @@ -325,6 +346,7 @@ export default class Shard extends ShellApiWithMongoClass { @returnsPromise + @apiVersions([]) async enableBalancing(ns: string): Promise { assertArgsDefinedType([ns], ['string'], 'Shard.enableBalancing'); this._emitShardApiCall('enableBalancing', { ns }); @@ -337,6 +359,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async disableBalancing(ns: string): Promise { assertArgsDefinedType([ns], ['string'], 'Shard.disableBalancing'); this._emitShardApiCall('disableBalancing', { ns }); @@ -349,6 +372,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async getBalancerState(): Promise { this._emitShardApiCall('getBalancerState', {}); const config = await getConfigDB(this._database); @@ -360,6 +384,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async isBalancerRunning(): Promise { this._emitShardApiCall('isBalancerRunning', {}); await getConfigDB(this._database); @@ -369,6 +394,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async startBalancer(timeout = 60000): Promise { assertArgsDefinedType([timeout], ['number'], 'Shard.startBalancer'); this._emitShardApiCall('startBalancer', { timeout }); @@ -378,6 +404,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async stopBalancer(timeout = 60000): Promise { assertArgsDefinedType([timeout], ['number'], 'Shard.stopBalancer'); this._emitShardApiCall('stopBalancer', { timeout }); @@ -387,6 +414,7 @@ export default class Shard extends ShellApiWithMongoClass { } @returnsPromise + @apiVersions([]) async setBalancerState(state: boolean): Promise { assertArgsDefinedType([state], ['boolean'], 'Shard.setBalancerState'); this._emitShardApiCall('setBalancerState', { state }); diff --git a/packages/shell-api/src/shell-api.spec.ts b/packages/shell-api/src/shell-api.spec.ts index 8eff7898b7..e42071e15b 100644 --- a/packages/shell-api/src/shell-api.spec.ts +++ b/packages/shell-api/src/shell-api.spec.ts @@ -4,7 +4,7 @@ import ShellApi from './shell-api'; import { signatures, toShellResult } from './index'; import Cursor from './cursor'; import { nonAsyncFunctionsReturningPromises } from './decorators'; -import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES } from './enums'; +import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES, ALL_API_VERSIONS } from './enums'; import sinon, { StubbedInstance, stubInterface } from 'ts-sinon'; import Mongo from './mongo'; import { ReplPlatform, ServiceProvider, bson, MongoClient } from '@mongosh/service-provider-core'; @@ -40,6 +40,7 @@ describe('ShellApi', () => { returnType: { type: 'unknown', attributes: {} }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: true, shellCommandCompleter: signatures.ShellApi.attributes.use.shellCommandCompleter @@ -51,6 +52,7 @@ describe('ShellApi', () => { returnType: { type: 'unknown', attributes: {} }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: true, shellCommandCompleter: signatures.ShellApi.attributes.show.shellCommandCompleter @@ -62,6 +64,7 @@ describe('ShellApi', () => { returnType: { type: 'unknown', attributes: {} }, platforms: [ ReplPlatform.CLI ], topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: true, shellCommandCompleter: undefined @@ -73,6 +76,7 @@ describe('ShellApi', () => { returnType: { type: 'unknown', attributes: {} }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: true, shellCommandCompleter: undefined @@ -84,6 +88,7 @@ describe('ShellApi', () => { returnType: { type: 'unknown', attributes: {} }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined @@ -95,6 +100,7 @@ describe('ShellApi', () => { returnType: { type: 'unknown', attributes: {} }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined @@ -106,6 +112,7 @@ describe('ShellApi', () => { returnType: { type: 'unknown', attributes: {} }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined @@ -117,6 +124,7 @@ describe('ShellApi', () => { returnType: { type: 'unknown', attributes: {} }, platforms: ALL_PLATFORMS, topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: true, shellCommandCompleter: undefined @@ -128,6 +136,7 @@ describe('ShellApi', () => { returnType: 'Mongo', platforms: [ ReplPlatform.CLI ], topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined @@ -139,6 +148,7 @@ describe('ShellApi', () => { returnType: 'Database', platforms: [ ReplPlatform.CLI ], topologies: ALL_TOPOLOGIES, + apiVersions: ALL_API_VERSIONS, serverVersions: ALL_SERVER_VERSIONS, isDirectShellCommand: false, shellCommandCompleter: undefined diff --git a/packages/shell-api/src/shell-internal-state.ts b/packages/shell-api/src/shell-internal-state.ts index 38ceaabf29..d67daea24c 100644 --- a/packages/shell-api/src/shell-internal-state.ts +++ b/packages/shell-api/src/shell-internal-state.ts @@ -6,7 +6,8 @@ import { ReplPlatform, ServiceProvider, TopologyDescription, - TopologyType + TopologyType, + ServerApi } from '@mongosh/service-provider-core'; import type { ApiEvent, ConfigProvider, MongoshBus, ShellUserConfig } from '@mongosh/types'; import { EventEmitter } from 'events'; @@ -37,6 +38,7 @@ export interface ShellCliOptions { export interface AutocompleteParameters { topology: () => Topologies; + apiVersionInfo: () => Required | undefined; connectionInfo: () => ConnectInfo | undefined; getCollectionCompletionsForCurrentDb: (collName: string) => Promise; getDatabaseCompletions: (dbName: string) => Promise; @@ -296,6 +298,10 @@ export default class ShellInternalState { } return topology; }, + apiVersionInfo: () => { + const { serverApi } = this.currentServiceProvider.getRawClient()?.options ?? {}; + return serverApi?.version ? { strict: false, deprecationErrors: false, ...serverApi } : undefined; + }, connectionInfo: () => { return this.connectionInfo.extraInfo; },