diff --git a/src/api/repos/release_assets.rs b/src/api/repos/release_assets.rs index 79d12b0a..b97640c1 100644 --- a/src/api/repos/release_assets.rs +++ b/src/api/repos/release_assets.rs @@ -1,5 +1,4 @@ use super::*; -use crate::models::AssetId; /// Handler for GitHub's releases API. /// @@ -18,7 +17,7 @@ impl<'octo, 'r> ReleaseAssetsHandler<'octo, 'r> { /// let release = octocrab::instance() /// .repos("owner", "repo") /// .release_assets() - /// .get(AssetId(3)) + /// .get(3) /// .await?; /// # Ok(()) /// # } @@ -60,12 +59,12 @@ impl<'octo, 'r> ReleaseAssetsHandler<'octo, 'r> { /// let release = octocrab::instance() /// .repos("owner", "repo") /// .release_assets() - /// .delete(AssetId(3)) + /// .delete(3) /// .await?; /// # Ok(()) /// # } /// ``` - pub async fn delete(&self, id: AssetId) -> Result<()> { + pub async fn delete(&self, id: u64) -> Result<()> { let route = format!( "/repos/{owner}/{repo}/releases/assets/{id}", owner = self.parent.owner, @@ -73,7 +72,8 @@ impl<'octo, 'r> ReleaseAssetsHandler<'octo, 'r> { id = id, ); - self.parent.crab.delete(route, None::<&()>).await + self.parent.crab._delete(route, None::<&()>).await?; + Ok(()) } /// Streams the binary contents of an asset. @@ -97,7 +97,7 @@ impl<'octo, 'r> ReleaseAssetsHandler<'octo, 'r> { #[cfg_attr(docsrs, doc(cfg(feature = "stream")))] pub async fn stream( &self, - id: AssetId, + id: u64, ) -> crate::Result>> { use futures_util::TryStreamExt; //use snafu::GenerateImplicitData; @@ -132,6 +132,7 @@ impl<'octo, 'r> ReleaseAssetsHandler<'octo, 'r> { pub struct UpdateReleaseAssetBuilder<'octo, 'repos, 'handler, 'name, 'label> { #[serde(skip)] handler: &'handler ReleaseAssetsHandler<'octo, 'repos>, + #[serde(skip)] asset_id: u64, #[serde(skip_serializing_if = "Option::is_none")] name: Option<&'name str>, @@ -158,13 +159,13 @@ impl<'octo, 'repos, 'handler, 'name, 'label, 'state> } /// The name of the release asset. - pub fn tag_name(mut self, name: &'name (impl AsRef + ?Sized)) -> Self { + pub fn name(mut self, name: &'name (impl AsRef + ?Sized)) -> Self { self.name = Some(name.as_ref()); self } /// The label of the release asset. - pub fn name(mut self, label: &'label (impl AsRef + ?Sized)) -> Self { + pub fn label(mut self, label: &'label (impl AsRef + ?Sized)) -> Self { self.label = Some(label.as_ref()); self } @@ -181,7 +182,7 @@ impl<'octo, 'repos, 'handler, 'name, 'label, 'state> /// Sends the actual request. pub async fn send(self) -> crate::Result { let route = format!( - "/repos/{owner}/{repo}/releases/{asset_id}", + "/repos/{owner}/{repo}/releases/assets/{asset_id}", owner = self.handler.parent.owner, repo = self.handler.parent.repo, asset_id = self.asset_id, diff --git a/src/api/repos/releases.rs b/src/api/repos/releases.rs index e3e74e32..1b5e1070 100644 --- a/src/api/repos/releases.rs +++ b/src/api/repos/releases.rs @@ -1,6 +1,8 @@ use super::*; +use crate::error::{UriParseError, UriParseSnafu}; use crate::from_response::FromResponse; -use crate::{body::OctoBody, models::repos::Asset}; +use crate::models::repos::Asset; +use std::convert::TryInto; /// Handler for GitHub's releases API. /// @@ -189,15 +191,13 @@ impl<'octo, 'r> ReleasesHandler<'octo, 'r> { /// Upload an [`crate::models::repos::Asset`] associated with /// a [`crate::models::repos::Release`] /// ```no_run + /// use bytes::Bytes; /// # 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 file_data: Bytes = Bytes::from("some_data"); /// let asset = octocrab::instance() /// .repos("owner", "repo") /// .releases() - /// .upload_asset(1, "my_asset.tar.gz", stream) + /// .upload_asset(1, "my_asset.tar.gz", file_data) /// .label("My Awesome Asset") /// .send() /// .await?; @@ -220,7 +220,7 @@ impl<'octo, 'r> ReleasesHandler<'octo, 'r> { /// # let octocrab = octocrab::Octocrab::default(); /// let page = octocrab.repos("owner", "repo") /// .releases() - /// .assets() + /// .assets(1) /// // Optional Parameters /// .per_page(100) /// .page(5u32) @@ -230,8 +230,8 @@ impl<'octo, 'r> ReleasesHandler<'octo, 'r> { /// # Ok(()) /// # } /// ``` - pub fn assets(&self) -> ListReleaseAssetsBuilder<'_, '_, '_> { - ListReleaseAssetsBuilder::new(self) + pub fn assets(&self, release_id: u64) -> ListReleaseAssetsBuilder<'_, '_, '_> { + ListReleaseAssetsBuilder::new(self, release_id) } /// Streams the binary contents of an asset. @@ -256,10 +256,33 @@ impl<'octo, 'r> ReleasesHandler<'octo, 'r> { #[deprecated(note = "use repos::ReleaseAssetsHandler::stream instead")] pub async fn stream_asset( &self, - asset_id: AssetId, + asset_id: u64, ) -> crate::Result>> { self.parent.release_assets().stream(asset_id).await } + + /// Delete a release using its id. + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// let release = octocrab::instance() + /// .repos("owner", "repo") + /// .releases() + /// .delete(3) + /// .await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn delete(&self, id: u64) -> Result<()> { + let route = format!( + "/repos/{owner}/{repo}/releases/{id}", + owner = self.parent.owner, + repo = self.parent.repo, + id = id, + ); + + self.parent.crab._delete(route, None::<&()>).await?; + Ok(()) + } } /// A builder pattern struct for listing releases. @@ -636,6 +659,8 @@ impl< pub struct ListReleaseAssetsBuilder<'octo, 'r1, 'r2> { #[serde(skip)] handler: &'r2 ReleasesHandler<'octo, 'r1>, + #[serde(skip)] + release_id: u64, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -643,9 +668,10 @@ pub struct ListReleaseAssetsBuilder<'octo, 'r1, 'r2> { } impl<'octo, 'r1, 'r2> ListReleaseAssetsBuilder<'octo, 'r1, 'r2> { - pub(crate) fn new(handler: &'r2 ReleasesHandler<'octo, 'r1>) -> Self { + pub(crate) fn new(handler: &'r2 ReleasesHandler<'octo, 'r1>, release_id: u64) -> Self { Self { handler, + release_id, per_page: None, page: None, } @@ -666,9 +692,10 @@ impl<'octo, 'r1, 'r2> ListReleaseAssetsBuilder<'octo, 'r1, 'r2> { /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( - "/repos/{owner}/{repo}/releases/assets", + "/repos/{owner}/{repo}/releases/{release_id}/assets", owner = self.handler.parent.owner, - repo = self.handler.parent.repo + repo = self.handler.parent.repo, + release_id = self.release_id, ); self.handler.parent.crab.get(route, Some(&self)).await } @@ -715,17 +742,25 @@ impl<'octo, 'repos, 'handler, 'name, 'label> // 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); + let mut base_uri = format!( + "{}?name={}", + release.upload_url.replace("{?name,label}", ""), + self.name + ); if let Some(label) = self.label { - url.query_pairs_mut().append_pair("label", label); + base_uri = format!("{}&label={}", base_uri, label); } + + let url: Uri = base_uri + .try_into() + .map_err(|_| UriParseError {}) + .context(UriParseSnafu)?; let request = Builder::new() .method(http::Method::POST) - .uri(url.to_string()) - .header(http::header::ACCEPT, "application/octet-stream") - .body(OctoBody::from(self.body)) + .uri(url) + .header(http::header::CONTENT_TYPE, "application/octet-stream") + .header(http::header::CONTENT_LENGTH, self.body.len()) + .body(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/tests/repos_releases_list_test.rs b/tests/repos_releases_list_test.rs index 6242cf76..ceed7aa2 100644 --- a/tests/repos_releases_list_test.rs +++ b/tests/repos_releases_list_test.rs @@ -24,9 +24,7 @@ async fn setup_get_api(template: ResponseTemplate) -> MockServer { let mock_server = MockServer::start().await; Mock::given(method("GET")) - .and(path(format!( - "/repos/{OWNER}/{REPO}/releases" - ))) + .and(path(format!("/repos/{OWNER}/{REPO}/releases"))) .respond_with(template) .mount(&mock_server) .await;