Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions packages/autocomplete/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -28,6 +37,7 @@ const sharded440 = {

const standalone300 = {
topology: () => Topologies.Standalone,
apiVersionInfo: () => undefined,
connectionInfo: () => ({
is_atlas: false,
is_data_lake: false,
Expand All @@ -38,6 +48,7 @@ const standalone300 = {
};
const datalake440 = {
topology: () => Topologies.Sharded,
apiVersionInfo: () => undefined,
connectionInfo: () => ({
is_atlas: true,
is_data_lake: true,
Expand All @@ -49,13 +60,15 @@ const datalake440 = {

const noParams = {
topology: () => Topologies.Standalone,
apiVersionInfo: () => undefined,
connectionInfo: () => undefined,
getCollectionCompletionsForCurrentDb: () => collections,
getDatabaseCompletions: () => databases
};

const emptyConnectionInfoParams = {
topology: () => Topologies.Standalone,
apiVersionInfo: () => undefined,
connectionInfo: () => ({}),
getCollectionCompletionsForCurrentDb: () => collections,
getDatabaseCompletions: () => databases
Expand Down Expand Up @@ -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]);
});
});
});
42 changes: 29 additions & 13 deletions packages/autocomplete/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string[]>;
getDatabaseCompletions: (dbName: string) => string[] | Promise<string[]>;
}
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -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 =
Expand Down
6 changes: 3 additions & 3 deletions packages/autocomplete/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/autocomplete/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion packages/shell-api/src/abstract-cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
toShellResult,
returnType,
ShellApiWithMongoClass,
returnsPromise
returnsPromise,
apiVersions
} from './decorators';
import type Mongo from './mongo';
import type {
Expand Down Expand Up @@ -194,6 +195,7 @@ export abstract class AbstractCursor extends ShellApiWithMongoClass {
}

@returnsPromise
@apiVersions([1])
async explain(verbosity?: ExplainVerbosityLike): Promise<any> {
// TODO: @maurizio we should probably move this in the Explain class?
// NOTE: the node driver always returns the full explain plan
Expand Down
3 changes: 2 additions & 1 deletion packages/shell-api/src/aggregation-cursor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions packages/shell-api/src/bulk.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
14 changes: 13 additions & 1 deletion packages/shell-api/src/bulk.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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.',
Expand All @@ -52,39 +54,45 @@ export class BulkFindOp extends ShellApiWithMongoClass {
}

@returnType('BulkFindOp')
@apiVersions([1])
hint(hintDoc: Document): BulkFindOp {
assertArgsDefinedType([hintDoc], [true], 'BulkFindOp.hint');
this._hint = hintDoc;
return this;
}

@returnType('Bulk')
@apiVersions([1])
delete(): Bulk {
this._parentBulk._batchCounts.nRemoveOps++;
this._serviceProviderBulkFindOp.delete();
return this._parentBulk;
}

@returnType('Bulk')
@apiVersions([1])
deleteOne(): Bulk {
this._parentBulk._batchCounts.nRemoveOps++;
this._serviceProviderBulkFindOp.deleteOne();
return this._parentBulk;
}

@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');
Expand All @@ -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');
Expand Down Expand Up @@ -182,6 +191,7 @@ export default class Bulk extends ShellApiWithMongoClass {
}

@returnsPromise
@apiVersions([1])
async execute(writeConcern?: WriteConcern): Promise<BulkWriteResult> {
const { result } = await this._serviceProviderBulkOp.execute() as any;
this._executed = true;
Expand All @@ -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');
Expand Down
3 changes: 2 additions & 1 deletion packages/shell-api/src/change-stream-cursor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions packages/shell-api/src/collection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading