From ba0386e72c3d987096837ca5f3b7b34367320e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFs=20Postula?= Date: Wed, 24 Jul 2024 15:03:44 +0200 Subject: [PATCH] feat: implement release assets uploads --- src/api/repos/release_assets.rs | 3 +- src/api/repos/releases.rs | 87 +++++++++++++++++++++++++++++++++ src/params.rs | 1 + 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/api/repos/release_assets.rs b/src/api/repos/release_assets.rs index 4e5aad08..79d12b0a 100644 --- a/src/api/repos/release_assets.rs +++ b/src/api/repos/release_assets.rs @@ -95,13 +95,12 @@ impl<'octo, 'r> ReleaseAssetsHandler<'octo, 'r> { /// ``` #[cfg(feature = "stream")] #[cfg_attr(docsrs, doc(cfg(feature = "stream")))] - #[deprecated(note = "use repos::ReleaseAssetsHandler instrad")] pub async fn stream( &self, id: AssetId, ) -> crate::Result>> { use futures_util::TryStreamExt; - use snafu::GenerateImplicitData; + //use snafu::GenerateImplicitData; let route = format!( "/repos/{owner}/{repo}/releases/assets/{id}", diff --git a/src/api/repos/releases.rs b/src/api/repos/releases.rs index c8343200..e3e74e32 100644 --- a/src/api/repos/releases.rs +++ b/src/api/repos/releases.rs @@ -1,4 +1,6 @@ use super::*; +use crate::from_response::FromResponse; +use crate::{body::OctoBody, models::repos::Asset}; /// Handler for GitHub's releases API. /// @@ -184,6 +186,33 @@ impl<'octo, 'r> ReleasesHandler<'octo, 'r> { GenerateReleaseNotesBuilder::new(self, tag_name.as_ref()) } + /// Upload an [`crate::models::repos::Asset`] associated with + /// a [`crate::models::repos::Release`] + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// let file_path = std::path::Path::new("/tmp/my_asset.tar.gz"); + /// let file_size = unwrap!(std::fs::metadata(file_path)).len(); + /// let file = unwrap!(tokio::fs::File::open(file).await); + /// let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new()); + /// let asset = octocrab::instance() + /// .repos("owner", "repo") + /// .releases() + /// .upload_asset(1, "my_asset.tar.gz", stream) + /// .label("My Awesome Asset") + /// .send() + /// .await?; + /// # Ok(()) + /// # } + /// ``` + pub fn upload_asset<'asset_name>( + &self, + id: u64, + asset_name: &'asset_name (impl AsRef + ?Sized), + body: Bytes, + ) -> UploadAssetBuilder<'_, '_, '_, 'asset_name, '_> { + UploadAssetBuilder::new(self, id, asset_name.as_ref(), body) + } + /// Creates a new [`ListReleaseAssetsBuilder`] that can be configured to filter /// listing release assetss. /// ```no_run @@ -644,3 +673,61 @@ impl<'octo, 'r1, 'r2> ListReleaseAssetsBuilder<'octo, 'r1, 'r2> { self.handler.parent.crab.get(route, Some(&self)).await } } + +/// A builder pattern struct for updating release assets. +/// +/// created by [`ReleasesHandler::upload_asset`]. +pub struct UploadAssetBuilder<'octo, 'repos, 'handler, 'name, 'label> { + handler: &'handler ReleasesHandler<'octo, 'repos>, + release_id: u64, + name: &'name str, + body: Bytes, + label: Option<&'label str>, +} + +impl<'octo, 'repos, 'handler, 'name, 'label> + UploadAssetBuilder<'octo, 'repos, 'handler, 'name, 'label> +{ + pub(crate) fn new( + handler: &'handler ReleasesHandler<'octo, 'repos>, + release_id: u64, + name: &'name str, + body: Bytes, + ) -> Self { + Self { + handler, + release_id, + name, + body, + label: None, + } + } + + /// The asset label + pub fn label(mut self, label: &'label (impl AsRef + ?Sized)) -> Self { + self.label = Some(label.as_ref()); + self + } + + /// Sends the actual request. + pub async fn send(self) -> crate::Result { + // the url could be constructed without fetching the release, but if the user has no access to the release + // then he will not have access to upload to it. + let release = self.handler.get(self.release_id).await?; + + // Documentation tells us to take the `upload_url`, but `upload_url` is just `assets_url` with `{?name,label}`. + let mut url = release.assets_url.clone(); + url.query_pairs_mut().clear().append_pair("name", self.name); + if let Some(label) = self.label { + url.query_pairs_mut().append_pair("label", label); + } + let request = Builder::new() + .method(http::Method::POST) + .uri(url.to_string()) + .header(http::header::ACCEPT, "application/octet-stream") + .body(OctoBody::from(self.body)) + .context(HttpSnafu)?; + let response = self.handler.parent.crab.execute(request).await?; + Asset::from_response(crate::map_github_error(response).await?).await + } +} diff --git a/src/params.rs b/src/params.rs index 4c3c1cb3..8ab1760e 100644 --- a/src/params.rs +++ b/src/params.rs @@ -458,6 +458,7 @@ pub mod repos { pub enum State { Open, Uploaded, + Starter, } } }