1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::collections::HashMap;
use std::sync::Arc;
use ansi_term::Colour::Yellow;
use async_trait::async_trait;
use maplit::*;
use serde_json::{json, Value};
use pact_models::bodies::OptionalBody;
use pact_models::content_types::JSON;
use pact_models::provider_states::ProviderState;
use pact_models::request::Request;
use crate::provider_client::{make_state_change_request, provider_client_error_to_string};
pub trait RequestFilterExecutor {
fn call(self: Arc<Self>, request: &Request) -> Request;
}
pub struct NullRequestFilterExecutor {
_private_field: (),
}
impl RequestFilterExecutor for NullRequestFilterExecutor {
fn call(self: Arc<Self>, _request: &Request) -> Request {
unimplemented!("NullRequestFilterExecutor should never be called")
}
}
#[derive(Debug, Clone)]
pub struct ProviderStateError {
pub description: String,
pub interaction_id: Option<String>
}
#[async_trait]
pub trait ProviderStateExecutor {
async fn call(self: Arc<Self>, interaction_id: Option<String>, provider_state: &ProviderState, setup: bool, client: Option<&reqwest::Client>) -> Result<HashMap<String, Value>, ProviderStateError>;
}
#[derive(Debug, Clone)]
pub struct HttpRequestProviderStateExecutor {
pub state_change_url: Option<String>,
pub state_change_teardown: bool,
pub state_change_body: bool
}
impl Default for HttpRequestProviderStateExecutor {
fn default() -> HttpRequestProviderStateExecutor {
HttpRequestProviderStateExecutor {
state_change_url: None,
state_change_teardown: false,
state_change_body: true
}
}
}
#[async_trait]
impl ProviderStateExecutor for HttpRequestProviderStateExecutor {
async fn call(
self: Arc<Self>,
interaction_id: Option<String>,
provider_state: &ProviderState,
setup: bool,
client: Option<&reqwest::Client>
) -> Result<HashMap<String, Value>, ProviderStateError> {
match &self.state_change_url {
Some(state_change_url) => {
let mut state_change_request = Request { method: "POST".to_string(), .. Request::default() };
if self.state_change_body {
let json_body = json!({
"state".to_string() : provider_state.name.clone(),
"params".to_string() : provider_state.params.clone(),
"action".to_string() : if setup {
"setup".to_string()
} else {
"teardown".to_string()
}
});
state_change_request.body = OptionalBody::Present(json_body.to_string().into(), Some(JSON.clone()));
state_change_request.headers = Some(hashmap!{ "Content-Type".to_string() => vec!["application/json".to_string()] });
} else {
let mut query = hashmap!{ "state".to_string() => vec![provider_state.name.clone()] };
if setup {
query.insert("action".to_string(), vec!["setup".to_string()]);
} else {
query.insert("action".to_string(), vec!["teardown".to_string()]);
}
for (k, v) in provider_state.params.clone() {
query.insert(k, vec![match v {
Value::String(ref s) => s.clone(),
_ => v.to_string()
}]);
}
state_change_request.query = Some(query);
}
make_state_change_request(client.unwrap_or(&reqwest::Client::default()), &state_change_url, &state_change_request).await
.map_err(|err| ProviderStateError { description: provider_client_error_to_string(err), interaction_id })
},
None => {
if setup {
println!(" {}", Yellow.paint("WARNING: State Change ignored as there is no state change URL provided"));
}
Ok(hashmap!{})
}
}
}
}