Skip to content

Commit af4d12f

Browse files
committed
feat(NODE-5513)!: remove callbacks from ClientEncryption encrypt, decrypt, and createDataKey
1 parent 844aa52 commit af4d12f

File tree

7 files changed

+98
-221
lines changed

7 files changed

+98
-221
lines changed

Diff for: src/bson.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ export {
1919
MinKey,
2020
ObjectId,
2121
serialize,
22-
Timestamp
22+
Timestamp,
23+
UUID
2324
} from 'bson';
2425

2526
/**

Diff for: src/client-side-encryption/client_encryption.ts

+22-94
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type {
55
MongoCryptOptions
66
} from 'mongodb-client-encryption';
77

8-
import { type Binary, type Document, type Long, serialize } from '../bson';
8+
import { type Binary, type Document, type Long, serialize, type UUID } from '../bson';
99
import { type AnyBulkWriteOperation, type BulkWriteResult } from '../bulk/common';
1010
import { type ProxyOptions } from '../cmap/connection';
1111
import { type Collection } from '../collection';
@@ -16,8 +16,7 @@ import { type MongoClient } from '../mongo_client';
1616
import { type Filter } from '../mongo_types';
1717
import { type CreateCollectionOptions } from '../operations/create_collection';
1818
import { type DeleteResult } from '../operations/delete';
19-
import { type Callback, MongoDBCollectionNamespace } from '../utils';
20-
import { maybeCallback, promiseOrCallback } from './common';
19+
import { MongoDBCollectionNamespace } from '../utils';
2120
import * as cryptoCallbacks from './crypto_callbacks';
2221
import {
2322
MongoCryptCreateDataKeyError,
@@ -35,7 +34,7 @@ import {
3534
* The schema for a DataKey in the key vault collection.
3635
*/
3736
export interface DataKey {
38-
_id: Binary;
37+
_id: UUID;
3938
version?: number;
4039
keyAltNames?: string[];
4140
keyMaterial: Binary;
@@ -125,18 +124,6 @@ export class ClientEncryption implements StateMachineExecutable {
125124
*
126125
* @example
127126
* ```ts
128-
* // Using callbacks to create a local key
129-
* clientEncryption.createDataKey('local', (err, dataKey) => {
130-
* if (err) {
131-
* // This means creating the key failed.
132-
* } else {
133-
* // key creation succeeded
134-
* }
135-
* });
136-
* ```
137-
*
138-
* @example
139-
* ```ts
140127
* // Using async/await to create a local key
141128
* const dataKeyId = await clientEncryption.createDataKey('local');
142129
* ```
@@ -164,19 +151,10 @@ export class ClientEncryption implements StateMachineExecutable {
164151
* });
165152
* ```
166153
*/
167-
createDataKey(
154+
async createDataKey(
168155
provider: KMSProvider,
169-
options?: ClientEncryptionCreateDataKeyProviderOptions,
170-
callback?: Callback<DataKey>
171-
) {
172-
if (typeof options === 'function') {
173-
callback = options;
174-
options = {};
175-
}
176-
if (options == null) {
177-
options = {};
178-
}
179-
156+
options: ClientEncryptionCreateDataKeyProviderOptions = {}
157+
): Promise<UUID> {
180158
const dataKey = Object.assign({ provider }, options.masterKey);
181159

182160
if (options.keyAltNames && !Array.isArray(options.keyAltNames)) {
@@ -213,32 +191,17 @@ export class ClientEncryption implements StateMachineExecutable {
213191
tlsOptions: this._tlsOptions
214192
});
215193

216-
// @ts-expect-error We did not convert promiseOrCallback to TS
217-
return promiseOrCallback(callback, cb => {
218-
stateMachine.execute<DataKey>(this, context, (err, dataKey) => {
219-
if (err || !dataKey) {
220-
cb(err, null);
221-
return;
222-
}
194+
const updatedDataKey = await stateMachine.executeAsync<DataKey>(this, context);
223195

224-
const { db: dbName, collection: collectionName } = MongoDBCollectionNamespace.fromString(
225-
this._keyVaultNamespace
226-
);
196+
const { db: dbName, collection: collectionName } = MongoDBCollectionNamespace.fromString(
197+
this._keyVaultNamespace
198+
);
227199

228-
this._keyVaultClient
229-
.db(dbName)
230-
.collection<DataKey>(collectionName)
231-
.insertOne(dataKey, { writeConcern: { w: 'majority' } })
232-
.then(
233-
result => {
234-
return cb(null, result.insertedId);
235-
},
236-
err => {
237-
cb(err, null);
238-
}
239-
);
240-
});
241-
});
200+
const { insertedId } = await this._keyVaultClient
201+
.db(dbName)
202+
.collection<DataKey>(collectionName)
203+
.insertOne(updatedDataKey, { writeConcern: { w: 'majority' } });
204+
return insertedId;
242205
}
243206

244207
/**
@@ -590,21 +553,7 @@ export class ClientEncryption implements StateMachineExecutable {
590553
*
591554
* @param value - The value that you wish to serialize. Must be of a type that can be serialized into BSON
592555
* @param options -
593-
* @param callback - Optional callback to invoke when value is encrypted
594-
* @returns If no callback is provided, returns a Promise that either resolves with the encrypted value, or rejects with an error. If a callback is provided, returns nothing.
595-
*
596-
* @example
597-
* ```ts
598-
* // Encryption with callback API
599-
* function encryptMyData(value, callback) {
600-
* clientEncryption.createDataKey('local', (err, keyId) => {
601-
* if (err) {
602-
* return callback(err);
603-
* }
604-
* clientEncryption.encrypt(value, { keyId, algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' }, callback);
605-
* });
606-
* }
607-
* ```
556+
* @returns a Promise that either resolves with the encrypted value, or rejects with an error.
608557
*
609558
* @example
610559
* ```ts
@@ -624,12 +573,8 @@ export class ClientEncryption implements StateMachineExecutable {
624573
* }
625574
* ```
626575
*/
627-
encrypt(
628-
value: unknown,
629-
options: ClientEncryptionEncryptOptions,
630-
callback: Callback<Binary>
631-
): Promise<Binary> | void {
632-
return maybeCallback(() => this._encrypt(value, false, options), callback);
576+
async encrypt(value: unknown, options: ClientEncryptionEncryptOptions): Promise<Binary> {
577+
return this._encrypt(value, false, options);
633578
}
634579

635580
/**
@@ -661,16 +606,7 @@ export class ClientEncryption implements StateMachineExecutable {
661606
* Explicitly decrypt a provided encrypted value
662607
*
663608
* @param value - An encrypted value
664-
* @param callback - Optional callback to invoke when value is decrypted
665-
* @returns If no callback is provided, returns a Promise that either resolves with the decrypted value, or rejects with an error. If a callback is provided, returns nothing.
666-
*
667-
* ```ts
668-
* @example
669-
* // Decrypting value with callback API
670-
* function decryptMyValue(value, callback) {
671-
* clientEncryption.decrypt(value, callback);
672-
* }
673-
* ```
609+
* @returns a Promise that either resolves with the decrypted value, or rejects with an error
674610
*
675611
* @example
676612
* ```ts
@@ -680,7 +616,7 @@ export class ClientEncryption implements StateMachineExecutable {
680616
* }
681617
* ```
682618
*/
683-
decrypt<T = any>(value: Binary, callback?: Callback<T>): Promise<T> | void {
619+
async decrypt<T = any>(value: Binary): Promise<T> {
684620
const valueBuffer = serialize({ v: value });
685621
const context = this._mongoCrypt.makeExplicitDecryptionContext(valueBuffer);
686622

@@ -689,17 +625,9 @@ export class ClientEncryption implements StateMachineExecutable {
689625
tlsOptions: this._tlsOptions
690626
});
691627

692-
// @ts-expect-error We did not convert promiseOrCallback to TS
693-
return promiseOrCallback(callback, cb => {
694-
stateMachine.execute<{ v: T }>(this, context, (err, result) => {
695-
if (err || !result) {
696-
cb(err, null);
697-
return;
698-
}
628+
const { v } = await stateMachine.executeAsync<{ v: T }>(this, context);
699629

700-
cb(null, result.v);
701-
});
702-
});
630+
return v;
703631
}
704632

705633
/**

Diff for: src/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export {
3333
MaxKey,
3434
MinKey,
3535
ObjectId,
36-
Timestamp
36+
Timestamp,
37+
UUID
3738
} from './bson';
3839
export { AnyBulkWriteOperation, BulkWriteOptions, MongoBulkWriteError } from './bulk/common';
3940
export { ChangeStreamCursor } from './cursor/change_stream_cursor';

Diff for: test/integration/client-side-encryption/client_side_encryption.prose.12.deadlock.test.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import * as BSON from 'bson';
22
import { expect } from 'chai';
33
import { readFileSync } from 'fs';
44
import * as path from 'path';
5-
import * as util from 'util';
65

76
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
87
import { ClientEncryption } from '../../../src/client-side-encryption/client_encryption';
@@ -138,11 +137,8 @@ describe('Connection Pool Deadlock Prevention', function () {
138137
keyVaultClient: this.keyVaultClient,
139138
extraOptions: getEncryptExtraOptions()
140139
});
141-
this.clientEncryption.encryptPromisified = util.promisify(
142-
this.clientEncryption.encrypt.bind(this.clientEncryption)
143-
);
144140

145-
this.ciphertext = await this.clientEncryption.encryptPromisified('string0', {
141+
this.ciphertext = await this.clientEncryption.encrypt('string0', {
146142
algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic',
147143
keyAltName: 'local'
148144
});

Diff for: test/integration/node-specific/client_encryption.test.ts

+13-20
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ describe('ClientEncryption integration tests', function () {
157157
}
158158
);
159159

160-
it('should fail to create a data key if keyMaterial is wrong', metadata, function (done) {
160+
it('should fail to create a data key if keyMaterial is wrong', metadata, async function () {
161161
const encryption = new ClientEncryption(client, {
162162
keyVaultNamespace: 'client.encryption',
163163
kmsProviders: { local: { key: 'A'.repeat(128) } }
@@ -166,13 +166,8 @@ describe('ClientEncryption integration tests', function () {
166166
const dataKeyOptions = {
167167
keyMaterial: new Binary(Buffer.alloc(97))
168168
};
169-
try {
170-
encryption.createDataKey('local', dataKeyOptions);
171-
expect.fail('missed exception');
172-
} catch (err) {
173-
expect(err.message).to.equal('keyMaterial should have length 96, but has length 97');
174-
done();
175-
}
169+
const error = await encryption.createDataKey('local', dataKeyOptions).catch(error => error);
170+
expect(error.message).to.equal('keyMaterial should have length 96, but has length 97');
176171
});
177172

178173
it(
@@ -536,23 +531,21 @@ describe('ClientEncryption integration tests', function () {
536531
}
537532

538533
describe('errors', function () {
539-
[42, 'hello', { keyAltNames: 'foobar' }, /foobar/].forEach(val => {
540-
it(`should fail if typeof keyAltNames = ${typeof val}`, metadata, function () {
534+
for (const val of [42, 'hello', { keyAltNames: 'foobar' }, /foobar/]) {
535+
it(`should fail if typeof keyAltNames = ${typeof val}`, metadata, async function () {
541536
const options = makeOptions(val);
542-
expect(() => clientEncryption.createDataKey('aws', options, () => undefined)).to.throw(
543-
MongoCryptInvalidArgumentError
544-
);
537+
const error = await clientEncryption.createDataKey('aws', options).catch(error => error);
538+
expect(error).to.be.instanceOf(MongoCryptInvalidArgumentError);
545539
});
546-
});
540+
}
547541

548-
[undefined, null, 42, { keyAltNames: 'foobar' }, ['foobar'], /foobar/].forEach(val => {
549-
it(`should fail if typeof keyAltNames[x] = ${typeof val}`, metadata, function () {
542+
for (const val of [undefined, null, 42, { keyAltNames: 'foobar' }, ['foobar'], /foobar/]) {
543+
it(`should fail if typeof keyAltNames[x] = ${typeof val}`, metadata, async function () {
550544
const options = makeOptions([val]);
551-
expect(() => clientEncryption.createDataKey('aws', options, () => undefined)).to.throw(
552-
MongoCryptInvalidArgumentError
553-
);
545+
const error = await clientEncryption.createDataKey('aws', options).catch(error => error);
546+
expect(error).to.be.instanceOf(MongoCryptInvalidArgumentError);
554547
});
555-
});
548+
}
556549
});
557550

558551
it('should create a key with keyAltNames', metadata, async function () {

0 commit comments

Comments
 (0)