Skip to content

Commit 8622545

Browse files
fix(NODE-6284): make sparsity and trimFactor optional (#4189)
1 parent 1ce2f0d commit 8622545

File tree

84 files changed

+1312
-354
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+1312
-354
lines changed

.evergreen/install-mongodb-client-encryption.sh

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#! /usr/bin/env bash
2-
set +o xtrace # Do not write AWS credentials to stderr
2+
set +o xtrace
33

44
# Initial checks for running these tests
55
if [ -z ${PROJECT_DIRECTORY+omitted} ]; then echo "PROJECT_DIRECTORY is unset" && exit 1; fi
@@ -9,20 +9,19 @@ source "${PROJECT_DIRECTORY}/.evergreen/init-node-and-npm-env.sh"
99
set -o xtrace # Write all commands first to stderr
1010
set -o errexit # Exit the script with error if any of the commands fail
1111

12-
rm -rf $INSTALL_DIR
12+
rm -rf mongodb-client-encryption
1313
git clone https://github.com/mongodb-js/mongodb-client-encryption.git
1414
pushd mongodb-client-encryption
1515

1616
if [ -n "${LIBMONGOCRYPT_VERSION}" ]; then
1717
# nightly tests test with `latest` to test against the laster FLE build.
18-
npm run install:libmongocrypt -- --libVersion $LIBMONGOCRYPT_VERSION
18+
npm run install:libmongocrypt -- --build --libVersion $LIBMONGOCRYPT_VERSION
1919
else
2020
# otherwise use whatever is specified in the package.json.
2121
npm run install:libmongocrypt
2222
fi
2323

2424
echo "finished installing libmongocrypt"
25-
BINDINGS_DIR=$(pwd)
2625

2726
popd
2827

package-lock.json

+4-21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
"js-yaml": "^4.1.0",
9999
"mocha": "^10.4.0",
100100
"mocha-sinon": "^2.1.2",
101-
"mongodb-client-encryption": "^6.1.0-alpha.0",
101+
"mongodb-client-encryption": "^6.1.0",
102102
"mongodb-legacy": "^6.0.1",
103103
"nyc": "^15.1.0",
104104
"prettier": "^2.8.8",

src/client-side-encryption/client_encryption.ts

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

8-
import { type Binary, deserialize, type Document, type Long, serialize, type UUID } from '../bson';
8+
import {
9+
type Binary,
10+
deserialize,
11+
type Document,
12+
type Int32,
13+
type Long,
14+
serialize,
15+
type UUID
16+
} from '../bson';
917
import { type AnyBulkWriteOperation, type BulkWriteResult } from '../bulk/common';
1018
import { type ProxyOptions } from '../cmap/connection';
1119
import { type Collection } from '../collection';
@@ -956,13 +964,19 @@ export interface ClientEncryptionRewrapManyDataKeyResult {
956964
/**
957965
* @public
958966
* RangeOptions specifies index options for a Queryable Encryption field supporting "rangePreview" queries.
959-
* min, max, sparsity, and range must match the values set in the encryptedFields of the destination collection.
967+
* min, max, sparsity, trimFactor and range must match the values set in the encryptedFields of the destination collection.
960968
* For double and decimal128, min/max/precision must all be set, or all be unset.
961969
*/
962970
export interface RangeOptions {
971+
/** min is the minimum value for the encrypted index. Required if precision is set. */
963972
min?: any;
973+
/** max is the minimum value for the encrypted index. Required if precision is set. */
964974
max?: any;
965-
sparsity: Long;
975+
/** sparsity may be used to tune performance. must be non-negative. When omitted, a default value is used. */
976+
sparsity?: Long | bigint;
977+
/** trimFactor may be used to tune performance. must be non-negative. When omitted, a default value is used. */
978+
trimFactor?: Int32 | number;
979+
/* precision determines the number of significant digits after the decimal point. May only be set for double or decimal128. */
966980
precision?: number;
967981
}
968982

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { expect } from 'chai';
2+
3+
/* eslint-disable @typescript-eslint/no-restricted-imports */
4+
import { ClientEncryption } from '../../../src/client-side-encryption/client_encryption';
5+
import { type Binary, EJSON, Int32, Long } from '../../mongodb';
6+
import { installNodeDNSWorkaroundHooks } from '../../tools/runner/hooks/configuration';
7+
8+
const metaData: MongoDBMetadataUI = {
9+
requires: {
10+
clientSideEncryption: '>=6.1.0',
11+
12+
// The Range Explicit Encryption tests require MongoDB server 7.0+ for QE v2.
13+
// The tests must not run against a standalone.
14+
//
15+
// `range` is not supported on 8.0+ servers.
16+
mongodb: '>=8.0.0',
17+
topology: '!single'
18+
}
19+
};
20+
21+
const getKmsProviders = (): { local: { key: string } } => {
22+
const result = EJSON.parse(process.env.CSFLE_KMS_PROVIDERS || '{}') as unknown as {
23+
local: { key: string };
24+
};
25+
26+
return { local: result.local };
27+
};
28+
29+
describe('Range Explicit Encryption Defaults', function () {
30+
installNodeDNSWorkaroundHooks();
31+
32+
let clientEncryption: ClientEncryption;
33+
let keyId;
34+
let keyVaultClient;
35+
let payload_defaults: Binary;
36+
37+
beforeEach(async function () {
38+
// Create a MongoClient named `keyVaultClient`.
39+
keyVaultClient = this.configuration.newClient();
40+
41+
// Create a ClientEncryption object named `clientEncryption` with these options:
42+
// ```typescript
43+
// class ClientEncryptionOpts {
44+
// keyVaultClient: keyVaultClient,
45+
// keyVaultNamespace: "keyvault.datakeys",
46+
// kmsProviders: { "local": { "key": "<base64 decoding of LOCAL_MASTERKEY>" } },
47+
// }
48+
// ```
49+
clientEncryption = new ClientEncryption(keyVaultClient, {
50+
keyVaultNamespace: 'keyvault.datakeys',
51+
kmsProviders: getKmsProviders()
52+
});
53+
54+
// Create a key with `clientEncryption.createDataKey`. Store the returned key ID in a variable named `keyId`.
55+
keyId = await clientEncryption.createDataKey('local');
56+
57+
// Call `clientEncryption.encrypt` to encrypt the int32 value `123` with these options:
58+
// ```typescript
59+
// class EncryptOpts {
60+
// keyId : keyId,
61+
// algorithm: "Range",
62+
// contentionFactor: 0,
63+
// rangeOpts: RangeOpts {
64+
// min: 0,
65+
// max: 1000
66+
// }
67+
// }
68+
// ```
69+
// Store the result in a variable named `payload_defaults`.
70+
payload_defaults = await clientEncryption.encrypt(new Int32(123), {
71+
keyId,
72+
algorithm: 'Range',
73+
contentionFactor: 0,
74+
rangeOptions: {
75+
min: 0,
76+
max: 1000
77+
}
78+
});
79+
});
80+
81+
afterEach(async function () {
82+
await keyVaultClient.close();
83+
});
84+
85+
it('Case 1: Uses libmongocrypt defaults', metaData, async function () {
86+
// Call `clientEncryption.encrypt` to encrypt the int32 value `123` with these options:
87+
// ```typescript
88+
// class EncryptOpts {
89+
// keyId : keyId,
90+
// algorithm: "Range",
91+
// contentionFactor: 0,
92+
// rangeOpts: RangeOpts {
93+
// min: 0,
94+
// max: 1000,
95+
// sparsity: 2,
96+
// trimFactor: 6
97+
// }
98+
// }
99+
// ```
100+
const encrypted = await clientEncryption.encrypt(new Int32(123), {
101+
keyId: keyId,
102+
algorithm: 'Range',
103+
contentionFactor: 0,
104+
rangeOptions: {
105+
min: 0,
106+
max: 1000,
107+
sparsity: new Long(2),
108+
trimFactor: new Int32(6)
109+
}
110+
});
111+
112+
// Assert the returned payload size equals the size of `payload_defaults`.
113+
expect(encrypted.length()).to.equal(payload_defaults.length());
114+
});
115+
116+
it('Case 2: can find encrypted range and return the maximum', metaData, async function () {
117+
// Call `clientEncryption.encrypt` to encrypt the int32 value `123` with these options:
118+
// ```typescript
119+
// class EncryptOpts {
120+
// keyId : keyId,
121+
// algorithm: "Range",
122+
// contentionFactor: 0,
123+
// rangeOpts: RangeOpts {
124+
// min: 0,
125+
// max: 1000,
126+
// trimFactor: 0
127+
// }
128+
// }
129+
// ```
130+
const encrypted = await clientEncryption.encrypt(new Int32(123), {
131+
keyId: keyId,
132+
algorithm: 'Range',
133+
contentionFactor: 0,
134+
rangeOptions: {
135+
min: 0,
136+
max: 1000,
137+
trimFactor: new Int32(0)
138+
}
139+
});
140+
141+
// Assert the returned payload size is greater than the size of `payload_defaults`.
142+
expect(encrypted.length()).to.be.greaterThan(payload_defaults.length());
143+
});
144+
});

test/integration/client-side-encryption/driver.test.ts

+71
Original file line numberDiff line numberDiff line change
@@ -405,3 +405,74 @@ describe('Client Side Encryption Functional', function () {
405405
}
406406
);
407407
});
408+
409+
describe('Range Explicit Encryption with JS native types', function () {
410+
installNodeDNSWorkaroundHooks();
411+
412+
const metaData: MongoDBMetadataUI = {
413+
requires: {
414+
clientSideEncryption: '>=6.1.0',
415+
416+
// The Range Explicit Encryption tests require MongoDB server 7.0+ for QE v2.
417+
// The tests must not run against a standalone.
418+
//
419+
// `range` is not supported on 8.0+ servers.
420+
mongodb: '>=8.0.0',
421+
topology: '!single'
422+
}
423+
};
424+
425+
const getKmsProviders = (): { local: { key: string } } => {
426+
const result = EJSON.parse(process.env.CSFLE_KMS_PROVIDERS || '{}') as unknown as {
427+
local: { key: string };
428+
};
429+
430+
return { local: result.local };
431+
};
432+
433+
let clientEncryption: ClientEncryption;
434+
let keyId;
435+
let keyVaultClient;
436+
437+
beforeEach(async function () {
438+
keyVaultClient = this.configuration.newClient();
439+
clientEncryption = new ClientEncryption(keyVaultClient, {
440+
keyVaultNamespace: 'keyvault.datakeys',
441+
kmsProviders: getKmsProviders()
442+
});
443+
444+
keyId = await clientEncryption.createDataKey('local');
445+
});
446+
447+
afterEach(async function () {
448+
await keyVaultClient.close();
449+
});
450+
451+
it('supports a js number for trimFactor', metaData, async function () {
452+
await clientEncryption.encrypt(new BSON.Int32(123), {
453+
keyId,
454+
algorithm: 'Range',
455+
contentionFactor: 0,
456+
rangeOptions: {
457+
min: 0,
458+
max: 1000,
459+
trimFactor: 1,
460+
sparsity: new BSON.Long(1)
461+
}
462+
});
463+
});
464+
465+
it('supports a bigint for sparsity', metaData, async function () {
466+
await clientEncryption.encrypt(new BSON.Int32(123), {
467+
keyId,
468+
algorithm: 'Range',
469+
contentionFactor: 0,
470+
rangeOptions: {
471+
min: 0,
472+
max: 1000,
473+
trimFactor: new BSON.Int32(1),
474+
sparsity: 1n
475+
}
476+
});
477+
});
478+
});

0 commit comments

Comments
 (0)