diff --git a/src/api/repos/releases.rs b/src/api/repos/releases.rs index 9c84d99f..68ee4792 100644 --- a/src/api/repos/releases.rs +++ b/src/api/repos/releases.rs @@ -150,6 +150,26 @@ impl<'octo, 'r> ReleasesHandler<'octo, 'r> { self.parent.crab.get(route, None::<&()>).await } + /// Generates [`crate::models::repos::ReleaseNotes`] which describe + /// a [`crate::models::repos::Release`] + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// let release_notes = octocrab::instance() + /// .repos("owner", "repo") + /// .releases() + /// .generate_release_notes("0.1.0") + /// .send() + /// .await?; + /// # Ok(()) + /// # } + /// ``` + pub fn generate_release_notes<'tag_name>( + &self, + tag_name: &'tag_name (impl AsRef + ?Sized), + ) -> GenerateReleaseNotesBuilder<'_, '_, '_, 'tag_name, '_, '_, '_> { + GenerateReleaseNotesBuilder::new(self, tag_name.as_ref()) + } + /// Streams the binary contents of an asset. /// ```no_run /// # async fn run() -> octocrab::Result<()> { @@ -454,3 +474,103 @@ impl<'octo, 'repos, 'handler, 'tag_name, 'target_commitish, 'name, 'body> self.handler.parent.crab.patch(route, Some(&self)).await } } + +/// A builder pattern struct for updating releases. +/// +/// created by [`ReleasesHandler::generate_release_notes`]. +#[derive(serde::Serialize)] +pub struct GenerateReleaseNotesBuilder< + 'octo, + 'repos, + 'handler, + 'tag_name, + 'previous_tag_name, + 'target_commitish, + 'configuration_file_path, +> { + #[serde(skip)] + handler: &'handler ReleasesHandler<'octo, 'repos>, + tag_name: &'tag_name str, + #[serde(skip_serializing_if = "Option::is_none")] + previous_tag_name: Option<&'previous_tag_name str>, + #[serde(skip_serializing_if = "Option::is_none")] + target_commitish: Option<&'target_commitish str>, + #[serde(skip_serializing_if = "Option::is_none")] + configuration_file_path: Option<&'configuration_file_path str>, +} + +impl< + 'octo, + 'repos, + 'handler, + 'tag_name, + 'previous_tag_name, + 'target_commitish, + 'configuration_file_path, + > + GenerateReleaseNotesBuilder< + 'octo, + 'repos, + 'handler, + 'tag_name, + 'previous_tag_name, + 'target_commitish, + 'configuration_file_path, + > +{ + pub(crate) fn new( + handler: &'handler ReleasesHandler<'octo, 'repos>, + tag_name: &'tag_name str, + ) -> Self { + Self { + handler, + tag_name, + previous_tag_name: None, + target_commitish: None, + configuration_file_path: None, + } + } + + /// The tag which is used as a starting point for the release notes. + pub fn previous_tag_name( + mut self, + previous_tag_name: &'previous_tag_name (impl AsRef + ?Sized), + ) -> Self { + self.previous_tag_name = Some(previous_tag_name.as_ref()); + self + } + + /// Specifies the commitish value that determines where the Git tag is + /// created from. Can be any branch or commit SHA. + /// Unused if the Git [`GenerateReleaseNotesBuilder::tag_name`] exists. + pub fn target_commitish( + mut self, + target_commitish: &'target_commitish (impl AsRef + ?Sized), + ) -> Self { + self.target_commitish = Some(target_commitish.as_ref()); + self + } + + /// A file path within the repository which contains the configuration settings + /// for generating release notes. + pub fn configuration_file_path( + mut self, + configuration_file_path: &'configuration_file_path (impl AsRef + ?Sized), + ) -> Self { + self.configuration_file_path = Some(configuration_file_path.as_ref()); + self + } + + /// Sends the actual request. + pub async fn send(self) -> crate::Result { + let route = format!( + "/repos/{owner}/{repo}/releases/generate-notes", + owner = self.handler.parent.owner, + repo = self.handler.parent.repo, + ); + + let result: Result = + self.handler.parent.crab.post(route, Some(&self)).await; + return result; + } +} diff --git a/src/models/repos.rs b/src/models/repos.rs index 3a85f1e2..bf46c7ec 100644 --- a/src/models/repos.rs +++ b/src/models/repos.rs @@ -316,6 +316,13 @@ pub struct Release { pub assets: Vec, } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct ReleaseNotes { + pub name: String, + pub body: String, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub struct Asset { diff --git a/tests/generate_release_notes_test.rs b/tests/generate_release_notes_test.rs new file mode 100644 index 00000000..0e80d6f2 --- /dev/null +++ b/tests/generate_release_notes_test.rs @@ -0,0 +1,61 @@ +/// Tests generating release notes: +/// /repos/{owner}/{repo}/releases/generate-notes +mod mock_error; +use mock_error::setup_error_handler; +use octocrab::models::repos::ReleaseNotes; +use octocrab::Octocrab; +use wiremock::{ + matchers::{method, path}, + Mock, MockServer, ResponseTemplate, +}; + +async fn setup_api(template: ResponseTemplate) -> MockServer { + let mock_server = MockServer::start().await; + + let mocked_path = "/repos/owner/repo/releases/generate-notes"; + + Mock::given(method("POST")) + .and(path(mocked_path)) + .respond_with(template) + .mount(&mock_server) + .await; + setup_error_handler( + &mock_server, + &format!("POST on {mocked_path} was not received"), + ) + .await; + mock_server +} + +fn setup_octocrab(uri: &str) -> Octocrab { + Octocrab::builder().base_uri(uri).unwrap().build().unwrap() +} + +#[tokio::test] +async fn should_return_page_with_check_runs() { + let owner = "owner"; + let repo = "repo"; + let tag_name = "2.0.0"; + let mocked_response: ReleaseNotes = + serde_json::from_str(include_str!("resources/generate_release_notes.json")).unwrap(); + + let template = ResponseTemplate::new(200).set_body_json(&mocked_response); + let mock_server = setup_api(template).await; + let client = setup_octocrab(&mock_server.uri()); + let result = client + .repos(owner, repo) + .releases() + .generate_release_notes(tag_name) + .send() + .await; + + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); + + let response = result.unwrap(); + assert_eq!(response.name, tag_name); + assert_eq!(response.body.is_empty(), false); +} diff --git a/tests/resources/generate_release_notes.json b/tests/resources/generate_release_notes.json new file mode 100644 index 00000000..290d2a6b --- /dev/null +++ b/tests/resources/generate_release_notes.json @@ -0,0 +1,4 @@ +{ + "name": "2.0.0", + "body": "**Full Changelog**: https://github.com/XAMPPRocky/octocrab/compare/v0.34.0...v0.34.1" +} \ No newline at end of file