Skip to content

Commit

Permalink
added missing /repos/{owner}/{repo}/pulls/... handlers (#546) (#605)
Browse files Browse the repository at this point in the history
* added comment handler for /repos/{OWNER}/{REPO}/pulls/comments/{COMMENT_ID}

* fixed doctest

* added 'list commits on a pull request'

* added particular review operations (/repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id})

* fixed cargo fmt

* added "Submit a review for a pull request" /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/events

* added "Dismiss a review for a pull request" https://docs.github.com/en/rest/pulls/reviews?apiVersion=2022-11-28#dismiss-a-review-for-a-pull-request

* added "List comments for a pull request review" https://docs.github.com/en/rest/pulls/reviews?apiVersion=2022-11-28#list-comments-for-a-pull-request-review

* added "Create a reply for a review comment" https://docs.github.com/en/rest/pulls/comments?apiVersion=2022-11-28#create-a-reply-for-a-review-comment

* added doc comments

* fixed doctest

* refactored specific PR usage; tests pending
  • Loading branch information
dmgorsky authored Jul 29, 2024
1 parent 9438485 commit a147ef2
Show file tree
Hide file tree
Showing 16 changed files with 1,239 additions and 7 deletions.
103 changes: 96 additions & 7 deletions src/api/pulls.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
//! The pull request API.

mod comment;
mod create;
mod list;
mod merge;
mod update;

use http::request::Builder;
use http::{Method, Uri};

use serde_json::json;
use snafu::ResultExt;

use crate::error::HttpSnafu;
use crate::models::pulls::ReviewComment;
use crate::models::CommentId;
use crate::pulls::specific_pr::pr_reviews::specific_review::SpecificReviewBuilder;
use crate::pulls::specific_pr::SpecificPullRequestBuilder;
use crate::{Octocrab, Page};

pub use self::{
create::CreatePullRequestBuilder, list::ListPullRequestsBuilder,
update::UpdatePullRequestBuilder,
};

mod comment;
mod create;
mod list;
mod merge;
mod specific_pr;
mod update;

/// A client to GitHub's pull request API.
///
/// Created with [`Octocrab::pulls`].
Expand Down Expand Up @@ -372,6 +377,90 @@ impl<'octo> PullRequestHandler<'octo> {
comment::ListCommentsBuilder::new(self, pr)
}

///creates a new `CommentBuilder` for GET/PATCH/DELETE requests
/// to the `/repos/{owner}/{repo}/pulls/{pr}/comments/{comment_id}` endpoint
/// ```no_run
/// use octocrab::models::CommentId;
/// use octocrab::models::pulls::Comment;
/// async fn run() -> octocrab::Result<Comment> {
/// let octocrab = octocrab::Octocrab::default();
/// let _ = octocrab.pulls("owner", "repo").comment(CommentId(21)).delete();
/// let _ = octocrab.pulls("owner", "repo").comment(CommentId(42)).update("new comment");
/// let comment = octocrab.pulls("owner", "repo").comment(CommentId(42)).get().await;
///
/// comment
/// }
/// ```
pub fn comment(&self, comment_id: CommentId) -> comment::CommentBuilder {
comment::CommentBuilder::new(self, comment_id)
}

/// creates a builder for the `/repos/{owner}/{repo}/pulls/{pull_number}/......` endpoint
/// working with particular pull request, e.g.
/// * /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/events
/// * /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}
/// * /repos/{owner}/{repo}/pulls/{pull_number}/commits
/// * /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments
/// * /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/dismissals
/// * /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies
///
#[deprecated(
since = "0.34.4",
note = "specific PR builder transitioned to pr_review_actions, reply_to_comment, reply_to_comment"
)]
//FIXME: remove?
pub fn pull_number(&self, pull_nr: u64) -> SpecificPullRequestBuilder {
SpecificPullRequestBuilder::new(self, pull_nr)
}

// /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/events
// /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}
// repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments
// repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/dismissals
pub fn pr_review_actions(
&self,
pull_nr: u64,
review_id: u64,
) -> SpecificReviewBuilder<'octo, '_> {
SpecificReviewBuilder::new(self, pull_nr, review_id)
}

