cli/commands/internal/graphql/
run.rs

1use anyhow::{anyhow, Result};
2use serde::{Deserialize, Serialize};
3use std::io::{self, Read};
4
5#[derive(Debug, clap::Args)]
6pub struct Run {
7    /// GraphQL query to execute. If not provided, reads from stdin.
8    pub query: Option<String>,
9
10    /// GraphQL variables as JSON string
11    #[arg(long, short = 'v')]
12    pub variables: Option<String>,
13
14    /// Read query from file
15    #[arg(long, short = 'f')]
16    pub file: Option<String>,
17
18    /// GraphQL server URL
19    #[arg(long, default_value = "http://localhost:3000/graphql")]
20    pub node: String,
21}
22
23#[derive(Debug, Serialize)]
24struct GraphQLRequest {
25    query: String,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    variables: Option<serde_json::Value>,
28}
29
30#[derive(Debug, Deserialize)]
31struct GraphQLResponse {
32    data: Option<serde_json::Value>,
33    errors: Option<Vec<GraphQLError>>,
34}
35
36#[derive(Debug, Deserialize)]
37struct GraphQLError {
38    message: String,
39}
40
41impl Run {
42    pub fn run(self) -> Result<()> {
43        // Get the query from various sources
44        let query = if let Some(query) = self.query {
45            query
46        } else if let Some(file_path) = self.file {
47            std::fs::read_to_string(&file_path)
48                .map_err(|e| anyhow!("Failed to read file '{}': {}", file_path, e))?
49        } else {
50            // Read from stdin
51            let mut buffer = String::new();
52            io::stdin()
53                .read_to_string(&mut buffer)
54                .map_err(|e| anyhow!("Failed to read from stdin: {}", e))?;
55            buffer
56        };
57
58        // Parse variables if provided
59        let variables = if let Some(vars_str) = self.variables {
60            let vars: serde_json::Value = serde_json::from_str(&vars_str)
61                .map_err(|e| anyhow!("Failed to parse variables JSON: {}", e))?;
62            Some(vars)
63        } else {
64            None
65        };
66
67        // Build GraphQL request
68        let request = GraphQLRequest { query, variables };
69
70        // Execute the query
71        let client = reqwest::blocking::Client::new();
72        let response = client
73            .post(&self.node)
74            .json(&request)
75            .send()
76            .map_err(|e| anyhow!("Failed to connect to GraphQL server: {}", e))?;
77
78        if !response.status().is_success() {
79            return Err(anyhow!(
80                "GraphQL server returned error: {}",
81                response.status()
82            ));
83        }
84
85        let graphql_response: GraphQLResponse = response
86            .json()
87            .map_err(|e| anyhow!("Failed to parse GraphQL response: {}", e))?;
88
89        // Display the response
90        if let Some(errors) = &graphql_response.errors {
91            if !errors.is_empty() {
92                eprintln!("Errors:");
93                for error in errors {
94                    eprintln!("  - {}", error.message);
95                }
96                if graphql_response.data.is_none() {
97                    return Err(anyhow!("GraphQL query failed with errors"));
98                }
99            }
100        }
101
102        if let Some(data) = graphql_response.data {
103            let formatted = serde_json::to_string_pretty(&data)
104                .unwrap_or_else(|_| serde_json::to_string(&data).unwrap_or_default());
105            println!("{}", formatted);
106        }
107
108        Ok(())
109    }
110}