@@ -17,6 +17,60 @@ const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
1717  methods : true 
1818} ; 
1919
20+ /** 
21+  * Validate fields declared on the child schema when either schema is configured for encryption.  Specifically, this function ensures that: 
22+  * 
23+  * - any encrypted fields are declared on exactly one of the schemas (not both) 
24+  * - encrypted fields cannot be declared on either the parent or child schema, where the other schema declares the same field without encryption. 
25+  * 
26+  * @param  {Schema } parentSchema 
27+  * @param  {Schema } childSchema 
28+  */ 
29+ function  validateDiscriminatorSchemasForEncryption ( parentSchema ,  childSchema )  { 
30+   /** 
31+    * @param  schema { Schema } 
32+    * 
33+    * Given a schema, yields **all** paths to values inside that schema, recursively iterating over any nested schemas.  This is 
34+    * intended for use with encryption, so nested arrays are not considered because encryption on values inside arrays is not supported. 
35+    * 
36+    * @returns  { Iterable<string>  } 
37+    */ 
38+   function *  allPaths ( schema ,  prefix )  { 
39+     for  ( const  path  of  Object . keys ( schema . paths ) )  { 
40+       const  fullPath  =  prefix  !=  null  ? `${ prefix }  .${ path }  `  : path ; 
41+       if  ( schema . path ( path ) . instance  ===  'Embedded' )  { 
42+         yield *  allPaths ( schema . path ( path ) . schema ,  fullPath ) ; 
43+       }  else  { 
44+         yield  fullPath ; 
45+       } 
46+     } 
47+   } 
48+ 
49+   /** 
50+    * @param  {Iterable<T> } i1 
51+    * @param  {Iterable<T> } i2 
52+    * 
53+    * @returns  {Generator<T> } 
54+    */ 
55+   function *  setIntersection ( i1 ,  i2 )  { 
56+     const  s1  =  new  Set ( i1 ) ; 
57+     for  ( const  item  of  i2 )  { 
58+       if  ( s1 . has ( item ) )  { 
59+         yield  item ; 
60+       } 
61+     } 
62+   } 
63+ 
64+   for  ( const  path  of  setIntersection ( allPaths ( parentSchema ) ,  allPaths ( childSchema ) ) )  { 
65+     if  ( parentSchema . _hasEncryptedField ( path )  &&  childSchema . _hasEncryptedField ( path ) )  { 
66+       throw  new  Error ( `encrypted fields cannot be declared on both the base schema and the child schema in a discriminator. path=${ path }  ` ) ; 
67+     } 
68+ 
69+     if  ( parentSchema . _hasEncryptedField ( path )  ||  childSchema . _hasEncryptedField ( path ) )  { 
70+       throw  new  Error ( `encrypted fields cannot have the same path as a non-encrypted field for discriminators. path=${ path }  ` ) ; 
71+     } 
72+   } 
73+ } 
2074/*! 
2175 * ignore 
2276 */ 
@@ -80,6 +134,8 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
80134    value  =  tiedValue ; 
81135  } 
82136
137+   validateDiscriminatorSchemasForEncryption ( model . schema ,  schema ) ; 
138+ 
83139  function  merge ( schema ,  baseSchema )  { 
84140    // Retain original schema before merging base schema 
85141    schema . _baseSchema  =  baseSchema ; 
@@ -95,13 +151,6 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
95151    const  baseSchemaPaths  =  Object . keys ( baseSchema . paths ) ; 
96152    const  conflictingPaths  =  [ ] ; 
97153
98- 
99-     baseSchema . eachPath ( ( pathname )  =>  { 
100-       if  ( schema . _hasEncryptedField ( pathname ) )  { 
101-         throw  new  Error ( `cannot declare an encrypted field on child schema overriding base schema. key=${ pathname }  ` ) ; 
102-       } 
103-     } ) ; 
104- 
105154    for  ( const  path  of  baseSchemaPaths )  { 
106155      if  ( schema . nested [ path ] )  { 
107156        conflictingPaths . push ( path ) ; 
0 commit comments