Skip to content

Commit 70f8935

Browse files
committed
fixes
1 parent 176a56b commit 70f8935

File tree

12 files changed

+81
-42
lines changed

12 files changed

+81
-42
lines changed

src/cmap/connection.ts

-4
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,6 @@ export class Connection extends EventEmitter {
367367

368368
const inTransaction = session && (session.inTransaction() || isTransactionCommand(finalCmd));
369369

370-
if (!inTransaction && !finalCmd.getMore && this.serverApi) {
371-
applyServerApiVersion(finalCmd, this.serverApi);
372-
}
373-
374370
const commandResponseHandler = inTransaction
375371
? (err?: AnyError, ...args: Document[]) => {
376372
// We need to add a TransientTransactionError errorLabel, as stated in the transaction spec.

src/connection_string.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import {
2121
MongoClient,
2222
MongoClientOptions,
2323
MongoOptions,
24-
PkFactory
24+
PkFactory,
25+
ServerApi
2526
} from './mongo_client';
2627
import { MongoCredentials } from './cmap/auth/mongo_credentials';
2728
import type { TagSet } from './sdam/server_description';
@@ -572,6 +573,15 @@ export const OPTIONS = {
572573
autoEncryption: {
573574
type: 'record'
574575
},
576+
serverApi: {
577+
target: 'serverApi',
578+
transform({ values: [version] }): ServerApi {
579+
if (typeof version === 'string') {
580+
return { version };
581+
}
582+
return version as ServerApi;
583+
}
584+
},
575585
checkKeys: {
576586
type: 'boolean'
577587
},

src/mongo_client.ts

+8-11
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
222222
logger?: Logger;
223223
/** Enable command monitoring for this client */
224224
monitorCommands?: boolean;
225+
/** Server API version */
226+
serverApi?: ServerApiVersion | ServerApi;
225227
/** Optionally enable client side auto encryption */
226228
autoEncryption?: AutoEncryptionOptions;
227229
/** Allows a wrapping driver to amend the client metadata generated by the driver to include information about the wrapping driver */
@@ -243,13 +245,13 @@ export interface MongoClientPrivate {
243245
readConcern?: ReadConcern;
244246
writeConcern?: WriteConcern;
245247
readPreference: ReadPreference;
248+
serverApi: ServerApi;
246249
bsonOptions: BSONSerializeOptions;
247250
namespace: MongoDBNamespace;
248251
logger: Logger;
249252
}
250253

251254
const kOptions = Symbol('options');
252-
const kServerApi = Symbol('serverApi');
253255

254256
/**
255257
* The **MongoClient** class is a class that allows for making Connections to MongoDB.
@@ -302,24 +304,17 @@ export class MongoClient extends EventEmitter {
302304
*/
303305
[kOptions]: MongoOptions;
304306

305-
/**
306-
* The MongoDB Server API version
307-
* @internal
308-
* */
309-
[kServerApi]: ServerApi;
310-
311307
// debugging
312308
originalUri;
313309
originalOptions;
314310

315-
constructor(url: string, options?: MongoClientOptions, serverApi?: ServerApi) {
311+
constructor(url: string, options?: MongoClientOptions) {
316312
super();
317313

318314
this.originalUri = url;
319315
this.originalOptions = options;
320316

321317
this[kOptions] = parseOptions(url, this, options);
322-
this[kServerApi] = Object.freeze({ version: ServerApiVersion.v1, ...serverApi });
323318

324319
// The internal state
325320
this.s = {
@@ -329,6 +324,7 @@ export class MongoClient extends EventEmitter {
329324
readConcern: this[kOptions].readConcern,
330325
writeConcern: this[kOptions].writeConcern,
331326
readPreference: this[kOptions].readPreference,
327+
serverApi: this[kOptions].serverApi,
332328
bsonOptions: resolveBSONOptions(this[kOptions]),
333329
namespace: ns('admin'),
334330
logger: this[kOptions].logger
@@ -339,8 +335,8 @@ export class MongoClient extends EventEmitter {
339335
return Object.freeze({ ...this[kOptions] });
340336
}
341337

342-
get serverApi(): Readonly<ServerApi> {
343-
return this[kServerApi];
338+
get serverApi(): Readonly<ServerApi | undefined> {
339+
return this[kOptions].serverApi && Object.freeze({ ...this[kOptions].serverApi });
344340
}
345341

346342
get autoEncrypter(): AutoEncrypter | undefined {
@@ -652,6 +648,7 @@ export interface MongoOptions
652648
credentials?: MongoCredentials;
653649
readPreference: ReadPreference;
654650
readConcern: ReadConcern;
651+
serverApi: ServerApi;
655652
writeConcern: WriteConcern;
656653
dbName: string;
657654
metadata: ClientMetadata;

src/operations/command.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import { Aspect, AbstractOperation, OperationOptions } from './operation';
22
import { ReadConcern } from '../read_concern';
33
import { WriteConcern, WriteConcernOptions } from '../write_concern';
4-
import { maxWireVersion, MongoDBNamespace, Callback, decorateWithExplain } from '../utils';
4+
import {
5+
applyServerApiVersion,
6+
maxWireVersion,
7+
MongoDBNamespace,
8+
Callback,
9+
decorateWithExplain
10+
} from '../utils';
511
import type { ReadPreference } from '../read_preference';
612
import { ClientSession, commandSupportsReadConcern } from '../sessions';
713
import { MongoError } from '../error';
@@ -173,6 +179,13 @@ export abstract class CommandOperation<T> extends AbstractOperation<T> {
173179
}
174180
}
175181

182+
const notMidTransaction = !inTransaction || !this.session.transaction.hasStarted;
183+
// FIXME: get these hacked conditions in the right place
184+
const hackedConditions = !cmd.getMore && !cmd.commitTransaction;
185+
if (notMidTransaction && hackedConditions && server.serverApi) {
186+
applyServerApiVersion(cmd, server.serverApi);
187+
}
188+
176189
server.command(this.ns, cmd, { fullResult: !!this.fullResponse, ...options }, callback);
177190
}
178191
}

src/operations/find.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
MongoDBNamespace,
55
Callback,
66
normalizeHintField,
7-
decorateWithExplain
7+
decorateWithExplain,
8+
applyServerApiVersion
89
} from '../utils';
910
import { MongoError } from '../error';
1011
import type { Document } from '../bson';
@@ -153,6 +154,9 @@ export class FindOperation extends CommandOperation<Document> {
153154
if (this.explain) {
154155
findCommand = decorateWithExplain(findCommand, this.explain);
155156
}
157+
if (server.serverApi) {
158+
applyServerApiVersion(findCommand, server.serverApi);
159+
}
156160

157161
server.command(
158162
this.ns,

src/sdam/server.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import type { ServerHeartbeatSucceededEvent } from './events';
4444
import type { ClientSession } from '../sessions';
4545
import type { Document, Long } from '../bson';
4646
import type { AutoEncrypter } from '../deps';
47+
import type { ServerApi } from '../mongo_client';
4748

4849
// Used for filtering out fields for logging
4950
const DEBUG_FIELDS = [
@@ -106,6 +107,7 @@ export interface ServerPrivate {
106107
export class Server extends EventEmitter {
107108
/** @internal */
108109
s: ServerPrivate;
110+
serverApi?: ServerApi;
109111
clusterTime?: ClusterTime;
110112
ismaster?: Document;
111113
[kMonitor]: Monitor;
@@ -131,7 +133,7 @@ export class Server extends EventEmitter {
131133
constructor(topology: Topology, description: ServerDescription, options: ServerOptions) {
132134
super();
133135

134-
options.serverApi = topology.serverApi;
136+
this.serverApi = options.serverApi = topology.serverApi;
135137

136138
const poolOptions = { hostAddress: description.hostAddress, ...options };
137139

src/transactions.ts

+5
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ export class Transaction {
113113
return !!this.server;
114114
}
115115

116+
/** @returns Whether the transaction has started */
117+
get hasStarted(): boolean {
118+
return this.state === TxnState.TRANSACTION_IN_PROGRESS;
119+
}
120+
116121
/**
117122
* @returns Whether this session is presently in a transaction
118123
*/

test/functional/unified-spec-runner/entities.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ export class UnifiedMongoClient extends MongoClient {
3737
} as const;
3838

3939
constructor(url: string, description: ClientEntity) {
40-
super(url, { monitorCommands: true, ...description.uriOptions }, description.serverApi);
40+
super(url, {
41+
monitorCommands: true,
42+
...description.uriOptions,
43+
serverApi: description.serverApi
44+
});
4145
this.events = [];
4246
this.failPoints = [];
4347
this.ignoredEvents = [

test/functional/unified-spec-runner/unified-utils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ export async function topologySatisfies(
3333
Sharded: 'sharded'
3434
}[config.topologyType];
3535

36-
if (r.topologies.includes('sharded-replicaset')) {
36+
if (r.topologies.includes('sharded-replicaset') && topologyType === 'sharded') {
3737
const shards = await utilClient.db('config').collection('shards').find({}).toArray();
38-
ok &&= shards.every(shard => shard.host.split(',').length > 1);
38+
ok &&= shards.length > 0 && shards.every(shard => shard.host.split(',').length > 1);
3939
} else {
4040
if (!topologyType) throw new Error(`Topology undiscovered: ${config.topologyType}`);
4141
ok &&= r.topologies.includes(topologyType);

test/functional/versioned-api.test.js

+13-10
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,19 @@ describe('Versioned API', function () {
99
expect(versionedApiTest).to.exist;
1010
context(String(versionedApiTest.description), function () {
1111
for (const test of versionedApiTest.tests) {
12-
it(String(test.description), async function () {
13-
try {
14-
await runUnifiedTest(this, versionedApiTest, test);
15-
} catch (error) {
16-
if (error.message.includes('not implemented.')) {
17-
console.log(`${test.description}: was skipped due to missing functionality`);
18-
console.log(error.stack);
19-
this.skip();
20-
} else {
21-
throw error;
12+
it(String(test.description), {
13+
metadata: { sessions: { skipLeakTests: true } },
14+
test: async function () {
15+
try {
16+
await runUnifiedTest(this, versionedApiTest, test);
17+
} catch (error) {
18+
if (error.message.includes('not implemented.')) {
19+
console.log(`${test.description}: was skipped due to missing functionality`);
20+
console.log(error.stack);
21+
this.skip();
22+
} else {
23+
throw error;
24+
}
2225
}
2326
}
2427
});

test/tools/runner/config.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ function convertToConnStringMap(obj) {
3434
}
3535

3636
class TestConfiguration {
37-
constructor(uri, context, serverApiVersion) {
37+
constructor(uri, context) {
3838
const { url, hosts } = parseURI(uri);
3939
const hostAddresses = hosts.map(HostAddress.fromString);
4040
this.topologyType = context.topologyType;
4141
this.version = context.version;
42-
this.serverApiVersion = serverApiVersion;
4342
this.clientSideEncryption = context.clientSideEncryption;
43+
this.serverApi = context.serverApi;
4444
this.parameters = undefined;
4545
this.options = {
4646
hosts,
@@ -97,17 +97,17 @@ class TestConfiguration {
9797
}
9898

9999
newClient(dbOptions, serverOptions) {
100+
const defaultOptions = { minHeartbeatFrequencyMS: 100 };
101+
if (this.serverApi) {
102+
Object.assign(defaultOptions, { serverApi: this.serverApi });
103+
}
100104
// support MongoClient constructor form (url, options) for `newClient`
101105
if (typeof dbOptions === 'string') {
102-
return new MongoClient(
103-
dbOptions,
104-
Object.assign({ minHeartbeatFrequencyMS: 100 }, serverOptions),
105-
{ version: this.serverApiVersion }
106-
);
106+
return new MongoClient(dbOptions, Object.assign(defaultOptions, serverOptions));
107107
}
108108

109109
dbOptions = dbOptions || {};
110-
serverOptions = Object.assign({}, { minHeartbeatFrequencyMS: 100 }, serverOptions);
110+
serverOptions = Object.assign({}, defaultOptions, serverOptions);
111111

112112
// Fall back
113113
let dbHost = (serverOptions && serverOptions.host) || this.options.host;
@@ -166,6 +166,7 @@ class TestConfiguration {
166166
if (Reflect.has(serverOptions, 'host') || Reflect.has(serverOptions, 'port')) {
167167
throw new Error(`Cannot use options to specify host/port, must be in ${connectionString}`);
168168
}
169+
169170
return new MongoClient(connectionString, serverOptions);
170171
}
171172

test/tools/runner/index.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ before(function (_done) {
6161
// )} topology`
6262
// );
6363

64-
const client = new MongoClient(MONGODB_URI);
64+
const options = MONGODB_API_VERSION ? { serverApi: MONGODB_API_VERSION } : {};
65+
const client = new MongoClient(MONGODB_URI, options);
6566
const done = err => client.close(err2 => _done(err || err2));
6667

6768
client.connect(err => {
@@ -71,14 +72,17 @@ before(function (_done) {
7172
}
7273

7374
initializeFilters(client, (err, context) => {
75+
if (MONGODB_API_VERSION) {
76+
Object.assign(context, { serverApi: MONGODB_API_VERSION });
77+
}
7478
if (err) {
7579
done(err);
7680
return;
7781
}
7882

7983
// replace this when mocha supports dynamic skipping with `afterEach`
8084
filterOutTests(this._runnable.parent);
81-
this.configuration = new TestConfiguration(MONGODB_URI, context, MONGODB_API_VERSION);
85+
this.configuration = new TestConfiguration(MONGODB_URI, context);
8286
done();
8387
});
8488
});

0 commit comments

Comments
 (0)