Skip to content

Commit

Permalink
feat: Add generate_release_notes: (#588)
Browse files Browse the repository at this point in the history
* feat: Add generate-notes URL handler:

/repos/{owner}/{repo}/releases/generate-notes

* Update src/api/repos/releases.rs

* Fix the doc example for generate_release_notes

---------

Co-authored-by: Robin-Combrink <[email protected]>
Co-authored-by: XAMPPRocky <[email protected]>
  • Loading branch information
3 people committed Apr 9, 2024
1 parent 67c316f commit 7a5c5a5
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 0 deletions.
120 changes: 120 additions & 0 deletions src/api/repos/releases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<str> + ?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<()> {
Expand Down Expand Up @@ -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<str> + ?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<str> + ?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<str> + ?Sized),
) -> Self {
self.configuration_file_path = Some(configuration_file_path.as_ref());
self
}

/// Sends the actual request.
pub async fn send(self) -> crate::Result<crate::models::repos::ReleaseNotes> {
let route = format!(
"/repos/{owner}/{repo}/releases/generate-notes",
owner = self.handler.parent.owner,
repo = self.handler.parent.repo,
);

let result: Result<crate::models::repos::ReleaseNotes> =
self.handler.parent.crab.post(route, Some(&self)).await;
return result;
}
}
7 changes: 7 additions & 0 deletions src/models/repos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,13 @@ pub struct Release {
pub assets: Vec<Asset>,
}

#[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 {
Expand Down
61 changes: 61 additions & 0 deletions tests/generate_release_notes_test.rs
Original file line number Diff line number Diff line change
@@ -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);
}
4 changes: 4 additions & 0 deletions tests/resources/generate_release_notes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "2.0.0",
"body": "**Full Changelog**: https://github.com/XAMPPRocky/octocrab/compare/v0.34.0...v0.34.1"
}

0 comments on commit 7a5c5a5

Please sign in to comment.