diff --git a/rust/pact_consumer/src/builders/interaction_builder.rs b/rust/pact_consumer/src/builders/interaction_builder.rs index 926e0b0f..9d990d38 100644 --- a/rust/pact_consumer/src/builders/interaction_builder.rs +++ b/rust/pact_consumer/src/builders/interaction_builder.rs @@ -1,11 +1,12 @@ use std::collections::HashMap; use maplit::hashmap; +use serde_json::{json, Value}; +use tracing::debug; +use pact_models::json_utils::json_deep_merge; use pact_models::provider_states::ProviderState; use pact_models::sync_interaction::RequestResponseInteraction; use pact_models::v4::synch_http::SynchronousHttp; -use serde_json::{json, Value}; -use tracing::debug; use super::request_builder::RequestBuilder; use super::response_builder::ResponseBuilder; @@ -162,13 +163,23 @@ impl InteractionBuilder { let request_config = self.request.plugin_config(); if !request_config.is_empty() { for (key, value) in request_config { - config.insert(key.clone(), value.interaction_configuration.clone()); + config.insert(key, value.interaction_configuration); } } let response_config = self.response.plugin_config(); if !response_config.is_empty() { for (key, value) in response_config { - config.insert(key.clone(), value.interaction_configuration.clone()); + let value_config = value.interaction_configuration.clone(); + config.entry(key) + .and_modify(|entry| { + for (k, v) in value_config { + entry + .entry(k) + .and_modify(|e| *e = json_deep_merge(e, &v)) + .or_insert(v); + } + }) + .or_insert(value.interaction_configuration); } } } @@ -195,3 +206,57 @@ impl InteractionBuilder { config } } + +#[cfg(all(test, feature = "plugins"))] +mod plugin_tests { + use expectest::prelude::*; + use maplit::hashmap; + use pact_plugin_driver::content::PluginConfiguration; + use serde_json::json; + + use crate::builders::InteractionBuilder; + + #[test] + fn plugin_config_merges_config_from_request_and_response_parts() { + let mut builder = InteractionBuilder::new("test", ""); + builder.request.plugin_config = hashmap!{ + "plugin1".to_string() => PluginConfiguration { + interaction_configuration: hashmap!{ + "other".to_string() => json!(100), + "request".to_string() => json!({ + "descriptorKey": "d58838959e37498cddf51805bedf4dca", + "message": ".area_calculator.ShapeMessage" + }) + }, + pact_configuration: Default::default() + } + }; + builder.response.plugin_config = hashmap!{ + "plugin1".to_string() => PluginConfiguration { + interaction_configuration: hashmap!{ + "other".to_string() => json!(200), + "response".to_string() => json!({ + "descriptorKey": "d58838959e37498cddf51805bedf4dca", + "message": ".area_calculator.AreaResponse" + }) + }, + pact_configuration: Default::default() + } + }; + + let config = builder.plugin_config(); + expect!(config).to(be_equal_to(hashmap!{ + "plugin1".to_string() => hashmap!{ + "other".to_string() => json!(200), + "request".to_string() => json!({ + "descriptorKey": "d58838959e37498cddf51805bedf4dca", + "message": ".area_calculator.ShapeMessage" + }), + "response".to_string() => json!({ + "descriptorKey": "d58838959e37498cddf51805bedf4dca", + "message": ".area_calculator.AreaResponse" + }) + } + })); + } +} diff --git a/rust/pact_consumer/src/builders/request_builder.rs b/rust/pact_consumer/src/builders/request_builder.rs index d6b4e05e..c9385db4 100644 --- a/rust/pact_consumer/src/builders/request_builder.rs +++ b/rust/pact_consumer/src/builders/request_builder.rs @@ -36,7 +36,7 @@ struct PluginConfiguration {} #[derive(Clone, Debug)] pub struct RequestBuilder { request: HttpRequest, - #[allow(dead_code)] plugin_config: HashMap, + #[allow(dead_code)] pub(crate) plugin_config: HashMap, interaction_markup: InteractionMarkup } @@ -208,7 +208,18 @@ impl RequestBuilder { request.generators.add_generators(generators.clone()); } if !contents.plugin_config.is_empty() { - self.plugin_config.insert(matcher.plugin_name(), contents.plugin_config.clone()); + let plugin_config = PluginConfiguration { + interaction_configuration: hashmap!{ + "request".to_string() => Value::Object( + contents.plugin_config.interaction_configuration + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect() + ) + }, + pact_configuration: contents.plugin_config.pact_configuration.clone(), + }; + self.plugin_config.insert(matcher.plugin_name(), plugin_config); } self.interaction_markup = InteractionMarkup { markup: contents.interaction_markup.clone(), diff --git a/rust/pact_consumer/src/builders/response_builder.rs b/rust/pact_consumer/src/builders/response_builder.rs index e11b9b91..bd462a85 100644 --- a/rust/pact_consumer/src/builders/response_builder.rs +++ b/rust/pact_consumer/src/builders/response_builder.rs @@ -25,7 +25,7 @@ struct PluginConfiguration {} #[derive(Clone, Debug)] pub struct ResponseBuilder { response: HttpResponse, - #[allow(dead_code)] plugin_config: HashMap, + #[allow(dead_code)] pub(crate) plugin_config: HashMap, interaction_markup: InteractionMarkup } @@ -138,7 +138,18 @@ impl ResponseBuilder { response.generators.add_generators(generators.clone()); } if !contents.plugin_config.is_empty() { - self.plugin_config.insert(matcher.plugin_name(), contents.plugin_config.clone()); + let plugin_config = PluginConfiguration { + interaction_configuration: hashmap!{ + "response".to_string() => Value::Object( + contents.plugin_config.interaction_configuration + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect() + ) + }, + pact_configuration: contents.plugin_config.pact_configuration.clone(), + }; + self.plugin_config.insert(matcher.plugin_name(), plugin_config); } self.interaction_markup = InteractionMarkup { markup: contents.interaction_markup.clone(),