Skip to main content

josh_github_graphql/operations/
pull_request.rs

1use crate::connection::GithubApiConnection;
2use anyhow::anyhow;
3
4use josh_github_codegen_graphql::{
5    close_pull_request, convert_pull_request_to_draft, create_pull_request, get_pr_by_head,
6    mark_pull_request_ready_for_review, update_pull_request, ClosePullRequest,
7    ConvertPullRequestToDraft, CreatePullRequest, GetPrByHead, MarkPullRequestReadyForReview,
8    UpdatePullRequest,
9};
10
11impl GithubApiConnection {
12    /// Find an open PR by head branch name. Returns (node_id, number, is_draft) if found.
13    pub async fn find_pull_request_by_head(
14        &self,
15        owner: &str,
16        name: &str,
17        head_ref_name: &str,
18    ) -> anyhow::Result<Option<(String, i64, bool)>> {
19        let variables = get_pr_by_head::Variables {
20            owner: owner.to_string(),
21            name: name.to_string(),
22            head_ref_name: head_ref_name.to_string(),
23        };
24        let response = self.make_request::<GetPrByHead>(variables).await?;
25        let repo = match response.repository {
26            Some(r) => r,
27            None => return Ok(None),
28        };
29        let nodes = repo.pull_requests.nodes.unwrap_or_default();
30        let pr = nodes.into_iter().flatten().next();
31        Ok(pr.map(|n| (n.id, n.number, n.is_draft)))
32    }
33
34    /// Update an existing PR's title, body, and/or base branch.
35    pub async fn update_pull_request(
36        &self,
37        pull_request_id: &str,
38        title: Option<&str>,
39        body: Option<&str>,
40        base_ref_name: Option<&str>,
41    ) -> anyhow::Result<(String, i64)> {
42        let variables = update_pull_request::Variables {
43            pull_request_id: pull_request_id.to_string(),
44            title: title.map(String::from),
45            body: body.map(String::from),
46            base_ref_name: base_ref_name.map(String::from),
47        };
48        let response = self.make_request::<UpdatePullRequest>(variables).await?;
49        let response = match response.update_pull_request {
50            Some(r) => r,
51            None => return Err(anyhow!("Failed to parse response: update_pull_request")),
52        };
53        let pr = match response.pull_request {
54            Some(p) => p,
55            None => return Err(anyhow!("Failed to parse response: pull_request")),
56        };
57        Ok((pr.id, pr.number))
58    }
59
60    pub async fn convert_pull_request_to_draft(
61        &self,
62        pull_request_id: &str,
63    ) -> anyhow::Result<(String, i64, bool)> {
64        let variables = convert_pull_request_to_draft::Variables {
65            pull_request_id: pull_request_id.to_string(),
66        };
67        let response = self
68            .make_request::<ConvertPullRequestToDraft>(variables)
69            .await?;
70        let response = match response.convert_pull_request_to_draft {
71            Some(r) => r,
72            None => {
73                return Err(anyhow!(
74                    "Failed to parse response: convert_pull_request_to_draft"
75                ))
76            }
77        };
78        let pr = match response.pull_request {
79            Some(p) => p,
80            None => return Err(anyhow!("Failed to parse response: pull_request")),
81        };
82        Ok((pr.id, pr.number, pr.is_draft))
83    }
84
85    pub async fn mark_pull_request_ready_for_review(
86        &self,
87        pull_request_id: &str,
88    ) -> anyhow::Result<(String, i64, bool)> {
89        let variables = mark_pull_request_ready_for_review::Variables {
90            pull_request_id: pull_request_id.to_string(),
91        };
92        let response = self
93            .make_request::<MarkPullRequestReadyForReview>(variables)
94            .await?;
95        let response = match response.mark_pull_request_ready_for_review {
96            Some(r) => r,
97            None => {
98                return Err(anyhow!(
99                    "Failed to parse response: mark_pull_request_ready_for_review"
100                ))
101            }
102        };
103        let pr = match response.pull_request {
104            Some(p) => p,
105            None => return Err(anyhow!("Failed to parse response: pull_request")),
106        };
107        Ok((pr.id, pr.number, pr.is_draft))
108    }
109
110    pub async fn create_pull_request(
111        &self,
112        repository_id: &str,
113        base_branch: &str,
114        head_branch: &str,
115        title: &str,
116        body: &str,
117        draft: bool,
118    ) -> anyhow::Result<(String, i64)> {
119        let variables = create_pull_request::Variables {
120            repository_id: repository_id.to_string(),
121            base_ref_name: base_branch.into(),
122            head_ref_name: head_branch.into(),
123            title: title.to_string(),
124            body: body.to_string(),
125            draft,
126        };
127
128        let response = self.make_request::<CreatePullRequest>(variables).await?;
129        let response = match response.create_pull_request {
130            Some(response) => response,
131            None => return Err(anyhow!("Failed to parse response: create_pull_request")),
132        };
133
134        let response = match response.pull_request {
135            Some(response) => response,
136            None => return Err(anyhow!("Failed to parse response: pull_request")),
137        };
138
139        Ok((response.id, response.number))
140    }
141
142    pub async fn close_pull_request(
143        &self,
144        // Note: this is not pull request number! This is a global "node id"
145        pull_request_node_id: &str,
146    ) -> anyhow::Result<()> {
147        let variables = close_pull_request::Variables {
148            pull_request_node_id: pull_request_node_id.to_string(),
149        };
150
151        let response = self.make_request::<ClosePullRequest>(variables).await?;
152        if response.close_pull_request.is_none() {
153            return Err(anyhow!("Failed to parse response: close_pull_request"));
154        };
155
156        Ok(())
157    }
158}