api_testing_core/graphql/
runner.rs1use 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}