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
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ open class Cursor<out T> internal constructor(protected var cursor: Value?, priv
}

override fun hasNext(): Boolean {
val (cursor, _) = checkClosed()
return cursor.invokeMember("hasNext").asBoolean()
val (cursor, converter) = checkClosed()
return converter.unwrapPromise(cursor.invokeMember("hasNext")).asBoolean()
}

override fun next(): T {
val (cursor, converter) = checkClosed()
if (!hasNext()) throw NoSuchElementException()
return converter.toJava(cursor.invokeMember("next")).value as T
return converter.toJava(converter.unwrapPromise(cursor.invokeMember("next"))).value as T
}

fun tryNext(): T {
val (cursor, converter) = checkClosed()
return converter.toJava(cursor.invokeMember("tryNext")).value as T
return converter.toJava(converter.unwrapPromise(cursor.invokeMember("tryNext"))).value as T
}

fun close() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
org.graalvm.polyglot.PolyglotException: Command failed with error 51024 (Location51024): 'BSON field 'batchSize' value must be >= 0, actual value '-1'' on server %mongohostport%. The full response is {"ok": 0.0, "errmsg": "BSON field 'batchSize' value must be >= 0, actual value '-1'", "code": 51024, "codeName": "Location51024"}
com.mongodb.MongoCommandException: Command failed with error 51024 (Location51024): 'BSON field 'batchSize' value must be >= 0, actual value '-1'' on server %mongohostport%. The full response is {"ok": 0.0, "errmsg": "BSON field 'batchSize]' value must be >= 0, actual value '-1'", "code": 51024, "codeName": "Location51024"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.mongodb.MongoCommandException: Command failed with error 51024 (Location51024): 'BSON field 'batchSize' value must be >= 0, actual value '-1'' on server %mongohostport%. The full response is {"ok": 0.0, "errmsg": "BSON field 'batchSize' value must be >= 0, actual value '-1'", "code": 51024, "codeName": "Location51024"}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
org.graalvm.polyglot.PolyglotException: Command failed with error 2 (BadValue): 'cursor.batchSize must not be negative' on server %mongohostport%. The full response is {"ok": 0.0, "errmsg": "cursor.batchSize must not be negative", "code": 2, "codeName": "BadValue"}
com.mongodb.MongoCommandException: Command failed with error 2 (BadValue): 'cursor.batchSize must not be negative' on server %mongohostport%. The full response is {"ok": 0.0, "errmsg": "cursor.batchSize must not be negative", "code": 2, "codeName": "BadValue"}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
org.graalvm.polyglot.PolyglotException: Query failed with error code 2 and error message 'error processing query: ns=admin.collTree: $and
com.mongodb.MongoQueryException: Query failed with error code 2 and error message 'error processing query: ns=admin.collTree: $and
10 changes: 5 additions & 5 deletions packages/shell-api/src/aggregation-cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,17 @@ export default class AggregationCursor extends ShellApiClass {
}

@returnsPromise
forEach(f: (doc: Document) => void): Promise<void> {
async forEach(f: (doc: Document) => void): Promise<void> {
return this._cursor.forEach(f);
}

@returnsPromise
hasNext(): Promise<boolean> {
async hasNext(): Promise<boolean> {
return this._cursor.hasNext();
}

@returnsPromise
tryNext(): Promise<Document | null> {
async tryNext(): Promise<Document | null> {
return this._cursor.tryNext();
}

Expand Down Expand Up @@ -99,12 +99,12 @@ export default class AggregationCursor extends ShellApiClass {
}

@returnsPromise
next(): Promise<Document | null> {
async next(): Promise<Document | null> {
return this._cursor.next();
}

@returnsPromise
toArray(): Promise<Document[]> {
async toArray(): Promise<Document[]> {
return this._cursor.toArray();
}

Expand Down
3 changes: 1 addition & 2 deletions packages/shell-api/src/change-stream-cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@ export default class ChangeStreamCursor extends ShellApiClass {
return this._cursor.closed;
}

@returnsPromise
isExhausted(): Promise<boolean> {
isExhausted(): never {
throw new MongoshInvalidInputError('isExhausted is not implemented for ChangeStreams because after closing a cursor, the remaining documents in the batch are no longer accessible. If you want to see if the cursor is closed use isClosed. If you want to see if there are documents left in the batch, use tryNext.');
}

Expand Down
3 changes: 1 addition & 2 deletions packages/shell-api/src/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -753,9 +753,8 @@ export default class Collection extends ShellApiClass {
);
}

@returnsPromise
@deprecated
save(): Promise<void> {
save(): never {
throw new MongoshInvalidInputError(
'Collection.save() is deprecated. Use insertOne, insertMany, updateOne, or updateMany.'
);
Expand Down
14 changes: 7 additions & 7 deletions packages/shell-api/src/cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export default class Cursor extends ShellApiClass {

@serverVersions([ServerVersions.earliest, '4.0.0'])
@returnsPromise
count(): Promise<number> {
async count(): Promise<number> {
return this._cursor.count();
}

Expand Down Expand Up @@ -164,12 +164,12 @@ export default class Cursor extends ShellApiClass {
}

@returnsPromise
forEach(f: (doc: Document) => void): Promise<void> {
async forEach(f: (doc: Document) => void): Promise<void> {
return this._cursor.forEach(f);
}

@returnsPromise
hasNext(): Promise<boolean> {
async hasNext(): Promise<boolean> {
if (this._tailable) {
printWarning(
'If this is a tailable cursor with awaitData, and there are no documents in the batch, this method ' +
Expand All @@ -181,7 +181,7 @@ export default class Cursor extends ShellApiClass {
}

@returnsPromise
tryNext(): Promise<Document | null> {
async tryNext(): Promise<Document | null> {
return this._cursor.tryNext();
}

Expand Down Expand Up @@ -253,7 +253,7 @@ export default class Cursor extends ShellApiClass {
}

@returnsPromise
next(): Promise<Document | null> {
async next(): Promise<Document | null> {
if (this._tailable) {
printWarning(
'If this is a tailable cursor with awaitData, and there are no documents in the batch, this' +
Expand Down Expand Up @@ -308,7 +308,7 @@ export default class Cursor extends ShellApiClass {
}

@returnsPromise
size(): Promise<number> {
async size(): Promise<number> {
return this._cursor.count();
}

Expand Down Expand Up @@ -336,7 +336,7 @@ export default class Cursor extends ShellApiClass {
}

@returnsPromise
toArray(): Promise<Document[]> {
async toArray(): Promise<Document[]> {
return this._cursor.toArray();
}

Expand Down
10 changes: 5 additions & 5 deletions packages/shell-api/src/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,7 @@ export function shellApiClassDefault(constructor: Function): void {
function markImplicitlyAwaited<T extends(...args: any) => Promise<any>>(orig: T): ((...args: Parameters<T>) => Promise<any>) {
function wrapper(this: any, ...args: any[]) {
const origResult = orig.call(this, ...args);
return Object.prototype.toString.call(origResult) === '[object Promise]' ? addHiddenDataProperty(
origResult,
Symbol.for('@@mongosh.syntheticPromise'),
true
) : origResult;
return addHiddenDataProperty(origResult, Symbol.for('@@mongosh.syntheticPromise'), true);
}
Object.setPrototypeOf(wrapper, Object.getPrototypeOf(orig));
Object.defineProperties(wrapper, Object.getOwnPropertyDescriptors(orig));
Expand Down Expand Up @@ -328,10 +324,14 @@ export function topologies(topologiesArray: Topologies[]): Function {
descriptor.value.topologies = topologiesArray;
};
}
export const nonAsyncFunctionsReturningPromises: string[] = []; // For testing.
export function returnsPromise(_target: any, _propertyKey: string, descriptor: PropertyDescriptor): void {
const orig = descriptor.value;
orig.returnsPromise = true;
descriptor.value = markImplicitlyAwaited(descriptor.value);
if (orig.constructor.name !== 'AsyncFunction') {
nonAsyncFunctionsReturningPromises.push(orig.name);
}
}
export function directShellCommand(_target: any, _propertyKey: string, descriptor: PropertyDescriptor): void {
descriptor.value.isDirectShellCommand = true;
Expand Down
10 changes: 7 additions & 3 deletions packages/shell-api/src/field-level-encryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
classPlatforms,
hasAsyncChild,
returnsPromise,
returnType,
ShellApiClass,
shellApiClassDefault
} from './decorators';
Expand Down Expand Up @@ -122,7 +123,7 @@ export class KeyVault extends ShellApiClass {
createKey(kms: ClientEncryptionDataKeyProvider, options: AWSEncryptionKeyOptions | AzureEncryptionKeyOptions | GCPEncryptionKeyOptions | undefined): Promise<Document>
createKey(kms: ClientEncryptionDataKeyProvider, options: AWSEncryptionKeyOptions | AzureEncryptionKeyOptions | GCPEncryptionKeyOptions | undefined, keyAltNames: string[]): Promise<Document>
@returnsPromise
createKey(
async createKey(
kms: ClientEncryptionDataKeyProvider,
masterKeyOrAltNames?: AWSEncryptionKeyOptions | AzureEncryptionKeyOptions | GCPEncryptionKeyOptions | string | undefined | string[],
keyAltNames?: string[]
Expand Down Expand Up @@ -177,28 +178,31 @@ export class KeyVault extends ShellApiClass {
return this._clientEncryption._libmongocrypt.createDataKey(kms, options as ClientEncryptionCreateDataKeyProviderOptions);
}

@returnType('Cursor')
getKey(keyId: BinaryType): Cursor {
assertArgsDefinedType([keyId], [true], 'KeyVault.getKey');
return this._keyColl.find({ '_id': keyId });
}

@returnType('Cursor')
getKeyByAltName(keyAltName: string): Cursor {
assertArgsDefinedType([keyAltName], ['string'], 'KeyVault.getKeyByAltName');
return this._keyColl.find({ 'keyAltNames': keyAltName });
}

@returnType('Cursor')
getKeys(): Cursor {
return this._keyColl.find({});
}

@returnsPromise
deleteKey(keyId: BinaryType): Promise<DeleteResult | Document> {
async deleteKey(keyId: BinaryType): Promise<DeleteResult | Document> {
assertArgsDefinedType([keyId], [true], 'KeyVault.deleteKey');
return this._keyColl.deleteOne({ '_id': keyId });
}

@returnsPromise
addKeyAlternateName(keyId: BinaryType, keyAltName: string): Promise<Document> {
async addKeyAlternateName(keyId: BinaryType, keyAltName: string): Promise<Document> {
assertArgsDefinedType([keyId, keyAltName], [true, 'string'], 'KeyVault.addKeyAlternateName');
return this._keyColl.findAndModify({
query: { '_id': keyId },
Expand Down
7 changes: 7 additions & 0 deletions packages/shell-api/src/shell-api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { expect } from 'chai';
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 sinon, { StubbedInstance, stubInterface } from 'ts-sinon';
import Mongo from './mongo';
Expand Down Expand Up @@ -625,3 +626,9 @@ describe('ShellApi', () => {
}
});
});

describe('returnsPromise marks async functions', () => {
it('no non-async functions are marked returnsPromise', () => {
expect(nonAsyncFunctionsReturningPromises).to.deep.equal([]);
});
});