Skip to main content

api_testing_core/graphql/
runner.rs

1use anyhow::Context;
2
3use crate::Result;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct GraphqlHttpResponse {
7    pub status: u16,
8    pub body: Vec<u8>,
9    pub content_type: Option<String>,
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct GraphqlExecutedRequest {
14    pub url: String,
15    pub response: GraphqlHttpResponse,
16}
17
18fn build_payload(operation: &str, variables: Option<&serde_json::Value>) -> serde_json::Value {
19    let mut obj = serde_json::Map::new();
20    obj.insert(
21        "query".to_string(),
22        serde_json::Value::String(operation.to_string()),
23    );
24    if let Some(vars) = variables {
25        obj.insert("variables".to_string(), vars.clone());
26    }
27    serde_json::Value::Object(obj)
28}
29
30pub fn execute_graphql_request(
31    endpoint_url: &str,
32    bearer_token: Option<&str>,
33    operation: &str,
34    variables: Option<&serde_json::Value>,
35) -> Result<GraphqlExecutedRequest> {
36    let payload = build_payload(operation, variables);
37    let bytes =
38        serde_json::to_vec(&payload).context("failed to serialize GraphQL request payload")?;
39
40    let client = reqwest::blocking::Client::new();
41    let mut builder = client
42        .post(endpoint_url)
43        .header(reqwest::header::ACCEPT, "application/json")
44        .header(reqwest::header::CONTENT_TYPE, "application/json")
45        .body(bytes);
46
47    if let Some(token) = bearer_token {
48        builder = builder.header(reqwest::header::AUTHORIZATION, format!("Bearer {token}"));
49    }
50
51    let response = builder
52        .send()
53        .with_context(|| format!("HTTP request failed: POST {endpoint_url}"))?;
54
55    let status = response.status().as_u16();
56    let content_type = response
57        .headers()
58        .get(reqwest::header::CONTENT_TYPE)
59        .and_then(|v| v.to_str().ok())
60        .map(|s| s.to_string());
61    let body = response
62        .bytes()
63        .context("failed to read response body")?
64        .to_vec();
65
66    if !(200..300).contains(&status) {
67        anyhow::bail!("HTTP request failed with status {status}.");
68    }
69
70    Ok(GraphqlExecutedRequest {
71        url: endpoint_url.to_string(),
72        response: GraphqlHttpResponse {
73            status,
74            body,
75            content_type,
76        },
77    })
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn graphql_runner_build_payload_includes_vars_only_when_present() {
86        let op = "query { ok }";
87        let with_vars = build_payload(op, Some(&serde_json::json!({"a": 1})));
88        assert!(with_vars.get("variables").is_some());
89        let without_vars = build_payload(op, None);
90        assert!(without_vars.get("variables").is_none());
91    }
92}