From 2a715dcae086cfa253a277b796a605c9a68aa02f Mon Sep 17 00:00:00 2001 From: Aleksei Arsenev Date: Wed, 21 Jun 2023 19:37:48 +0300 Subject: [PATCH 1/4] added repo secrets api --- examples/create_repo_secret.rs | 40 +++++++ src/api/repos.rs | 7 ++ src/api/repos/secrets.rs | 157 ++++++++++++++++++++++++ src/models/repos.rs | 2 + src/models/repos/secrets.rs | 31 +++++ tests/repo_secrets_test.rs | 173 +++++++++++++++++++++++++++ tests/resources/repo_public_key.json | 4 + tests/resources/repo_secret.json | 5 + tests/resources/repo_secrets.json | 15 +++ 9 files changed, 434 insertions(+) create mode 100644 examples/create_repo_secret.rs create mode 100644 src/api/repos/secrets.rs create mode 100644 src/models/repos/secrets.rs create mode 100644 tests/repo_secrets_test.rs create mode 100644 tests/resources/repo_public_key.json create mode 100644 tests/resources/repo_secret.json create mode 100644 tests/resources/repo_secrets.json diff --git a/examples/create_repo_secret.rs b/examples/create_repo_secret.rs new file mode 100644 index 00000000..7bb2c3fe --- /dev/null +++ b/examples/create_repo_secret.rs @@ -0,0 +1,40 @@ +use base64::{engine::general_purpose::STANDARD as B64, Engine}; +use crypto_box::{self, aead::OsRng, PublicKey}; +use octocrab::{ + models::repos::secrets::CreateRepositorySecret, + Octocrab, +}; +use std::convert::TryInto; + +#[tokio::main] +async fn main() -> octocrab::Result<()> { + let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); + + let octocrab = Octocrab::builder().personal_token(token).build()?; + let repo = octocrab.repos("owner", "repo"); + let secrets = repo.secrets(); + + let public_key = secrets.get_public_key().await?; + + let crypto_pk = { + let pk_bytes = B64.decode(public_key.key).unwrap(); + let pk_array: [u8; crypto_box::KEY_SIZE] = pk_bytes.try_into().unwrap(); + PublicKey::from(pk_array) + }; + + let encrypted_value = crypto_box::seal(&mut OsRng, &crypto_pk, b"Very secret value").unwrap(); + + let result = secrets + .create_or_update_secret( + "TEST_SECRET_RS", + &CreateRepositorySecret { + encrypted_value: &B64.encode(encrypted_value), + key_id: &public_key.key_id, + }, + ) + .await?; + + println!("{:?}", result); + + Ok(()) +} diff --git a/src/api/repos.rs b/src/api/repos.rs index 6e29216e..c835e4a8 100644 --- a/src/api/repos.rs +++ b/src/api/repos.rs @@ -17,6 +17,7 @@ pub mod releases; mod stargazers; mod status; mod tags; +mod secrets; use crate::error::HttpSnafu; use crate::{models, params, Octocrab, Result}; @@ -30,6 +31,7 @@ pub use releases::ReleasesHandler; pub use stargazers::ListStarGazersBuilder; pub use status::{CreateStatusBuilder, ListStatusesBuilder}; pub use tags::ListTagsBuilder; +pub use secrets::RepoSecretsHandler; /// Handler for GitHub's repository API. /// @@ -588,4 +590,9 @@ impl<'octo> RepoHandler<'octo> { ) -> MergeBranchBuilder<'octo, '_> { MergeBranchBuilder::new(self, head, base) } + + /// Handle secrets on the repository + pub fn secrets(&self) -> RepoSecretsHandler<'_> { + RepoSecretsHandler::new(self) + } } diff --git a/src/api/repos/secrets.rs b/src/api/repos/secrets.rs new file mode 100644 index 00000000..93a9bfbf --- /dev/null +++ b/src/api/repos/secrets.rs @@ -0,0 +1,157 @@ +use http::StatusCode; +use snafu::GenerateImplicitData; + +use super::RepoHandler; +use crate::models::repos::secrets::{CreateRepositorySecret, CreateRepositorySecretResponse}; + +/// A client to GitHub's repository secrets API. +/// +/// Created with [`Octocrab::repos`]. +pub struct RepoSecretsHandler<'octo> { + repo: &'octo RepoHandler<'octo>, +} + +impl<'octo> RepoSecretsHandler<'octo> { + pub(crate) fn new(repo: &'octo RepoHandler<'octo>) -> Self { + Self { repo } + } + + /// Lists all secrets available in a repository without revealing their encrypted values. + /// You must authenticate using an access token with the `repo` scope to use this endpoint. + /// GitHub Apps must have the `secrets` repository permission to use this endpoint. + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// # let octocrab = octocrab::Octocrab::default(); + /// let repo = octocrab.repos("owner", "repo"); + /// let secrets = repo.secrets(); + /// let all_secrets = secrets.get_secrets().await?; + /// # Ok(()) + /// # } + pub async fn get_secrets( + &self, + ) -> crate::Result { + let route = format!("/repos/{owner}/{repo}/actions/secrets", owner = self.repo.owner, repo = self.repo.repo); + self.repo.crab.get(route, None::<&()>).await + } + + /// Gets your public key, which you need to encrypt secrets. + /// You need to encrypt a secret before you can create or update secrets. + /// Anyone with read access to the repository can use this endpoint. + /// If the repository is private you must use an access token with the `repo` scope. + /// GitHub Apps must have the `secrets` repository permission to use this endpoint. + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// # let octocrab = octocrab::Octocrab::default(); + /// let repo = octocrab.repos("owner", "repo"); + /// let secrets = repo.secrets(); + /// let public_key = secrets.get_public_key().await?; + /// # Ok(()) + /// # } + pub async fn get_public_key(&self) -> crate::Result { + let route = format!("/repos/{owner}/{repo}/actions/secrets/public-key", owner = self.repo.owner, repo = self.repo.repo); + self.repo.crab.get(route, None::<&()>).await + } + + /// Gets a single repository secret without revealing its encrypted value. + /// You must authenticate using an access token with the `repo` scope to use this endpoint. + /// GitHub Apps must have the `secrets` repository permission to use this endpoint. + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// # let octocrab = octocrab::Octocrab::default(); + /// let repo = octocrab.repos("owner", "repo"); + /// let secrets = repo.secrets(); + /// let secret_info = secrets.get_secret("TOKEN").await?; + /// # Ok(()) + /// # } + pub async fn get_secret( + &self, + secret_name: impl AsRef, + ) -> crate::Result { + let route = format!( + "/repos/{owner}/{repo}/actions/secrets/{secret_name}", + owner = self.repo.owner, + repo = self.repo.repo, + secret_name = secret_name.as_ref() + ); + self.repo.crab.get(route, None::<&()>).await + } + + /// Creates or updates a repository secret with an encrypted value. + /// Encrypt your secret using [`crypto_box`](https://crates.io/crates/crypto_box). + /// You must authenticate using an access token with the `repo` scope to use this endpoint. + /// GitHub Apps must have the `secrets` repository permission to use this endpoint. + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// # let octocrab = octocrab::Octocrab::default(); + /// use octocrab::models::repos::secrets::{CreateRepositorySecret, CreateRepositorySecretResponse}; + /// + /// let repo = octocrab.repos("owner", "repo"); + /// let secrets = repo.secrets(); + /// let result = secrets.create_or_update_secret("GH_TOKEN", &CreateRepositorySecret{ + /// key_id: "123456", + /// encrypted_value: "some-b64-encrypted-string", + /// }).await?; + /// + /// match result { + /// CreateRepositorySecretResponse::Created => println!("Created secret!"), + /// CreateRepositorySecretResponse::Updated => println!("Updated secret!"), + /// } + /// # Ok(()) + /// # } + pub async fn create_or_update_secret( + &self, + secret_name: impl AsRef, + secret: &CreateRepositorySecret<'_>, + ) -> crate::Result { + let route = format!( + "/repos/{owner}/{repo}/actions/secrets/{secret_name}", + owner = self.repo.owner, + repo = self.repo.repo, + secret_name = secret_name.as_ref() + ); + + let resp = { + let resp = self.repo.crab._put(route, Some(secret)).await?; + crate::map_github_error(resp).await? + }; + + match resp.status() { + StatusCode::CREATED => Ok(CreateRepositorySecretResponse::Created), + StatusCode::NO_CONTENT => Ok(CreateRepositorySecretResponse::Updated), + status_code => Err(crate::Error::Other { + source: format!( + "Unexpected status code from request: {}", + status_code.as_str() + ) + .into(), + backtrace: snafu::Backtrace::generate(), + }), + } + } + + /// Deletes a secret in an organization using the secret name. + /// You must authenticate using an access token with the `admin:org` scope to use this endpoint. + /// GitHub Apps must have the `secrets` organization permission to use this endpoint. + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// # let octocrab = octocrab::Octocrab::default(); + /// let repo = octocrab.repos("owner", "repo"); + /// let secrets = repo.secrets(); + /// + /// secrets.delete_secret("GH_TOKEN").await?; + /// + /// # Ok(()) + /// # } + pub async fn delete_secret(&self, secret_name: impl AsRef) -> crate::Result<()> { + let route = format!( + "/repos/{owner}/{repo}/actions/secrets/{secret_name}", + owner = self.repo.owner, + repo = self.repo.repo, + secret_name = secret_name.as_ref() + ); + + let resp = self.repo.crab._delete(route, None::<&()>).await?; + crate::map_github_error(resp).await?; + Ok(()) + } +} diff --git a/src/models/repos.rs b/src/models/repos.rs index 3b1c68e3..900a5c43 100644 --- a/src/models/repos.rs +++ b/src/models/repos.rs @@ -5,6 +5,8 @@ use hyper::{body, Response}; use snafu::ResultExt; use url::Url; +pub mod secrets; + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[non_exhaustive] diff --git a/src/models/repos/secrets.rs b/src/models/repos/secrets.rs new file mode 100644 index 00000000..34e4f399 --- /dev/null +++ b/src/models/repos/secrets.rs @@ -0,0 +1,31 @@ +use super::super::*; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct RepositorySecret { + pub name: String, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[non_exhaustive] +pub struct RepositorySecrets { + pub total_count: i32, + pub secrets: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub struct CreateRepositorySecret<'a> { + /// Value for your secret, + /// encrypted with LibSodium using the public key retrieved from the Get an organization public key endpoint. + pub encrypted_value: &'a str, + /// ID of the key you used to encrypt the secret. + pub key_id: &'a str, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum CreateRepositorySecretResponse { + Created, + Updated, +} diff --git a/tests/repo_secrets_test.rs b/tests/repo_secrets_test.rs new file mode 100644 index 00000000..5ca794bd --- /dev/null +++ b/tests/repo_secrets_test.rs @@ -0,0 +1,173 @@ +// Tests for calls to the /repos/{owner}/{repo}/actions/secrets API. +mod mock_error; + +use chrono::DateTime; +use mock_error::setup_error_handler; +use octocrab::{ + models::{ + repos::secrets::{ + CreateRepositorySecret, CreateRepositorySecretResponse, RepositorySecret, + RepositorySecrets + }, + PublicKey, + }, + Octocrab, +}; +use wiremock::{ + matchers::{method, path}, + Mock, MockServer, ResponseTemplate, +}; + +const OWNER: &str = "owner"; +const REPO: &str = "repo"; + +async fn setup_get_api(template: ResponseTemplate, secrets_path: &str) -> MockServer { + let mock_server = MockServer::start().await; + + Mock::given(method("GET")) + .and(path(format!("/repos/{OWNER}/{REPO}/actions/secrets{secrets_path}"))) + .respond_with(template) + .mount(&mock_server) + .await; + setup_error_handler( + &mock_server, + &format!("GET on /repos/{OWNER}/{REPO}/actions/secrets{secrets_path} was not received"), + ) + .await; + mock_server +} + +async fn setup_put_api(template: ResponseTemplate, secrets_path: &str) -> MockServer { + let mock_server = MockServer::start().await; + + Mock::given(method("PUT")) + .and(path(format!("/repos/{OWNER}/{REPO}/actions/secrets{secrets_path}"))) + .respond_with(template) + .mount(&mock_server) + .await; + setup_error_handler( + &mock_server, + &format!("PUT on /repos/{OWNER}/{REPO}/actions/secrets{secrets_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_repo_secrets() { + let repo_secrets: RepositorySecrets = + serde_json::from_str(include_str!("resources/repo_secrets.json")).unwrap(); + + let template = ResponseTemplate::new(200).set_body_json(&repo_secrets); + let mock_server = setup_get_api(template, "").await; + let client = setup_octocrab(&mock_server.uri()); + let repo = client.repos(OWNER.to_owned(), REPO.to_owned()); + let secrets = repo.secrets(); + let result = secrets.get_secrets().await; + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); + let item = result.unwrap(); + + assert_eq!(item.total_count, 2); + assert_eq!(item.secrets,vec![ + RepositorySecret { + name: String::from("GH_TOKEN"), + created_at: DateTime::parse_from_rfc3339("2019-08-10T14:59:22Z").unwrap().into(), + updated_at: DateTime::parse_from_rfc3339("2020-01-10T14:59:22Z").unwrap().into(), + }, + RepositorySecret { + name: String::from("GIST_ID"), + created_at: DateTime::parse_from_rfc3339("2020-01-10T10:59:22Z").unwrap().into(), + updated_at: DateTime::parse_from_rfc3339("2020-01-11T11:59:22Z").unwrap().into(), + }, + ] + ); +} + +#[tokio::test] +async fn should_return_repo_public_key() { + let repo_secrets: PublicKey = + serde_json::from_str(include_str!("resources/repo_public_key.json")).unwrap(); + + let template = ResponseTemplate::new(200).set_body_json(&repo_secrets); + let mock_server = setup_get_api(template, "/public-key").await; + let client = setup_octocrab(&mock_server.uri()); + let repo = client.repos(OWNER.to_owned(), REPO.to_owned()); + let secrets = repo.secrets(); + let result = secrets.get_public_key().await; + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); + let item = result.unwrap(); + + assert_eq!(item.key_id, String::from("012345678912345678")); + assert_eq!( + item.key, + String::from("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234") + ); +} + +#[tokio::test] +async fn should_return_repo_secret() { + let repo_secrets: RepositorySecret = + serde_json::from_str(include_str!("resources/repo_secret.json")).unwrap(); + + let template = ResponseTemplate::new(200).set_body_json(&repo_secrets); + let mock_server = setup_get_api(template, "/GH_TOKEN").await; + let client = setup_octocrab(&mock_server.uri()); + let repo = client.repos(OWNER.to_owned(), REPO.to_owned()); + let secrets = repo.secrets(); + let result = secrets.get_secret("GH_TOKEN").await; + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); + let item = result.unwrap(); + assert_eq!( + item, + RepositorySecret { + name: String::from("GH_TOKEN"), + created_at: DateTime::parse_from_rfc3339("2019-08-10T14:59:22Z") + .unwrap() + .into(), + updated_at: DateTime::parse_from_rfc3339("2020-01-10T14:59:22Z") + .unwrap() + .into(), + } + ); +} + +#[tokio::test] +async fn should_add_secret() { + let template = ResponseTemplate::new(201); + let mock_server = setup_put_api(template, "/GH_TOKEN").await; + let client = setup_octocrab(&mock_server.uri()); + let repo = client.repos(OWNER.to_owned(), REPO.to_owned()); + let secrets = repo.secrets(); + let result = secrets + .create_or_update_secret( + "GH_TOKEN", + &CreateRepositorySecret { + key_id: "123456", + encrypted_value: "some-b64-string", + }, + ) + .await; + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); + let item = result.unwrap(); + assert_eq!(item, CreateRepositorySecretResponse::Created); +} diff --git a/tests/resources/repo_public_key.json b/tests/resources/repo_public_key.json new file mode 100644 index 00000000..b0bac2f3 --- /dev/null +++ b/tests/resources/repo_public_key.json @@ -0,0 +1,4 @@ +{ + "key_id": "012345678912345678", + "key": "2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234" +} diff --git a/tests/resources/repo_secret.json b/tests/resources/repo_secret.json new file mode 100644 index 00000000..aad6fe1a --- /dev/null +++ b/tests/resources/repo_secret.json @@ -0,0 +1,5 @@ +{ + "name": "GH_TOKEN", + "created_at": "2019-08-10T14:59:22Z", + "updated_at": "2020-01-10T14:59:22Z" +} \ No newline at end of file diff --git a/tests/resources/repo_secrets.json b/tests/resources/repo_secrets.json new file mode 100644 index 00000000..3925180d --- /dev/null +++ b/tests/resources/repo_secrets.json @@ -0,0 +1,15 @@ +{ + "total_count": 2, + "secrets": [ + { + "name": "GH_TOKEN", + "created_at": "2019-08-10T14:59:22Z", + "updated_at": "2020-01-10T14:59:22Z" + }, + { + "name": "GIST_ID", + "created_at": "2020-01-10T10:59:22Z", + "updated_at": "2020-01-11T11:59:22Z" + } + ] +} \ No newline at end of file From 6413896811596fe70b1e60677a173d409ff49c89 Mon Sep 17 00:00:00 2001 From: alesharik Date: Thu, 22 Jun 2023 21:11:23 +0300 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: XAMPPRocky <4464295+XAMPPRocky@users.noreply.github.com> --- src/api/repos/secrets.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/api/repos/secrets.rs b/src/api/repos/secrets.rs index 93a9bfbf..269beda9 100644 --- a/src/api/repos/secrets.rs +++ b/src/api/repos/secrets.rs @@ -42,9 +42,10 @@ impl<'octo> RepoSecretsHandler<'octo> { /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); - /// let repo = octocrab.repos("owner", "repo"); - /// let secrets = repo.secrets(); - /// let public_key = secrets.get_public_key().await?; + /// let public_key = octocrab.repos("owner", "repo") + /// .secrets() + /// .get_public_key() + /// .await?; /// # Ok(()) /// # } pub async fn get_public_key(&self) -> crate::Result { @@ -58,9 +59,10 @@ impl<'octo> RepoSecretsHandler<'octo> { /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); - /// let repo = octocrab.repos("owner", "repo"); - /// let secrets = repo.secrets(); - /// let secret_info = secrets.get_secret("TOKEN").await?; + /// let secret_info = octocrab.repos("owner", "repo") + /// .secrets() + /// .get_secret("TOKEN") + /// .await?; /// # Ok(()) /// # } pub async fn get_secret( @@ -85,12 +87,13 @@ impl<'octo> RepoSecretsHandler<'octo> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::models::repos::secrets::{CreateRepositorySecret, CreateRepositorySecretResponse}; /// - /// let repo = octocrab.repos("owner", "repo"); - /// let secrets = repo.secrets(); - /// let result = secrets.create_or_update_secret("GH_TOKEN", &CreateRepositorySecret{ - /// key_id: "123456", - /// encrypted_value: "some-b64-encrypted-string", - /// }).await?; + /// let result = octocrab.repos("owner", "repo") + /// .secrets() + /// .create_or_update_secret("GH_TOKEN", &CreateRepositorySecret{ + /// key_id: "123456", + /// encrypted_value: "some-b64-encrypted-string", + /// }) + /// .await?; /// /// match result { /// CreateRepositorySecretResponse::Created => println!("Created secret!"), From b6253283cf3548f79067be5ca0ec37ffd967e2b7 Mon Sep 17 00:00:00 2001 From: Aleksei Arsenev Date: Thu, 22 Jun 2023 21:24:35 +0300 Subject: [PATCH 3/4] Also use method chaining in other places --- src/api/repos/secrets.rs | 15 ++++++++------- tests/repo_secrets_test.rs | 34 ++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/api/repos/secrets.rs b/src/api/repos/secrets.rs index 269beda9..68bf2f62 100644 --- a/src/api/repos/secrets.rs +++ b/src/api/repos/secrets.rs @@ -22,9 +22,10 @@ impl<'octo> RepoSecretsHandler<'octo> { /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); - /// let repo = octocrab.repos("owner", "repo"); - /// let secrets = repo.secrets(); - /// let all_secrets = secrets.get_secrets().await?; + /// let all_secrets = octocrab.repos("owner", "repo") + /// .secrets() + /// .get_secrets() + /// .await?; /// # Ok(()) /// # } pub async fn get_secrets( @@ -138,10 +139,10 @@ impl<'octo> RepoSecretsHandler<'octo> { /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); - /// let repo = octocrab.repos("owner", "repo"); - /// let secrets = repo.secrets(); - /// - /// secrets.delete_secret("GH_TOKEN").await?; + /// let repo = octocrab.repos("owner", "repo") + /// .secrets() + /// .delete_secret("GH_TOKEN") + /// .await?; /// /// # Ok(()) /// # } diff --git a/tests/repo_secrets_test.rs b/tests/repo_secrets_test.rs index 5ca794bd..d84bde0f 100644 --- a/tests/repo_secrets_test.rs +++ b/tests/repo_secrets_test.rs @@ -64,10 +64,11 @@ async fn should_return_repo_secrets() { let template = ResponseTemplate::new(200).set_body_json(&repo_secrets); let mock_server = setup_get_api(template, "").await; - let client = setup_octocrab(&mock_server.uri()); - let repo = client.repos(OWNER.to_owned(), REPO.to_owned()); - let secrets = repo.secrets(); - let result = secrets.get_secrets().await; + let result = setup_octocrab(&mock_server.uri()) + .repos(OWNER.to_owned(), REPO.to_owned()) + .secrets() + .get_secrets() + .await; assert!( result.is_ok(), "expected successful result, got error: {:#?}", @@ -98,10 +99,11 @@ async fn should_return_repo_public_key() { let template = ResponseTemplate::new(200).set_body_json(&repo_secrets); let mock_server = setup_get_api(template, "/public-key").await; - let client = setup_octocrab(&mock_server.uri()); - let repo = client.repos(OWNER.to_owned(), REPO.to_owned()); - let secrets = repo.secrets(); - let result = secrets.get_public_key().await; + let result = setup_octocrab(&mock_server.uri()) + .repos(OWNER.to_owned(), REPO.to_owned()) + .secrets() + .get_public_key() + .await; assert!( result.is_ok(), "expected successful result, got error: {:#?}", @@ -123,10 +125,11 @@ async fn should_return_repo_secret() { let template = ResponseTemplate::new(200).set_body_json(&repo_secrets); let mock_server = setup_get_api(template, "/GH_TOKEN").await; - let client = setup_octocrab(&mock_server.uri()); - let repo = client.repos(OWNER.to_owned(), REPO.to_owned()); - let secrets = repo.secrets(); - let result = secrets.get_secret("GH_TOKEN").await; + let result = setup_octocrab(&mock_server.uri()) + .repos(OWNER.to_owned(), REPO.to_owned()) + .secrets() + .get_secret("GH_TOKEN") + .await; assert!( result.is_ok(), "expected successful result, got error: {:#?}", @@ -151,10 +154,9 @@ async fn should_return_repo_secret() { async fn should_add_secret() { let template = ResponseTemplate::new(201); let mock_server = setup_put_api(template, "/GH_TOKEN").await; - let client = setup_octocrab(&mock_server.uri()); - let repo = client.repos(OWNER.to_owned(), REPO.to_owned()); - let secrets = repo.secrets(); - let result = secrets + let result = setup_octocrab(&mock_server.uri()) + .repos(OWNER.to_owned(), REPO.to_owned()) + .secrets() .create_or_update_secret( "GH_TOKEN", &CreateRepositorySecret { From f12c5ff14ddfe63c9e852ab4a56fd07ff4d265b8 Mon Sep 17 00:00:00 2001 From: Aleksei Arsenev Date: Thu, 22 Jun 2023 21:28:49 +0300 Subject: [PATCH 4/4] Reformat project --- examples/create_repo_secret.rs | 5 +---- src/api/repos.rs | 4 ++-- src/api/repos/secrets.rs | 14 +++++++++++--- tests/repo_secrets_test.rs | 30 ++++++++++++++++++++++-------- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/examples/create_repo_secret.rs b/examples/create_repo_secret.rs index 7bb2c3fe..e8e11264 100644 --- a/examples/create_repo_secret.rs +++ b/examples/create_repo_secret.rs @@ -1,9 +1,6 @@ use base64::{engine::general_purpose::STANDARD as B64, Engine}; use crypto_box::{self, aead::OsRng, PublicKey}; -use octocrab::{ - models::repos::secrets::CreateRepositorySecret, - Octocrab, -}; +use octocrab::{models::repos::secrets::CreateRepositorySecret, Octocrab}; use std::convert::TryInto; #[tokio::main] diff --git a/src/api/repos.rs b/src/api/repos.rs index c835e4a8..d014c7c9 100644 --- a/src/api/repos.rs +++ b/src/api/repos.rs @@ -14,10 +14,10 @@ mod generate; mod merges; mod pulls; pub mod releases; +mod secrets; mod stargazers; mod status; mod tags; -mod secrets; use crate::error::HttpSnafu; use crate::{models, params, Octocrab, Result}; @@ -28,10 +28,10 @@ pub use generate::GenerateRepositoryBuilder; pub use merges::MergeBranchBuilder; pub use pulls::ListPullsBuilder; pub use releases::ReleasesHandler; +pub use secrets::RepoSecretsHandler; pub use stargazers::ListStarGazersBuilder; pub use status::{CreateStatusBuilder, ListStatusesBuilder}; pub use tags::ListTagsBuilder; -pub use secrets::RepoSecretsHandler; /// Handler for GitHub's repository API. /// diff --git a/src/api/repos/secrets.rs b/src/api/repos/secrets.rs index 68bf2f62..71f8fb99 100644 --- a/src/api/repos/secrets.rs +++ b/src/api/repos/secrets.rs @@ -31,7 +31,11 @@ impl<'octo> RepoSecretsHandler<'octo> { pub async fn get_secrets( &self, ) -> crate::Result { - let route = format!("/repos/{owner}/{repo}/actions/secrets", owner = self.repo.owner, repo = self.repo.repo); + let route = format!( + "/repos/{owner}/{repo}/actions/secrets", + owner = self.repo.owner, + repo = self.repo.repo + ); self.repo.crab.get(route, None::<&()>).await } @@ -50,7 +54,11 @@ impl<'octo> RepoSecretsHandler<'octo> { /// # Ok(()) /// # } pub async fn get_public_key(&self) -> crate::Result { - let route = format!("/repos/{owner}/{repo}/actions/secrets/public-key", owner = self.repo.owner, repo = self.repo.repo); + let route = format!( + "/repos/{owner}/{repo}/actions/secrets/public-key", + owner = self.repo.owner, + repo = self.repo.repo + ); self.repo.crab.get(route, None::<&()>).await } @@ -127,7 +135,7 @@ impl<'octo> RepoSecretsHandler<'octo> { "Unexpected status code from request: {}", status_code.as_str() ) - .into(), + .into(), backtrace: snafu::Backtrace::generate(), }), } diff --git a/tests/repo_secrets_test.rs b/tests/repo_secrets_test.rs index d84bde0f..9e07a615 100644 --- a/tests/repo_secrets_test.rs +++ b/tests/repo_secrets_test.rs @@ -7,7 +7,7 @@ use octocrab::{ models::{ repos::secrets::{ CreateRepositorySecret, CreateRepositorySecretResponse, RepositorySecret, - RepositorySecrets + RepositorySecrets, }, PublicKey, }, @@ -25,7 +25,9 @@ async fn setup_get_api(template: ResponseTemplate, secrets_path: &str) -> MockSe let mock_server = MockServer::start().await; Mock::given(method("GET")) - .and(path(format!("/repos/{OWNER}/{REPO}/actions/secrets{secrets_path}"))) + .and(path(format!( + "/repos/{OWNER}/{REPO}/actions/secrets{secrets_path}" + ))) .respond_with(template) .mount(&mock_server) .await; @@ -41,7 +43,9 @@ async fn setup_put_api(template: ResponseTemplate, secrets_path: &str) -> MockSe let mock_server = MockServer::start().await; Mock::given(method("PUT")) - .and(path(format!("/repos/{OWNER}/{REPO}/actions/secrets{secrets_path}"))) + .and(path(format!( + "/repos/{OWNER}/{REPO}/actions/secrets{secrets_path}" + ))) .respond_with(template) .mount(&mock_server) .await; @@ -77,16 +81,26 @@ async fn should_return_repo_secrets() { let item = result.unwrap(); assert_eq!(item.total_count, 2); - assert_eq!(item.secrets,vec![ + assert_eq!( + item.secrets, + vec![ RepositorySecret { name: String::from("GH_TOKEN"), - created_at: DateTime::parse_from_rfc3339("2019-08-10T14:59:22Z").unwrap().into(), - updated_at: DateTime::parse_from_rfc3339("2020-01-10T14:59:22Z").unwrap().into(), + created_at: DateTime::parse_from_rfc3339("2019-08-10T14:59:22Z") + .unwrap() + .into(), + updated_at: DateTime::parse_from_rfc3339("2020-01-10T14:59:22Z") + .unwrap() + .into(), }, RepositorySecret { name: String::from("GIST_ID"), - created_at: DateTime::parse_from_rfc3339("2020-01-10T10:59:22Z").unwrap().into(), - updated_at: DateTime::parse_from_rfc3339("2020-01-11T11:59:22Z").unwrap().into(), + created_at: DateTime::parse_from_rfc3339("2020-01-10T10:59:22Z") + .unwrap() + .into(), + updated_at: DateTime::parse_from_rfc3339("2020-01-11T11:59:22Z") + .unwrap() + .into(), }, ] );