Skip to content

Commit

Permalink
feat: implement hook deliveries
Browse files Browse the repository at this point in the history
  • Loading branch information
loispostula committed Jul 25, 2024
1 parent 88fabfd commit 337daec
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 1 deletion.
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 @@ -252,7 +252,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 @@ -1168,6 +1168,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>,
}

0 comments on commit 337daec

Please sign in to comment.