@@ -12,6 +12,8 @@ const pkg = require('../../../package.json');
1212const processConnectionOptions = require ( '../../helpers/processConnectionOptions' ) ;
1313const setTimeout = require ( '../../helpers/timers' ) . setTimeout ;
1414const utils = require ( '../../utils' ) ;
15+ const { inferBSONType } = require ( '../../encryption_utils' ) ;
16+ const clone = require ( '../../helpers/clone' ) ;
1517
1618/**
1719 * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) connection implementation.
@@ -304,6 +306,9 @@ NativeConnection.prototype.createClient = async function createClient(uri, optio
304306 } ;
305307 }
306308
309+
310+ options = this . _buildEncryptionSchemas ( options ) ;
311+
307312 this . readyState = STATES . connecting ;
308313 this . _connectionString = uri ;
309314
@@ -327,6 +332,86 @@ NativeConnection.prototype.createClient = async function createClient(uri, optio
327332 return this ;
328333} ;
329334
335+ /**
336+ * Given a connection, which may or may not have encrypted models, build
337+ * a schemaMap and/or an encryptedFieldsMap for the connection, combining all models
338+ * into a single schemaMap and encryptedFields map.
339+ *
340+ * @param { object } options
341+ * @param { object } [options.autoEncryption] the auto encryption options for the connection. Technically
342+ * marked optional, because it is optional in the MongoClient options interface, but auto encryption
343+ * will fail without it so in practice it is always required if encryption is enabled.
344+ *
345+ * @returns a copy of the options object with a schemaMap and/or an encryptedFieldsMap added to the options' autoEncryption
346+ * options.
347+ */
348+ NativeConnection . prototype . _buildEncryptionSchemas = function ( options ) {
349+ const schemaMap = Object . values ( this . models ) . filter ( model => model . schema . encryptionType ( ) === 'csfle' ) . reduce (
350+ schemaMapReducer . bind ( this ) ,
351+ { }
352+ ) ;
353+ const encryptedFieldsMap = Object . values ( this . models ) . filter ( model => model . schema . encryptionType ( ) === 'qe' ) . reduce (
354+ encryptedFieldsMapReducer . bind ( this ) ,
355+ { }
356+ ) ;
357+
358+ return Object . assign (
359+ clone ( options ) , {
360+ autoEncryption : {
361+ ...options . autoEncryption ,
362+ schemaMap,
363+ encryptedFieldsMap
364+ }
365+ } ) ;
366+
367+ /**
368+ * @param {object } schemaMap the accumulation schemaMap
369+ * @param {Model } the model
370+ * @returns
371+ */
372+ function schemaMapReducer ( schemaMap , model ) {
373+ const { schema, collection : { collectionName } } = model ;
374+ const namespace = `${ this . $dbName } .${ collectionName } ` ;
375+
376+ function schemaMapPropertyReducer ( accum , [ path , propertyConfig ] ) {
377+ const bsonType = inferBSONType ( schema , path ) ;
378+ // `<db>.<collection>`: { encrypt: { keyId, bsonType, algorithm }}
379+ accum [ path ] = { encrypt : { ...propertyConfig , bsonType } } ;
380+ return accum ;
381+ }
382+ const properties = Object . entries ( schema . encryptedFields ) . reduce (
383+ schemaMapPropertyReducer ,
384+ { } ) ;
385+
386+ schemaMap [ namespace ] = {
387+ bsonType : 'object' ,
388+ properties
389+ } ;
390+
391+ return schemaMap ;
392+ }
393+
394+ /**
395+ *
396+ * @param {object } encryptedFieldsMap the accumulation encryptedFieldsMap
397+ * @param {Model } the model
398+ * @returns
399+ */
400+ function encryptedFieldsMapReducer ( encryptedFieldsMap , { schema, collection : { collectionName } } ) {
401+ const namespace = `${ this . $dbName } .${ collectionName } ` ;
402+ const fields = Object . entries ( schema . encryptedFields ) . map (
403+ ( [ path , config ] ) => {
404+ const bsonType = inferBSONType ( schema , path ) ;
405+ // { path, bsonType, keyId, queries? }
406+ return { path, bsonType, ...config } ;
407+ } ) ;
408+
409+ encryptedFieldsMap [ namespace ] = { fields } ;
410+
411+ return encryptedFieldsMap ;
412+ }
413+ } ;
414+
330415/*!
331416 * ignore
332417 */
@@ -347,7 +432,7 @@ NativeConnection.prototype.setClient = function setClient(client) {
347432
348433 for ( const model of Object . values ( this . models ) ) {
349434 // Errors handled internally, so safe to ignore error
350- model . init ( ) . catch ( function $modelInitNoop ( ) { } ) ;
435+ model . init ( ) . catch ( function $modelInitNoop ( ) { } ) ;
351436 }
352437
353438 return this ;
@@ -390,9 +475,9 @@ function _setClient(conn, client, options, dbName) {
390475 } ;
391476
392477 const type = client &&
393- client . topology &&
394- client . topology . description &&
395- client . topology . description . type || '' ;
478+ client . topology &&
479+ client . topology . description &&
480+ client . topology . description . type || '' ;
396481
397482 if ( type === 'Single' ) {
398483 client . on ( 'serverDescriptionChanged' , ev => {
0 commit comments