/// /repos/{owner}/{repo}/pulls/{pull_number}/commits
// pub fn pr_commits(&self, pull_nr: u64) -> SpecificPullRequestCommentBuilder<'octo, '_> {
// SpecificPullRequestCommentBuilder::new(self, pull_nr, 0)
// }

// /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies
/// Creates a reply to a specific comment of a pull request specified in the first argument
/// ```no_run
/// # use octocrab::models::CommentId;
/// async fn run() -> octocrab::Result<()> {
/// # let octocrab = octocrab::Octocrab::default();
/// use octocrab::params;
///
/// let page = octocrab.pulls("owner", "repo").reply_to_comment(142, CommentId(24), "This is my reply")
/// .await?;
/// # Ok(())
/// # }
/// ```
pub async fn reply_to_comment(
&self,
pull_nr: u64,
comment_id: CommentId,
comment: impl Into<String>,
) -> crate::Result<ReviewComment> {
let route = format!(
"/repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies",
owner = self.owner,
repo = self.repo,
pull_number = pull_nr,
comment_id = comment_id
);
self.crab
.post(route, Some(&json!({ "body": comment.into() })))
.await
}

/// Creates a new `MergePullRequestsBuilder` that can be configured used to
/// merge a pull request.
/// ```no_run
Expand Down
74 changes: 74 additions & 0 deletions src/api/pulls/comment.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use serde_json::json;

use crate::models::pulls::Comment;

use super::*;

/// A builder pattern struct for listing comments.
Expand Down Expand Up @@ -84,6 +88,76 @@ impl<'octo, 'b> ListCommentsBuilder<'octo, 'b> {
}
}

/// A builder pattern struct for working with specific comment.
///
/// created by [`PullRequestHandler::comment`]
///
/// [`PullRequestHandler::comment`]: ./struct.PullRequestHandler.html#method.comment
#[derive(serde::Serialize)]
pub struct CommentBuilder<'octo, 'b> {
#[serde(skip)]
handler: &'b PullRequestHandler<'octo>,
comment_id: CommentId,
}

impl<'octo, 'b> CommentBuilder<'octo, 'b> {
pub(crate) fn new(handler: &'b PullRequestHandler<'octo>, comment_id: CommentId) -> Self {
Self {
handler,
comment_id,
}
}

///https://docs.github.com/en/rest/pulls/comments?apiVersion=2022-11-28#get-a-review-comment-for-a-pull-request
pub async fn get(self) -> crate::Result<Comment> {
self.handler
.crab
.get(
format!(
"/repos/{owner}/{repo}/pulls/comments/{comment_id}",
owner = self.handler.owner,
repo = self.handler.repo,
comment_id = self.comment_id
),
None::<&Comment>,
)
.await
}

///https://docs.github.com/en/rest/pulls/comments?apiVersion=2022-11-28#update-a-review-comment-for-a-pull-request
pub async fn update(self, comment: &str) -> crate::Result<Comment> {
self.handler
.crab
.patch(
format!(
"/repos/{owner}/{repo}/pulls/comments/{comment_id}",
owner = self.handler.owner,
repo = self.handler.repo,
comment_id = self.comment_id
),
Some(&json!({ "body": comment })),
)
.await
}

///https://docs.github.com/en/rest/pulls/comments?apiVersion=2022-11-28#delete-a-review-comment-for-a-pull-request
pub async fn delete(self) -> crate::Result<()> {
self.handler
.crab
._delete(
format!(
"/repos/{owner}/{repo}/pulls/comments/{comment_id}",
owner = self.handler.owner,
repo = self.handler.repo,
comment_id = self.comment_id
),
None::<&()>,
)
.await?;
Ok(())
}
}

