Skip to content

Commit 27900d1

Browse files
feat(shell-api): create compactStructuredEncryptionData helper MONGOSH-1207 (#1281)
1 parent 46e7252 commit 27900d1

File tree

4 files changed

+91
-2
lines changed

4 files changed

+91
-2
lines changed

packages/cli-repl/test/e2e-fle.spec.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,16 @@ describe('FLE tests', () => {
250250
expect(plainMongoResult).to.not.include("phoneNumber: '+12874627836445'");
251251
});
252252

253+
it('does not allow compactStructuredEncryptionData command when mongo instance configured without auto encryption', async() => {
254+
const shell = TestShell.start({
255+
args: [await testServer.connectionString()]
256+
});
257+
await shell.waitForPrompt();
258+
259+
const compactResult = await shell.executeLine('db.test.compactStructuredEncryptionData()');
260+
expect(compactResult).to.include('The "compactStructuredEncryptionData" command requires Mongo instance configured with auto encryption.');
261+
});
262+
253263
context('6.0+', () => {
254264
skipIfServerVersion(testServer, '< 6.0'); // FLE2 only available on 6.0+
255265

@@ -313,6 +323,45 @@ describe('FLE tests', () => {
313323
expect(collections).to.not.include('enxcol_.collfle2.ecoc');
314324
expect(collections).to.not.include('collfle2');
315325
});
326+
327+
it('allows compactStructuredEncryptionData command when mongo instance configured with auto encryption', async() => {
328+
const shell = TestShell.start({
329+
args: ['--nodb', `--csfleLibraryPath=${csfleLibrary}`]
330+
});
331+
const uri = JSON.stringify(await testServer.connectionString());
332+
333+
await shell.waitForPrompt();
334+
335+
await shell.executeLine('local = { key: BinData(0, "kh4Gv2N8qopZQMQYMEtww/AkPsIrXNmEMxTrs3tUoTQZbZu4msdRUaR8U5fXD7A7QXYHcEvuu4WctJLoT+NvvV3eeIg3MD+K8H9SR794m/safgRHdIfy6PD+rFpvmFbY") }');
336+
337+
await shell.executeLine(`keyMongo = Mongo(${uri}, { \
338+
keyVaultNamespace: '${dbname}.keyVault', \
339+
kmsProviders: { local } \
340+
});`);
341+
342+
await shell.executeLine('keyVault = keyMongo.getKeyVault();');
343+
await shell.executeLine('keyId = keyVault.createKey("local");');
344+
345+
await shell.executeLine(`encryptedFieldsMap = { \
346+
'${dbname}.test': { \
347+
fields: [{ path: 'phoneNumber', keyId, bsonType: 'string' }] \
348+
} \
349+
};`);
350+
351+
await shell.executeLine(`autoMongo = Mongo(${uri}, { \
352+
keyVaultNamespace: '${dbname}.keyVault', \
353+
kmsProviders: { local }, \
354+
encryptedFieldsMap \
355+
});`);
356+
357+
await shell.executeLine(`autoMongo.getDB('${dbname}').createCollection('test', { encryptedFields: { fields: [] } });`);
358+
await shell.executeLine(`autoMongo.getDB('${dbname}').test.insertOne({ \
359+
phoneNumber: '+12874627836445' \
360+
});`);
361+
362+
const compactResult = await shell.executeLine(`autoMongo.getDB('${dbname}').test.compactStructuredEncryptionData()`);
363+
expect(compactResult).to.include('ok: 1');
364+
});
316365
});
317366

318367
it('performs KeyVault data key management as expected', async() => {

packages/i18n/src/locales/en_US.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,11 @@ const translations: Catalog = {
528528
description: 'Updates all documents that match the specified filter for a collection.',
529529
example: 'db.collection.updateMany(filter, update, options)'
530530
},
531+
compactStructuredEncryptionData: {
532+
link: '',
533+
description: 'Compacts structured encryption data',
534+
example: 'db.collection.compactStructuredEncryptionData()'
535+
},
531536
convertToCapped: {
532537
link: '',
533538
description: "calls {convertToCapped:'coll', size:maxBytes}} command",

packages/shell-api/src/collection.spec.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,6 +1939,18 @@ describe('Collection', () => {
19391939
);
19401940
});
19411941
});
1942+
1943+
describe('compactStructuredEncryptionData', () => {
1944+
it('calls service provider runCommandWithCheck', async() => {
1945+
const result = await collection.compactStructuredEncryptionData();
1946+
1947+
expect(serviceProvider.runCommandWithCheck).to.have.been.calledWith(
1948+
'db1',
1949+
{ compactStructuredEncryptionData: 'collfle2' }
1950+
);
1951+
expect(result).to.be.deep.equal({ ok: 1 });
1952+
});
1953+
});
19421954
});
19431955
describe('with session', () => {
19441956
let serviceProvider: StubbedInstance<ServiceProvider>;
@@ -1965,6 +1977,7 @@ describe('Collection', () => {
19651977
getIndexKeys: { m: 'getIndexes', i: 2 },
19661978
dropIndex: { m: 'runCommandWithCheck', i: 2 },
19671979
dropIndexes: { m: 'runCommandWithCheck', i: 2 },
1980+
compactStructuredEncryptionData: { m: 'runCommandWithCheck' },
19681981
convertToCapped: { m: 'runCommandWithCheck', i: 2 },
19691982
dataSize: { m: 'aggregate', e: true },
19701983
storageSize: { m: 'aggregate', e: true },
@@ -1995,7 +2008,7 @@ describe('Collection', () => {
19952008
watch: { i: 1 }
19962009
};
19972010
const ignore: (keyof (typeof Collection)['prototype'])[] = [
1998-
'getShardDistribution', 'stats', 'isCapped'
2011+
'getShardDistribution', 'stats', 'isCapped', 'compactStructuredEncryptionData'
19992012
];
20002013
const args = [ { query: {} }, {}, { out: 'coll' } ];
20012014
beforeEach(() => {
@@ -2047,7 +2060,9 @@ describe('Collection', () => {
20472060
}
20482061
});
20492062
context('all commands that use other methods', () => {
2050-
for (const method of Object.keys(exceptions)) {
2063+
for (const method of Object.keys(exceptions).filter(
2064+
k => !ignore.includes(k as any)
2065+
)) {
20512066
const customA = exceptions[method].a || args;
20522067
const customM = exceptions[method].m || method;
20532068
const customI = exceptions[method].i || 3;

packages/shell-api/src/collection.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,25 @@ export default class Collection extends ShellApiWithMongoClass {
963963
);
964964
}
965965

966+
/**
967+
* Compacts structured encryption data.
968+
*
969+
* @return {Promise}
970+
*/
971+
@returnsPromise
972+
@apiVersions([])
973+
async compactStructuredEncryptionData(): Promise<Document> {
974+
if (!this._mongo._fleOptions) {
975+
throw new MongoshInvalidInputError(
976+
'The "compactStructuredEncryptionData" command requires Mongo instance configured with auto encryption.',
977+
CommonErrors.InvalidArgument
978+
);
979+
}
980+
981+
this._emitCollectionApiCall('compactStructuredEncryptionData');
982+
return await this._database._runCommand({ compactStructuredEncryptionData: this._name });
983+
}
984+
966985
/**
967986
* Converts a collection to capped
968987
*
@@ -981,6 +1000,7 @@ export default class Collection extends ShellApiWithMongoClass {
9811000
}
9821001
);
9831002
}
1003+
9841004
/**
9851005
* Internal function which calls the Service Provider createIndexes function.
9861006
* This function is used also by createIndex and ensureIndex

0 commit comments

Comments
 (0)