Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add associated pull requests and commit compare functionality #413

Merged
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> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this endpoint accepts pagination params but doesn't return an array so I didn't wrap the return type in Page

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
Copy link
Contributor Author

@matthewgapp matthewgapp Jul 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These types were created automatically using the json schema here

The types were generated using https://quicktype.io/

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>,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discovered this while using the endpoints

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,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flyby fix

Completed,
}

Expand Down