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
use pact_matching::models::{Request, OptionalBody};
use pact_matching::models::provider_states::ProviderState;
use crate::provider_client::{make_state_change_request, provider_client_error_to_string};
use std::collections::HashMap;
use serde_json::{Value, json};
use ansi_term::Colour::Yellow;
use async_trait::async_trait;
use maplit::*;
pub trait RequestFilterExecutor {
fn call(&self, request: &Request) -> Request;
}
pub struct NullRequestFilterExecutor {
_private_field: (),
}
impl RequestFilterExecutor for NullRequestFilterExecutor {
fn call(&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, interaction_id: Option<String>, provider_state: &ProviderState, setup: bool, client: Option<&reqwest::Client>) -> Result<HashMap<String, Value>, ProviderStateError>;
}
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, 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 mut json_body = json!({
"state".to_string() : json!(provider_state.name.clone()),
"action".to_string() : json!(if setup {
"setup".to_string()
} else {
"teardown".to_string()
})
});
{
let json_body_mut = json_body.as_object_mut().unwrap();
for (k, v) in provider_state.params.clone() {
json_body_mut.insert(k, v);
}
}
state_change_request.body = OptionalBody::Present(json_body.to_string().into());
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);
}
match make_state_change_request(client.unwrap_or(&reqwest::Client::default()), &state_change_url, &state_change_request).await {
Ok(_) => Ok(hashmap!{}),
Err(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"));
}
Ok(hashmap!{})
}
}
}
}