@@ -104,59 +104,67 @@ public JsonNode copySecrets(final JsonNode src, final JsonNode dst, final JsonNo
104104 if (!isValidJsonSchema (schema )) {
105105 return dst ;
106106 }
107+
107108 Preconditions .checkArgument (dst .isObject ());
108109 Preconditions .checkArgument (src .isObject ());
109110
110111 final ObjectNode dstCopy = dst .deepCopy ();
112+ return copySecretsRecursive (src , dstCopy , schema );
113+ }
114+
115+ return src ;
116+ }
111117
118+ // This function is modifying dstCopy in place.
119+ private JsonNode copySecretsRecursive (final JsonNode src , JsonNode dstCopy , final JsonNode schema ) {
120+ // todo (cgardens) this is not safe. should throw.
121+ if (!isValidJsonSchema (schema )) {
122+ return dstCopy ;
123+ }
124+
125+ Preconditions .checkArgument (dstCopy .isObject ());
126+ Preconditions .checkArgument (src .isObject ());
127+
128+ final Optional <String > combinationKey = findJsonCombinationNode (schema );
129+ if (combinationKey .isPresent ()) {
130+ final var arrayNode = (ArrayNode ) schema .get (combinationKey .get ());
131+ for (int i = 0 ; i < arrayNode .size (); i ++) {
132+ final JsonNode childSchema = arrayNode .get (i );
133+ /*
134+ * when traversing a oneOf or anyOf if multiple schema in the oneOf or anyOf have the SAME key, but
135+ * a different type, then, without this test, we can try to apply the wrong schema to the object
136+ * resulting in errors because of type mismatches.
137+ */
138+ if (VALIDATOR .test (childSchema , dstCopy )) {
139+ // Absorb field values if any of the combination option is declaring it as secrets
140+ copySecretsRecursive (src , dstCopy , childSchema );
141+ }
142+ }
143+ } else {
112144 final ObjectNode properties = (ObjectNode ) schema .get (PROPERTIES_FIELD );
113145 for (final String key : Jsons .keys (properties )) {
114- // If the source object doesn't have this key then we have nothing to copy, so we should skip to the
115- // next key.
116- if (!src .has (key )) {
146+ // If the source or destination doesn't have this key then we have nothing to copy, so we should
147+ // skip to the next key.
148+ if (!src .has (key ) || ! dstCopy . has ( key ) ) {
117149 continue ;
118150 }
119151
120152 final JsonNode fieldSchema = properties .get (key );
121153 // We only copy the original secret if the destination object isn't attempting to overwrite it
122154 // I.e. if the destination object's value is set to the mask, then we can copy the original secret
123- if (JsonSecretsProcessor .isSecret (fieldSchema ) && dst .has (key ) && AirbyteSecretConstants .SECRETS_MASK .equals (dst .get (key ).asText ())) {
124- dstCopy .set (key , src .get (key ));
125- } else if (dstCopy .has (key )) {
126- // If the destination has this key, then we should consider copying it
127-
128- // Check if this schema is a combination node; if it is, find a matching sub-schema and copy based
129- // on that sub-schema
130- final var combinationKey = findJsonCombinationNode (fieldSchema );
131- if (combinationKey .isPresent ()) {
132- var combinationCopy = dstCopy .get (key );
133- final var arrayNode = (ArrayNode ) fieldSchema .get (combinationKey .get ());
134- for (int i = 0 ; i < arrayNode .size (); i ++) {
135- final JsonNode childSchema = arrayNode .get (i );
136- /*
137- * when traversing a oneOf or anyOf if multiple schema in the oneOf or anyOf have the SAME key, but
138- * a different type, then, without this test, we can try to apply the wrong schema to the object
139- * resulting in errors because of type mismatches.
140- */
141- if (VALIDATOR .test (childSchema , combinationCopy )) {
142- // Absorb field values if any of the combination option is declaring it as secrets
143- combinationCopy = copySecrets (src .get (key ), combinationCopy , childSchema );
144- }
145- }
146- dstCopy .set (key , combinationCopy );
147- } else {
148- // Otherwise, this is just a plain old json node; recurse into it. If it's not actually an object,
149- // the recursive call will exit immediately.
150- final JsonNode copiedField = copySecrets (src .get (key ), dstCopy .get (key ), fieldSchema );
151- dstCopy .set (key , copiedField );
152- }
155+ if (JsonSecretsProcessor .isSecret (fieldSchema ) && AirbyteSecretConstants .SECRETS_MASK .equals (dstCopy .get (key ).asText ())) {
156+ ((ObjectNode ) dstCopy ).set (key , src .get (key ));
157+
158+ } else {
159+ // Otherwise, this is just a plain old json node; recurse into it. If it's not actually an object,
160+ // the recursive call will exit immediately.
161+ final JsonNode copiedField = copySecretsRecursive (src .get (key ), dstCopy .get (key ), fieldSchema );
162+ ((ObjectNode ) dstCopy ).set (key , copiedField );
153163 }
154164 }
155-
156- return dstCopy ;
157165 }
158166
159- return src ;
167+ return dstCopy ;
160168 }
161169
162170 static boolean isSecret (final JsonNode obj ) {
0 commit comments