Skip to content

Commit aab0bd1

Browse files
migrate getMore operatoin
1 parent c460580 commit aab0bd1

File tree

3 files changed

+54
-189
lines changed

3 files changed

+54
-189
lines changed

src/operations/get_more.ts

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import type { Long } from '../bson';
1+
import type { Document, Long } from '../bson';
2+
import { type Connection } from '../cmap/connection';
23
import { CursorResponse } from '../cmap/wire_protocol/responses';
34
import { MongoRuntimeError } from '../error';
4-
import type { Server } from '../sdam/server';
5-
import type { ClientSession } from '../sessions';
5+
import type { Server, ServerCommandOptions } from '../sdam/server';
66
import { type TimeoutContext } from '../timeout';
77
import { maxWireVersion, type MongoDBNamespace } from '../utils';
8-
import { AbstractOperation, Aspect, defineAspects, type OperationOptions } from './operation';
8+
import { Aspect, defineAspects, ModernizedOperation, type OperationOptions } from './operation';
99

1010
/** @internal */
1111
export interface GetMoreOptions extends OperationOptions {
@@ -37,7 +37,8 @@ export interface GetMoreCommand {
3737
}
3838

3939
/** @internal */
40-
export class GetMoreOperation extends AbstractOperation {
40+
export class GetMoreOperation extends ModernizedOperation<CursorResponse> {
41+
override SERVER_COMMAND_RESPONSE_TYPE = CursorResponse;
4142
cursorId: Long;
4243
override options: GetMoreOptions;
4344

@@ -53,19 +54,8 @@ export class GetMoreOperation extends AbstractOperation {
5354
override get commandName() {
5455
return 'getMore' as const;
5556
}
56-
/**
57-
* Although there is a server already associated with the get more operation, the signature
58-
* for execute passes a server so we will just use that one.
59-
*/
60-
override async execute(
61-
server: Server,
62-
_session: ClientSession | undefined,
63-
timeoutContext: TimeoutContext
64-
): Promise<CursorResponse> {
65-
if (server !== this.server) {
66-
throw new MongoRuntimeError('Getmore must run on the same server operation began on');
67-
}
6857

58+
override buildCommand(connection: Connection): Document {
6959
if (this.cursorId == null || this.cursorId.isZero()) {
7060
throw new MongoRuntimeError('Unable to iterate cursor with no id');
7161
}
@@ -92,18 +82,26 @@ export class GetMoreOperation extends AbstractOperation {
9282

9383
// we check for undefined specifically here to allow falsy values
9484
// eslint-disable-next-line no-restricted-syntax
95-
if (this.options.comment !== undefined && maxWireVersion(server) >= 9) {
85+
if (this.options.comment !== undefined && maxWireVersion(connection) >= 9) {
9686
getMoreCmd.comment = this.options.comment;
9787
}
9888

99-
const commandOptions = {
89+
return getMoreCmd;
90+
}
91+
92+
override buildOptions(timeoutContext: TimeoutContext): ServerCommandOptions {
93+
return {
10094
returnFieldSelector: null,
10195
documentsReturnedIn: 'nextBatch',
10296
timeoutContext,
10397
...this.options
10498
};
99+
}
105100

106-
return await server.command(this.ns, getMoreCmd, commandOptions, CursorResponse);
101+
override handleOk(
102+
response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
103+
): CursorResponse {
104+
return response;
107105
}
108106
}
109107

src/sdam/server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export type ServerEvents = {
111111
/** @internal */
112112
export type ServerCommandOptions = Omit<CommandOptions, 'timeoutContext' | 'socketTimeoutMS'> & {
113113
timeoutContext: TimeoutContext;
114+
returnFieldSelector?: Document | null;
114115
} & Abortable;
115116

116117
/** @internal */

test/unit/operations/get_more.test.ts

Lines changed: 35 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import { expect } from 'chai';
2-
import * as sinon from 'sinon';
32

43
import {
54
Aspect,
6-
ClientSession,
75
GetMoreOperation,
86
Long,
9-
MongoRuntimeError,
107
ns,
118
ReadPreference,
129
Server,
@@ -23,10 +20,6 @@ describe('GetMoreOperation', function () {
2320
readPreference: ReadPreference.primary
2421
};
2522

26-
afterEach(function () {
27-
sinon.restore();
28-
});
29-
3023
describe('#constructor', function () {
3124
const topology = topologyWithPlaceholderClient([], {} as any);
3225
const server = new Server(topology, new ServerDescription('a:1'), {} as any);
@@ -45,161 +38,24 @@ describe('GetMoreOperation', function () {
4538
});
4639
});
4740

48-
describe('#execute', function () {
49-
context('when the server is the same as the instance', function () {
50-
it('executes a getMore on the provided server', async function () {
51-
const server = new Server(
52-
topologyWithPlaceholderClient([], {} as any),
53-
new ServerDescription('a:1'),
54-
{} as any
55-
);
56-
const opts = {
57-
...options,
58-
documentsReturnedIn: 'nextBatch',
59-
returnFieldSelector: null,
60-
timeoutContext: undefined
61-
};
62-
const operation = new GetMoreOperation(namespace, cursorId, server, opts);
63-
const stub = sinon.stub(server, 'command').resolves({});
64-
65-
const expectedGetMoreCommand = {
66-
getMore: cursorId,
67-
collection: namespace.collection,
68-
batchSize: 100,
69-
maxTimeMS: 500
70-
};
71-
72-
await operation.execute(server, undefined);
73-
expect(stub.calledOnce).to.be.true;
74-
const call = stub.getCall(0);
75-
expect(call.args[0]).to.equal(namespace);
76-
expect(call.args[1]).to.deep.equal(expectedGetMoreCommand);
77-
expect(call.args[2]).to.deep.equal(opts);
78-
});
79-
});
80-
81-
context('when the server is not the same as the instance', function () {
82-
it('errors in the callback', async function () {
83-
const server1 = new Server(
84-
topologyWithPlaceholderClient([], {} as any),
85-
new ServerDescription('a:1'),
86-
{} as any
87-
);
88-
const server2 = new Server(
89-
topologyWithPlaceholderClient([], {} as any),
90-
new ServerDescription('a:1'),
91-
{} as any
92-
);
93-
const session = sinon.createStubInstance(ClientSession);
94-
const opts = { ...options, session };
95-
const operation = new GetMoreOperation(namespace, cursorId, server1, opts);
96-
const error = await operation.execute(server2, session).catch(error => error);
97-
expect(error).to.be.instanceOf(MongoRuntimeError);
98-
expect(error.message).to.equal('Getmore must run on the same server operation began on');
99-
});
100-
});
101-
102-
context('command construction', () => {
103-
const cursorId = Long.fromBigInt(0xffff_ffffn);
104-
const namespace = ns('db.collection');
105-
const server = new Server(
106-
topologyWithPlaceholderClient([], {} as any),
107-
new ServerDescription('a:1'),
108-
{} as any
109-
);
110-
111-
it('should build basic getMore command with cursorId and collection', async () => {
112-
const getMoreOperation = new GetMoreOperation(namespace, cursorId, server, {});
113-
const stub = sinon.stub(server, 'command').resolves({});
114-
await getMoreOperation.execute(server, undefined);
115-
expect(stub).to.have.been.calledOnceWith(namespace, {
116-
getMore: cursorId,
117-
collection: namespace.collection
118-
});
119-
});
120-
121-
it('should build getMore command with batchSize', async () => {
122-
const options = {
123-
batchSize: 234
124-
};
125-
const getMoreOperation = new GetMoreOperation(namespace, cursorId, server, options);
126-
const stub = sinon.stub(server, 'command').resolves({});
127-
await getMoreOperation.execute(server, undefined);
128-
expect(stub).to.have.been.calledOnceWith(
129-
namespace,
130-
sinon.match.has('batchSize', options.batchSize)
131-
);
132-
});
133-
134-
it('should build getMore command with maxTimeMS if maxAwaitTimeMS specified', async () => {
135-
const options = {
136-
maxAwaitTimeMS: 234
137-
};
138-
const getMoreOperation = new GetMoreOperation(namespace, cursorId, server, options);
139-
const stub = sinon.stub(server, 'command').resolves({});
140-
await getMoreOperation.execute(server, undefined);
141-
expect(stub).to.have.been.calledOnceWith(
142-
namespace,
143-
sinon.match.has('maxTimeMS', options.maxAwaitTimeMS)
144-
);
145-
});
146-
147-
context('comment', function () {
148-
const optionsWithComment = {
149-
...options,
150-
comment: 'test'
151-
};
41+
context('command construction', () => {
42+
const cursorId = Long.fromBigInt(0xffff_ffffn);
43+
const namespace = ns('db.collection');
44+
const server = new Server(
45+
topologyWithPlaceholderClient([], {} as any),
46+
new ServerDescription('a:1'),
47+
{} as any
48+
);
15249

153-
const serverVersions = [
154-
{
155-
serverVersion: 8,
156-
getMore: {
157-
getMore: cursorId,
158-
collection: namespace.collection,
159-
batchSize: 100,
160-
maxTimeMS: 500
161-
}
162-
},
163-
{
164-
serverVersion: 9,
165-
getMore: {
166-
getMore: cursorId,
167-
collection: namespace.collection,
168-
batchSize: 100,
169-
maxTimeMS: 500,
170-
comment: 'test'
171-
}
172-
},
173-
{
174-
serverVersion: 10,
175-
getMore: {
176-
getMore: cursorId,
177-
collection: namespace.collection,
178-
batchSize: 100,
179-
maxTimeMS: 500,
180-
comment: 'test'
181-
}
182-
}
183-
];
184-
for (const { serverVersion, getMore } of serverVersions) {
185-
const verb = serverVersion < 9 ? 'does not' : 'does';
186-
const state = serverVersion < 9 ? 'less than 9' : 'greater than or equal to 9';
187-
it(`${verb} set the comment on the command if the server wire version is ${state}`, async () => {
188-
const server = new Server(
189-
topologyWithPlaceholderClient([], {} as any),
190-
new ServerDescription('a:1'),
191-
{} as any
192-
);
193-
server.hello = {
194-
maxWireVersion: serverVersion
195-
};
196-
const operation = new GetMoreOperation(namespace, cursorId, server, optionsWithComment);
197-
const stub = sinon.stub(server, 'command').resolves({});
198-
await operation.execute(server, undefined);
199-
expect(stub).to.have.been.calledOnceWith(namespace, getMore);
200-
});
201-
}
202-
});
50+
it('should build getMore command with maxTimeMS if maxAwaitTimeMS specified', async () => {
51+
const options = {
52+
maxAwaitTimeMS: 234
53+
};
54+
const getMoreOperation = new GetMoreOperation(namespace, cursorId, server, options);
55+
const { maxTimeMS } = getMoreOperation.buildCommand({
56+
description: {}
57+
} as any);
58+
expect(maxTimeMS).to.equal(234);
20359
});
20460

20561
context('error cases', () => {
@@ -208,18 +64,20 @@ describe('GetMoreOperation', function () {
20864
new ServerDescription('a:1'),
20965
{} as any
21066
);
211-
sinon.stub(server, 'command').resolves({});
21267

21368
it('should throw if the cursorId is undefined', async () => {
21469
const getMoreOperation = new GetMoreOperation(
21570
ns('db.collection'),
216-
// @ts-expect-error: Testing undefined cursorId
21771
undefined,
21872
server,
21973
options
22074
);
221-
const error = await getMoreOperation.execute(server, undefined).catch(error => error);
222-
expect(error).to.be.instanceOf(MongoRuntimeError);
75+
const connection = {
76+
description: {}
77+
} as any;
78+
expect(() => {
79+
getMoreOperation.buildCommand(connection);
80+
}).to.throw(/Unable to iterate cursor with no id/);
22381
});
22482

22583
it('should throw if the collection is undefined', async () => {
@@ -229,8 +87,12 @@ describe('GetMoreOperation', function () {
22987
server,
23088
options
23189
);
232-
const error = await getMoreOperation.execute(server, undefined).catch(error => error);
233-
expect(error).to.be.instanceOf(MongoRuntimeError);
90+
const connection = {
91+
description: {}
92+
} as any;
93+
expect(() => {
94+
getMoreOperation.buildCommand(connection);
95+
}).to.throw(/A collection name must be determined before getMore/);
23496
});
23597

23698
it('should throw if the cursorId is zero', async () => {
@@ -240,8 +102,12 @@ describe('GetMoreOperation', function () {
240102
server,
241103
options
242104
);
243-
const error = await getMoreOperation.execute(server, undefined).catch(error => error);
244-
expect(error).to.be.instanceOf(MongoRuntimeError);
105+
const connection = {
106+
description: {}
107+
} as any;
108+
expect(() => {
109+
getMoreOperation.buildCommand(connection);
110+
}).to.throw(/Unable to iterate cursor with no id/);
245111
});
246112
});
247113
});

0 commit comments

Comments
 (0)