Skip to main content

josh_github_graphql/
request.rs

1use anyhow::{anyhow, Context};
2use graphql_client::GraphQLQuery;
3use http::header;
4
5use crate::connection::GithubApiConnection;
6
7pub const GITHUB_ACCEPT: &str = "application/vnd.github+json";
8pub const GITHUB_REST_API_URL: &str = "https://api.github.com";
9pub const GITHUB_GRAPHQL_API_URL: &str = "https://api.github.com/graphql";
10pub const GITHUB_API_VERSION: &str = "2022-11-28";
11
12pub const JOSH_API_USER_AGENT: &str = "josh-project";
13
14impl GithubApiConnection {
15    // Non-generic code to actually make the request -
16    // reduces the amount of compiled templated code
17    async fn do_send_request(&self, body: serde_json::Value) -> anyhow::Result<serde_json::Value> {
18        let builder = self
19            .client
20            .post(self.api_url.clone())
21            .header(header::USER_AGENT, JOSH_API_USER_AGENT)
22            .header(header::ACCEPT, GITHUB_ACCEPT);
23
24        let response = builder
25            .header("X-GitHub-Api-Version", GITHUB_API_VERSION)
26            .header("X-Github-Next-Global-ID", "1")
27            .json(&body)
28            .send()
29            .await?;
30
31        let status = response.status();
32        if status.is_success() {
33            let response_body = response.json().await?;
34            Ok(response_body)
35        } else {
36            let response_body = response.text().await?;
37            let message = format!(
38                "Unexpected status code while making request: {} {}",
39                self.api_url, status
40            );
41
42            if response_body.trim().is_empty() {
43                Err(anyhow!(message))
44            } else {
45                Err(anyhow!("{}, body: {}", message, response_body))
46            }
47        }
48    }
49
50    pub(crate) async fn make_request<T: GraphQLQuery>(
51        &self,
52        variables: T::Variables,
53    ) -> anyhow::Result<T::ResponseData> {
54        let request_body = T::build_query(variables);
55        let request_body = serde_json::to_value(&request_body)?;
56
57        let response = self.do_send_request(request_body).await?;
58        let response_body: graphql_client::Response<T::ResponseData> =
59            serde_json::from_value(response).context("Failed to parse response body")?;
60
61        if let Some(errors) = response_body.errors {
62            let message = errors
63                .iter()
64                .map(|error| error.message.clone())
65                .collect::<Vec<_>>()
66                .join(", ");
67
68            return Err(anyhow!("GraphQL request failed: {:?}", message));
69        }
70
71        response_body.data.context("Response data is missing")
72    }
73}