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

feat: implement hook deliveries #6

Merged
merged 1 commit into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod current;
pub mod events;
pub mod gists;
pub mod gitignore;
pub mod hooks;
pub mod issues;
pub mod licenses;
pub mod markdown;
Expand Down
69 changes: 69 additions & 0 deletions src/api/hooks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//! The hooks API.
use crate::{Octocrab};
use crate::models::{HookDeliveryId, HookId};

mod list_deliveries;
mod retry_delivery;

pub use self::{
list_deliveries::ListHooksDeliveriesBuilder,
retry_delivery::RetryDeliveryBuilder,
};

/// A client to GitHub's webhooks API.
///
/// Created with [`Octocrab::hooks`].
pub struct HooksHandler<'octo> {
crab: &'octo Octocrab,
owner: String,
repo: Option<String>,
}

impl<'octo> HooksHandler<'octo> {
pub(crate) fn new(crab: &'octo Octocrab, owner: String) -> Self {
Self {
crab,
owner,
repo: None,
}
}

pub fn repo(mut self, repo: String) -> Self {
self.repo = Some(repo);
self
}

/// Lists all of the `Delivery`s associated with the hook.
/// ```no_run
/// # async fn run() -> octocrab::Result<()> {
/// let reviews = octocrab::instance()
/// .hooks("owner")
/// //.repo("repo")
/// .list_deliveries(21u64.into())
/// .per_page(100)
/// .page(2u32)
/// .send()
/// .await?;
/// # Ok(())
/// # }
/// ```
pub fn list_deliveries(&self, hook_id: HookId) -> ListHooksDeliveriesBuilder<'_, '_> {
ListHooksDeliveriesBuilder::new(self, hook_id)
}

/// Retry a delivery.
/// ```no_run
/// # async fn run() -> octocrab::Result<()> {
/// let reviews = octocrab::instance()
/// .hooks("owner")
/// //.repo("repo")
/// .retry_delivery(20u64.into(), 21u64.into())
/// .send()
/// .await?;
/// # Ok(())
/// # }
/// ```
pub fn retry_delivery(&self, hook_id: HookId, delivery_id: HookDeliveryId) -> RetryDeliveryBuilder<'_, '_> {
RetryDeliveryBuilder::new(self, hook_id, delivery_id)
}
}
55 changes: 55 additions & 0 deletions src/api/hooks/list_deliveries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use super::*;

/// A builder pattern struct for listing hooks deliveries.
///
/// created by [`HooksHandler::list_deliveries`]
///
/// [`HooksHandler::list_deliveries`]: ./struct.HooksHandler.html#method.list_deliveries
#[derive(serde::Serialize)]
pub struct ListHooksDeliveriesBuilder<'octo, 'r> {
#[serde(skip)]
handler: &'r HooksHandler<'octo>,
#[serde(skip)]
hook_id: HookId,
#[serde(skip_serializing_if = "Option::is_none")]
per_page: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
page: Option<u32>,
}
impl<'octo, 'r> ListHooksDeliveriesBuilder<'octo, 'r> {
pub(crate) fn new(handler: &'r HooksHandler<'octo>, hook_id: HookId) -> Self {
Self {
handler,
hook_id,
per_page: None,
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
}

/// Send the actual request.
pub async fn send(self) -> crate::Result<crate::Page<crate::models::hooks::Delivery>> {
let route = match self.handler.repo.clone() {
Some(repo) => format!(
"/repos/{}/{}/hooks/{}/deliveries",
self.handler.owner, repo, self.hook_id
),
None => format!(
"/orgs/{}/hooks/{}/deliveries",
self.handler.owner, self.hook_id
),
};
self.handler.crab.get(route, Some(&self)).await
}
}
73 changes: 73 additions & 0 deletions src/api/hooks/retry_delivery.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use http::Uri;
use snafu::ResultExt;
use crate::error::HttpSnafu;
use super::*;

/// A builder pattern struct for listing hooks deliveries.
///
/// created by [`HooksHandler::retry_delivery`]
///
/// [`HooksHandler::retry_delivery`]: ./struct.HooksHandler.html#method.retry_delivery
#[derive(serde::Serialize)]
pub struct RetryDeliveryBuilder<'octo, 'r> {
#[serde(skip)]
handler: &'r HooksHandler<'octo>,
#[serde(skip)]
hook_id: HookId,
#[serde(skip)]
delivery_id: HookDeliveryId,
}
impl<'octo, 'r> RetryDeliveryBuilder<'octo, 'r> {
pub(crate) fn new(handler: &'r HooksHandler<'octo>, hook_id: HookId, delivery_id: HookDeliveryId) -> Self {
Self {
handler,
hook_id,
delivery_id
}
}

/// Send the actual request.
pub async fn send(self) -> crate::Result<()> {
let route= match self.handler.repo.clone() {
Some(repo) => format!("/repos/{}/{}/hooks/{}/deliveries/{}/attempts", self.handler.owner, repo, self.hook_id, self.delivery_id),
None => format!("/orgs/{}/hooks/{}/deliveries/{}/attempts", self.handler.owner, self.hook_id, self.delivery_id),
};

let uri = Uri::builder()
.path_and_query(route)
.build()
.context(HttpSnafu)?;
crate::map_github_error(self.handler.crab._post(uri, None::<&()>).await?)
.await
.map(drop)

}
}

#[cfg(test)]
mod tests {
// use crate::models::HookId;

// #[tokio::test]
// async fn serialize() {
// let octocrab = crate::Octocrab::default();
// let handler = octocrab.hooks("rust-lang");
// let list = handler
// .list_deliveries(HookId::from(21u64))
// .per_page(100)
// .page(1u8);
//
// assert_eq!(
// serde_json::to_value(list).unwrap(),
// serde_json::json!({
// "state": "open",
// "head": "master",
// "base": "branch",
// "sort": "popularity",
// "direction": "asc",
// "per_page": 100,
// "page": 1,
// })
// )
// }
}
7 changes: 6 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ use models::{AppId, InstallationId, InstallationToken};

pub use self::{
api::{
actions, activity, apps, checks, commits, current, events, gists, gitignore, issues,
actions, activity, apps, checks, commits, current, events, gists, gitignore, hooks, issues,
licenses, markdown, orgs, projects, pulls, ratelimit, repos, search, teams, workflows,
},
error::{Error, GitHubError},
Expand Down Expand Up @@ -1146,6 +1146,11 @@ impl Octocrab {
pub fn ratelimit(&self) -> ratelimit::RateLimitHandler {
ratelimit::RateLimitHandler::new(self)
}

/// Creates a [`hooks::HooksHandler`] that returns the API hooks
pub fn hooks(&self, owner: impl Into<String>) -> hooks::HooksHandler {
hooks::HooksHandler::new(self, owner.into())
}
}

/// # GraphQL API.
Expand Down
1 change: 1 addition & 0 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ id_type!(
IssueId,
JobId,
HookId,
HookDeliveryId,
LabelId,
MilestoneId,
NotificationId,
Expand Down
15 changes: 15 additions & 0 deletions src/models/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,18 @@ pub enum ContentType {
#[serde(untagged)]
Other(String),
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Delivery {
pub id: HookDeliveryId,
pub guid: String,
pub delivered_at: DateTime<Utc>,
pub duration: f64,
pub status: String,
pub status_code: usize,
pub event: Option<WebhookEventType>,
pub action: Option<String>,
pub installation_id: Option<InstallationId>,
pub repository_id: Option<InstallationId>,
}
Loading