Skip to content

Commit

Permalink
Add associated pull requests and commit compare functionality (#413)
Browse files Browse the repository at this point in the history
* add InProgress variant to WorkflowRunEventAction

* added types and handler for comparing commits

* wip

* fix type based on test get request

* wip

* add associated pull requests endpoint

* implement paging

* wip

* remove pagination of compare commit

* remove commented code

* wip

* fix page type

* make unit tests async
  • Loading branch information
matthewgapp authored Jul 28, 2023
1 parent 7590176 commit 6e91b68
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 0 deletions.
19 changes: 19 additions & 0 deletions src/api/commits.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
//! The commit API.
mod associated_pull_requests;
mod compare_commit;
mod create_comment;

pub use associated_pull_requests::PullRequestTarget;

pub use self::create_comment::CreateCommentBuilder;
use crate::{models, Octocrab, Result};

Expand All @@ -19,6 +23,21 @@ impl<'octo> CommitHandler<'octo> {
// create::CreateIssueBuilder::new(self, title.into())
// }

pub fn compare(
&self,
base: impl Into<String>,
head: impl Into<String>,
) -> compare_commit::CompareCommitsBuilder<'_, '_> {
compare_commit::CompareCommitsBuilder::new(self, base.into(), head.into())
}

pub fn associated_pull_requests(
&self,
target: PullRequestTarget,
) -> associated_pull_requests::AssociatedPullRequestsBuilder<'_, '_> {
associated_pull_requests::AssociatedPullRequestsBuilder::new(self, target)
}

pub fn create_comment(
&self,
sha: impl Into<String>,
Expand Down
88 changes: 88 additions & 0 deletions src/api/commits/associated_pull_requests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use super::*;

/// helper to let users know they can pass a branch name or a commit sha
#[derive(Clone, Debug, serde::Serialize)]
#[serde(untagged)]
pub enum PullRequestTarget {
Branch(String),
Sha(String),
}

impl ToString for PullRequestTarget {
fn to_string(&self) -> String {
match self {
Self::Branch(branch) => branch.to_string(),
Self::Sha(commit) => commit.to_string(),
}
}
}

#[derive(serde::Serialize)]
pub struct AssociatedPullRequestsBuilder<'octo, 'r> {
#[serde(skip)]
handler: &'r super::CommitHandler<'octo>,
target: PullRequestTarget,
#[serde(skip_serializing_if = "Option::is_none")]
per_page: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
page: Option<u32>,
}

impl<'octo, 'r> AssociatedPullRequestsBuilder<'octo, 'r> {
/// Sha will return all closed pull requests for the given commit sha.
///
/// Pass a Branch to return all open pull requests against that branch.
pub(crate) fn new(handler: &'r super::CommitHandler<'octo>, target: PullRequestTarget) -> Self {
Self {
handler,
target,
page: None,
per_page: None,
}
}

/// Results per page (max 100).
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.
pub fn page(mut self, page: impl Into<u32>) -> Self {
self.page = Some(page.into());
self
}

/// Sends the actual request.
pub async fn send(self) -> crate::Result<crate::Page<models::pulls::PullRequest>> {
let route = format!(
"/repos/{owner}/{repo}/commits/{target}/pulls",
owner = self.handler.owner,
repo = self.handler.repo,
target = self.target.to_string(),
);

self.handler.crab.get(route, Some(&self)).await
}
}

#[cfg(test)]
mod tests {

#[tokio::test]
async fn associated_pull_requests_serializes_correctly() {
use super::PullRequestTarget;

let octocrab = crate::Octocrab::default();
let handler = octocrab.commits("owner", "repo");
let associated_prs =
handler.associated_pull_requests(PullRequestTarget::Sha("commit_sha".to_string()));

assert_eq!(
serde_json::to_value(associated_prs).unwrap(),
serde_json::json!({
"target": "commit_sha"
})
);
}
}
73 changes: 73 additions & 0 deletions src/api/commits/compare_commit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use super::*;

#[derive(serde::Serialize)]
pub struct CompareCommitsBuilder<'octo, 'r> {
#[serde(skip)]
handler: &'r super::CommitHandler<'octo>,
base: String,
head: String,
#[serde(skip_serializing_if = "Option::is_none")]
per_page: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
page: Option<u32>,
}

impl<'octo, 'r> CompareCommitsBuilder<'octo, 'r> {
pub(crate) fn new(
handler: &'r super::CommitHandler<'octo>,
base: String,
head: String,
) -> Self {
Self {
handler,
base,
head,
page: None,
per_page: None,
}
}

/// Results per page (max 100).
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.
pub fn page(mut self, page: impl Into<u32>) -> Self {
self.page = Some(page.into());
self
}

/// Sends the actual request.
pub async fn send(self) -> crate::Result<models::commits::CommitComparison> {
let route = format!(
"/repos/{owner}/{repo}/compare/{base}...{head}",
owner = self.handler.owner,
repo = self.handler.repo,
base = self.base,
head = self.head,
);

self.handler.crab.get(route, Some(&self)).await
}
}

#[cfg(test)]
mod tests {

#[tokio::test]
async fn compare_commits_serializes_correctly() {
let octocrab = crate::Octocrab::default();
let handler = octocrab.commits("owner", "repo");
let comparison = handler.compare("base", "head");

assert_eq!(
serde_json::to_value(comparison).unwrap(),
serde_json::json!({
"base": "base",
"head": "head",
})
);
}
}
124 changes: 124 additions & 0 deletions src/models/commits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,127 @@ pub struct Comment {
pub updated_at: Option<chrono::DateTime<chrono::Utc>>,
pub author_association: String,
}

/// Commit Comparison
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommitComparison {
pub ahead_by: i64,
/// Commit
pub base_commit: Commit,
pub behind_by: i64,
pub commits: Vec<Commit>,
pub diff_url: String,
pub files: Option<Vec<CommitFile>>,
pub html_url: String,
/// Commit
pub merge_base_commit: Commit,
pub patch_url: String,
pub permalink_url: String,
pub status: GithubCommitStatus,
pub total_commits: i64,
pub url: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommitElement {
pub author: Option<GitUser>,
pub comment_count: i64,
pub committer: Option<GitUser>,
pub message: String,
pub tree: Tree,
pub url: String,
pub verification: Option<Verification>,
}

/// Metaproperties for Git author/committer information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GitUser {
pub date: Option<String>,
pub email: Option<String>,
pub name: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tree {
pub sha: String,
pub url: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Verification {
pub payload: Option<String>,
pub reason: String,
pub signature: Option<String>,
pub verified: bool,
}

/// Diff Entry
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FileStatus {
Added,
Changed,
Copied,
Modified,
Removed,
Renamed,
Unchanged,
}

/// Commit
/// Diff Entry
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommitFile {
pub additions: i64,
// unlike the schema online, this can be null
pub blob_url: Option<String>,
pub changes: i64,
pub contents_url: String,
pub deletions: i64,
pub filename: String,
pub patch: Option<String>,
pub previous_filename: Option<String>,
// unlike the schema online, this can be null
pub raw_url: Option<String>,
pub sha: String,
pub status: FileStatus,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommitParent {
pub html_url: Option<String>,
pub sha: String,
pub url: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommitStats {
pub additions: Option<i64>,
pub deletions: Option<i64>,
pub total: Option<i64>,
}

/// Commit
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Commit {
pub author: Option<Author>,
pub comments_url: String,
pub commit: CommitElement,
pub committer: Option<Author>,
pub files: Option<Vec<CommitFile>>,
pub html_url: String,
pub node_id: String,
pub parents: Vec<CommitParent>,
pub sha: String,
pub stats: Option<CommitStats>,
pub url: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GithubCommitStatus {
Ahead,
Behind,
Diverged,
Identical,
}
1 change: 1 addition & 0 deletions src/models/events/payload/workflow_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct WorkflowRunEventPayload {
#[non_exhaustive]
pub enum WorkflowRunEventAction {
Requested,
InProgress,
Completed,
}

Expand Down

0 comments on commit 6e91b68

Please sign in to comment.