Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add generate-notes URL handler: #588

Merged
merged 6 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
}
Loading