Skip to content

Commit 4c2ff26

Browse files
refactor(NODE-7085): refactor db collection management APIs to use modernized operation (#4612)
1 parent e628296 commit 4c2ff26

File tree

7 files changed

+199
-117
lines changed

7 files changed

+199
-117
lines changed

src/operations/create_collection.ts

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1+
import { type Connection } from '..';
12
import type { Document } from '../bson';
23
import {
34
MIN_SUPPORTED_QE_SERVER_VERSION,
45
MIN_SUPPORTED_QE_WIRE_VERSION
56
} from '../cmap/wire_protocol/constants';
7+
import { MongoDBResponse } from '../cmap/wire_protocol/responses';
68
import { Collection } from '../collection';
79
import type { Db } from '../db';
810
import { MongoCompatibilityError } from '../error';
911
import type { PkFactory } from '../mongo_client';
10-
import type { Server } from '../sdam/server';
1112
import type { ClientSession } from '../sessions';
1213
import { TimeoutContext } from '../timeout';
13-
import { CommandOperation, type CommandOperationOptions } from './command';
14+
import { maxWireVersion } from '../utils';
15+
import { type CommandOperationOptions, ModernizedCommandOperation } from './command';
1416
import { executeOperation } from './execute_operation';
1517
import { CreateIndexesOperation } from './indexes';
1618
import { Aspect, defineAspects } from './operation';
@@ -110,7 +112,8 @@ const INVALID_QE_VERSION =
110112
'Driver support of Queryable Encryption is incompatible with server. Upgrade server to use Queryable Encryption.';
111113

112114
/** @internal */
113-
export class CreateCollectionOperation extends CommandOperation<Collection> {
115+
export class CreateCollectionOperation extends ModernizedCommandOperation<Collection> {
116+
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
114117
override options: CreateCollectionOptions;
115118
db: Db;
116119
name: string;
@@ -127,25 +130,19 @@ export class CreateCollectionOperation extends CommandOperation<Collection> {
127130
return 'create' as const;
128131
}
129132

130-
override async execute(
131-
server: Server,
132-
session: ClientSession | undefined,
133-
timeoutContext: TimeoutContext
134-
): Promise<Collection> {
135-
const db = this.db;
136-
const name = this.name;
137-
const options = this.options;
138-
139-
const cmd: Document = { create: name };
140-
for (const [option, value] of Object.entries(options)) {
141-
if (value != null && typeof value !== 'function' && !ILLEGAL_COMMAND_FIELDS.has(option)) {
142-
cmd[option] = value;
143-
}
144-
}
133+
override buildCommandDocument(_connection: Connection, _session?: ClientSession): Document {
134+
const isOptionValid = ([k, v]: [k: string, v: unknown]) =>
135+
v != null && typeof v !== 'function' && !ILLEGAL_COMMAND_FIELDS.has(k);
136+
return {
137+
create: this.name,
138+
...Object.fromEntries(Object.entries(this.options).filter(isOptionValid))
139+
};
140+
}
145141

146-
// otherwise just execute the command
147-
await super.executeCommand(server, session, cmd, timeoutContext);
148-
return new Collection(db, name, options);
142+
override handleOk(
143+
_response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
144+
): Collection<Document> {
145+
return new Collection(this.db, this.name, this.options);
149146
}
150147
}
151148

@@ -167,23 +164,17 @@ export async function createCollections<TSchema extends Document>(
167164

168165
if (encryptedFields) {
169166
class CreateSupportingFLEv2CollectionOperation extends CreateCollectionOperation {
170-
override execute(
171-
server: Server,
172-
session: ClientSession | undefined,
173-
timeoutContext: TimeoutContext
174-
): Promise<Collection> {
175-
// Creating a QE collection required min server of 7.0.0
176-
// TODO(NODE-5353): Get wire version information from connection.
167+
override buildCommandDocument(connection: Connection, session?: ClientSession): Document {
177168
if (
178-
!server.loadBalanced &&
179-
server.description.maxWireVersion < MIN_SUPPORTED_QE_WIRE_VERSION
169+
!connection.description.loadBalanced &&
170+
maxWireVersion(connection) < MIN_SUPPORTED_QE_WIRE_VERSION
180171
) {
181172
throw new MongoCompatibilityError(
182173
`${INVALID_QE_VERSION} The minimum server version required is ${MIN_SUPPORTED_QE_SERVER_VERSION}`
183174
);
184175
}
185176

186-
return super.execute(server, session, timeoutContext);
177+
return super.buildCommandDocument(connection, session);
187178
}
188179
}
189180

src/operations/drop.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { MongoServerError } from '..';
1+
import { type Connection, MongoServerError } from '..';
22
import type { Document } from '../bson';
3+
import { MongoDBResponse } from '../cmap/wire_protocol/responses';
34
import { CursorTimeoutContext } from '../cursor/abstract_cursor';
45
import type { Db } from '../db';
56
import { MONGODB_ERROR_CODES } from '../error';
6-
import type { Server } from '../sdam/server';
77
import type { ClientSession } from '../sessions';
88
import { TimeoutContext } from '../timeout';
9-
import { CommandOperation, type CommandOperationOptions } from './command';
9+
import { type CommandOperationOptions, ModernizedCommandOperation } from './command';
1010
import { executeOperation } from './execute_operation';
1111
import { Aspect, defineAspects } from './operation';
1212

@@ -17,7 +17,9 @@ export interface DropCollectionOptions extends CommandOperationOptions {
1717
}
1818

1919
/** @internal */
20-
export class DropCollectionOperation extends CommandOperation<boolean> {
20+
export class DropCollectionOperation extends ModernizedCommandOperation<boolean> {
21+
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
22+
2123
override options: DropCollectionOptions;
2224
name: string;
2325

@@ -31,12 +33,11 @@ export class DropCollectionOperation extends CommandOperation<boolean> {
3133
return 'drop' as const;
3234
}
3335

34-
override async execute(
35-
server: Server,
36-
session: ClientSession | undefined,
37-
timeoutContext: TimeoutContext
38-
): Promise<boolean> {
39-
await super.executeCommand(server, session, { drop: this.name }, timeoutContext);
36+
override buildCommandDocument(_connection: Connection, _session?: ClientSession): Document {
37+
return { drop: this.name };
38+
}
39+
40+
override handleOk(_response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>): boolean {
4041
return true;
4142
}
4243
}
@@ -106,7 +107,8 @@ export async function dropCollections(
106107
export type DropDatabaseOptions = CommandOperationOptions;
107108

108109
/** @internal */
109-
export class DropDatabaseOperation extends CommandOperation<boolean> {
110+
export class DropDatabaseOperation extends ModernizedCommandOperation<boolean> {
111+
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
110112
override options: DropDatabaseOptions;
111113

112114
constructor(db: Db, options: DropDatabaseOptions) {
@@ -117,12 +119,11 @@ export class DropDatabaseOperation extends CommandOperation<boolean> {
117119
return 'dropDatabase' as const;
118120
}
119121

120-
override async execute(
121-
server: Server,
122-
session: ClientSession | undefined,
123-
timeoutContext: TimeoutContext
124-
): Promise<boolean> {
125-
await super.executeCommand(server, session, { dropDatabase: 1 }, timeoutContext);
122+
override buildCommandDocument(_connection: Connection, _session?: ClientSession): Document {
123+
return { dropDatabase: 1 };
124+
}
125+
126+
override handleOk(_response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>): boolean {
126127
return true;
127128
}
128129
}

src/operations/list_databases.ts

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1+
import { type Connection } from '..';
12
import type { Document } from '../bson';
3+
import { MongoDBResponse } from '../cmap/wire_protocol/responses';
24
import type { Db } from '../db';
3-
import { type TODO_NODE_3286 } from '../mongo_types';
4-
import type { Server } from '../sdam/server';
55
import type { ClientSession } from '../sessions';
6-
import { type TimeoutContext } from '../timeout';
76
import { maxWireVersion, MongoDBNamespace } from '../utils';
8-
import { CommandOperation, type CommandOperationOptions } from './command';
7+
import { type CommandOperationOptions, ModernizedCommandOperation } from './command';
98
import { Aspect, defineAspects } from './operation';
109

1110
/** @public */
@@ -27,7 +26,8 @@ export interface ListDatabasesOptions extends CommandOperationOptions {
2726
}
2827

2928
/** @internal */
30-
export class ListDatabasesOperation extends CommandOperation<ListDatabasesResult> {
29+
export class ListDatabasesOperation extends ModernizedCommandOperation<ListDatabasesResult> {
30+
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
3131
override options: ListDatabasesOptions;
3232

3333
constructor(db: Db, options?: ListDatabasesOptions) {
@@ -40,11 +40,7 @@ export class ListDatabasesOperation extends CommandOperation<ListDatabasesResult
4040
return 'listDatabases' as const;
4141
}
4242

43-
override async execute(
44-
server: Server,
45-
session: ClientSession | undefined,
46-
timeoutContext: TimeoutContext
47-
): Promise<ListDatabasesResult> {
43+
override buildCommandDocument(connection: Connection, _session?: ClientSession): Document {
4844
const cmd: Document = { listDatabases: 1 };
4945

5046
if (typeof this.options.nameOnly === 'boolean') {
@@ -61,16 +57,11 @@ export class ListDatabasesOperation extends CommandOperation<ListDatabasesResult
6157

6258
// we check for undefined specifically here to allow falsy values
6359
// eslint-disable-next-line no-restricted-syntax
64-
if (maxWireVersion(server) >= 9 && this.options.comment !== undefined) {
60+
if (maxWireVersion(connection) >= 9 && this.options.comment !== undefined) {
6561
cmd.comment = this.options.comment;
6662
}
6763

68-
return await (super.executeCommand(
69-
server,
70-
session,
71-
cmd,
72-
timeoutContext
73-
) as Promise<TODO_NODE_3286>);
64+
return cmd;
7465
}
7566
}
7667

src/operations/rename.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import { type Connection } from '..';
12
import type { Document } from '../bson';
3+
import { MongoDBResponse } from '../cmap/wire_protocol/responses';
24
import { Collection } from '../collection';
3-
import type { Server } from '../sdam/server';
45
import type { ClientSession } from '../sessions';
5-
import { type TimeoutContext } from '../timeout';
66
import { MongoDBNamespace } from '../utils';
7-
import { CommandOperation, type CommandOperationOptions } from './command';
7+
import { type CommandOperationOptions, ModernizedCommandOperation } from './command';
88
import { Aspect, defineAspects } from './operation';
99

1010
/** @public */
@@ -16,7 +16,8 @@ export interface RenameOptions extends CommandOperationOptions {
1616
}
1717

1818
/** @internal */
19-
export class RenameOperation extends CommandOperation<Document> {
19+
export class RenameOperation extends ModernizedCommandOperation<Document> {
20+
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
2021
collection: Collection;
2122
newName: string;
2223
override options: RenameOptions;
@@ -33,24 +34,20 @@ export class RenameOperation extends CommandOperation<Document> {
3334
return 'renameCollection' as const;
3435
}
3536

36-
override async execute(
37-
server: Server,
38-
session: ClientSession | undefined,
39-
timeoutContext: TimeoutContext
40-
): Promise<Collection> {
41-
// Build the command
37+
override buildCommandDocument(_connection: Connection, _session?: ClientSession): Document {
4238
const renameCollection = this.collection.namespace;
43-
const toCollection = this.collection.s.namespace.withCollection(this.newName).toString();
39+
const to = this.collection.s.namespace.withCollection(this.newName).toString();
4440
const dropTarget =
4541
typeof this.options.dropTarget === 'boolean' ? this.options.dropTarget : false;
4642

47-
const command = {
48-
renameCollection: renameCollection,
49-
to: toCollection,
50-
dropTarget: dropTarget
43+
return {
44+
renameCollection,
45+
to,
46+
dropTarget
5147
};
48+
}
5249

53-
await super.executeCommand(server, session, command, timeoutContext);
50+
override handleOk(_response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>): Document {
5451
return new Collection(this.collection.s.db, this.newName, this.collection.s.options);
5552
}
5653
}

src/operations/validate_collection.ts

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import { type Connection } from '..';
12
import type { Admin } from '../admin';
2-
import type { Document } from '../bson';
3+
import { type Document } from '../bson';
4+
import { MongoDBResponse } from '../cmap/wire_protocol/responses';
35
import { MongoUnexpectedServerResponseError } from '../error';
4-
import type { Server } from '../sdam/server';
56
import type { ClientSession } from '../sessions';
6-
import { type TimeoutContext } from '../timeout';
7-
import { CommandOperation, type CommandOperationOptions } from './command';
7+
import { type CommandOperationOptions, ModernizedCommandOperation } from './command';
88

99
/** @public */
1010
export interface ValidateCollectionOptions extends CommandOperationOptions {
@@ -13,46 +13,38 @@ export interface ValidateCollectionOptions extends CommandOperationOptions {
1313
}
1414

1515
/** @internal */
16-
export class ValidateCollectionOperation extends CommandOperation<Document> {
16+
export class ValidateCollectionOperation extends ModernizedCommandOperation<Document> {
17+
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
1718
override options: ValidateCollectionOptions;
1819
collectionName: string;
19-
command: Document;
2020

2121
constructor(admin: Admin, collectionName: string, options: ValidateCollectionOptions) {
22-
// Decorate command with extra options
23-
const command: Document = { validate: collectionName };
24-
const keys = Object.keys(options);
25-
for (let i = 0; i < keys.length; i++) {
26-
if (Object.prototype.hasOwnProperty.call(options, keys[i]) && keys[i] !== 'session') {
27-
command[keys[i]] = (options as Document)[keys[i]];
28-
}
29-
}
30-
3122
super(admin.s.db, options);
3223
this.options = options;
33-
this.command = command;
3424
this.collectionName = collectionName;
3525
}
3626

3727
override get commandName() {
3828
return 'validate' as const;
3929
}
4030

41-
override async execute(
42-
server: Server,
43-
session: ClientSession | undefined,
44-
timeoutContext: TimeoutContext
45-
): Promise<Document> {
46-
const collectionName = this.collectionName;
31+
override buildCommandDocument(_connection: Connection, _session?: ClientSession): Document {
32+
// Decorate command with extra options
33+
return {
34+
validate: this.collectionName,
35+
...Object.fromEntries(Object.entries(this.options).filter(entry => entry[0] !== 'session'))
36+
};
37+
}
4738

48-
const doc = await super.executeCommand(server, session, this.command, timeoutContext);
49-
if (doc.result != null && typeof doc.result !== 'string')
39+
override handleOk(response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>): Document {
40+
const result = super.handleOk(response);
41+
if (result.result != null && typeof result.result !== 'string')
5042
throw new MongoUnexpectedServerResponseError('Error with validation data');
51-
if (doc.result != null && doc.result.match(/exception|corrupt/) != null)
52-
throw new MongoUnexpectedServerResponseError(`Invalid collection ${collectionName}`);
53-
if (doc.valid != null && !doc.valid)
54-
throw new MongoUnexpectedServerResponseError(`Invalid collection ${collectionName}`);
43+
if (result.result != null && result.result.match(/exception|corrupt/) != null)
44+
throw new MongoUnexpectedServerResponseError(`Invalid collection ${this.collectionName}`);
45+
if (result.valid != null && !result.valid)
46+
throw new MongoUnexpectedServerResponseError(`Invalid collection ${this.collectionName}`);
5547

56-
return doc;
48+
return response;
5749
}
5850
}

0 commit comments

Comments
 (0)