#[cfg(test)]
mod tests {
#[tokio::test]
Expand Down
101 changes: 101 additions & 0 deletions src/api/pulls/specific_pr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use crate::models::repos::RepoCommit;
use crate::models::CommentId;
use crate::pulls::specific_pr::pr_comment::SpecificPullRequestCommentBuilder;
use crate::pulls::specific_pr::pr_reviews::ReviewsBuilder;
use crate::pulls::PullRequestHandler;
use crate::Page;

mod pr_comment;
pub(crate) mod pr_reviews;
/// A builder pattern struct for working with a specific pull request data,
/// e.g. reviews, commits, comments, etc.
///
/// created by [`PullRequestHandler::pull_number`]
///
/// [`PullRequestHandler::pull_number`]: ./struct.PullRequestHandler.html#method.pull_number
#[derive(serde::Serialize)]
pub struct SpecificPullRequestBuilder<'octo, 'b> {
#[serde(skip)]
handler: &'b PullRequestHandler<'octo>,
pr_number: u64,
#[serde(skip_serializing_if = "Option::is_none")]
per_page: Option<u8>,

#[serde(skip_serializing_if = "Option::is_none")]
page: Option<u32>,
}

impl<'octo, 'b> SpecificPullRequestBuilder<'octo, 'b> {
pub(crate) fn new(handler: &'b PullRequestHandler<'octo>, pr_number: u64) -> Self {
Self {
handler,
pr_number,
per_page: None,
page: None,
}
}

/// Results per page (max: 100, default: 30).
pub fn per_page(mut self, per_page: impl Into<u8>) -> Self {
self.per_page = Some(per_page.into());
self
}

/// Page number of the results to fetch. (default: 1)
pub fn page(mut self, page: impl Into<u32>) -> Self {
self.page = Some(page.into());
self
}

///Lists a maximum of 250 commits for a pull request.
/// To receive a complete commit list for pull requests with more than 250 commits,
/// use the [List commits](https://docs.github.com/rest/commits/commits#list-commits) endpoint.
pub async fn commits(&self) -> crate::Result<Page<RepoCommit>> {
let route = format!(
"/repos/{owner}/{repo}/pulls/{pr_number}/commits",
owner = self.handler.owner,
repo = self.handler.repo,
pr_number = self.pr_number
);
self.handler.crab.get(route, Some(&self)).await
}

/// Creates a new `ReviewsBuilder`
/// ```no_run
/// # use octocrab::models::CommentId;
/// async fn run() -> octocrab::Result<()> {
/// # let octocrab = octocrab::Octocrab::default();
/// use octocrab::params;
///
/// let _ = octocrab.pulls("owner", "repo")
/// .pull_number(42)
/// .reviews()
/// .review(42)
/// .get()
/// .await;
/// Ok(())
/// }
/// ```
pub fn reviews(&self) -> ReviewsBuilder<'octo, '_> {
ReviewsBuilder::new(self.handler, self.pr_number)
}

/// Creates a new `SpecificPullRequestCommentBuilder`
/// ```no_run
/// # use octocrab::models::CommentId;
/// async fn run() -> octocrab::Result<()> {
/// # let octocrab = octocrab::Octocrab::default();
/// use octocrab::params;
///
/// let _ = octocrab.pulls("owner", "repo")
/// .pull_number(42)
/// .comment(CommentId(42))
/// .reply("new comment")
/// .await;
/// Ok(())
/// }
/// ```
pub fn comment(&self, comment_id: CommentId) -> SpecificPullRequestCommentBuilder {
SpecificPullRequestCommentBuilder::new(self.handler, self.pr_number, comment_id)
}
}
41 changes: 41 additions & 0 deletions src/api/pulls/specific_pr/pr_comment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use serde_json::json;

use crate::models::pulls::ReviewComment;
use crate::models::CommentId;
use crate::pulls::PullRequestHandler;

#[derive(serde::Serialize)]
pub struct SpecificPullRequestCommentBuilder<'octo, 'b> {
#[serde(skip)]
handler: &'b PullRequestHandler<'octo>,
pr_number: u64,
comment_id: CommentId,
}

impl<'octo, 'b> SpecificPullRequestCommentBuilder<'octo, 'b> {
pub(crate) fn new(
handler: &'b PullRequestHandler<'octo>,
pr_number: u64,
comment_id: CommentId,
) -> Self {
Self {
handler,
comment_id,
pr_number,
}
}

pub async fn reply(&self, comment: impl Into<String>) -> crate::Result<ReviewComment> {
let route = format!(
"/repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies",
owner = self.handler.owner,
repo = self.handler.repo,
pull_number = self.pr_number,
comment_id = self.comment_id
);
self.handler
.crab
.post(route, Some(&json!({ "body": comment.into() })))
.await
}
}
Loading

0 comments on commit a147ef2

Please sign in to comment.