From 853846e1dc665f5f3cb990cb3506e79ad343806c Mon Sep 17 00:00:00 2001 From: Dmytro Horskyi Date: Sat, 5 Oct 2024 01:19:40 +0300 Subject: [PATCH 1/5] added set_email_visibility (PATCH /user/email/visibility) --- src/api/users.rs | 36 +++++++- src/models.rs | 9 ++ src/params.rs | 13 +++ tests/resources/user_emails.json | 8 ++ tests/user_emails_tests.rs | 140 +++++++++++++++++++++++++++++++ 5 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 tests/resources/user_emails.json create mode 100644 tests/user_emails_tests.rs diff --git a/src/api/users.rs b/src/api/users.rs index 94f154e7..21fdf4ab 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -5,13 +5,13 @@ use std::backtrace::Backtrace; use http::StatusCode; use snafu::GenerateImplicitData; +pub use self::follow::{ListUserFollowerBuilder, ListUserFollowingBuilder}; +use self::user_repos::ListUserReposBuilder; use crate::api::users::user_blocks::BlockedUsersBuilder; use crate::models::UserId; +use crate::params::users::emails::EmailVisibilityState; use crate::{error, GitHubError, Octocrab}; -pub use self::follow::{ListUserFollowerBuilder, ListUserFollowingBuilder}; -use self::user_repos::ListUserReposBuilder; - mod follow; mod user_blocks; mod user_repos; @@ -141,4 +141,34 @@ impl<'octo> UserHandler<'octo> { self.crab.delete(route, None::<&()>).await } + + ///## Set primary email visibility for the authenticated user + ///works with the following token types: + ///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app) + ///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) + /// + ///The fine-grained token must have the following permission set: + /// + ///"Email addresses" user permissions (write) + /// + ///```no_run + /// use octocrab::params::users::emails::EmailVisibilityState::*; + /// use octocrab::models::UserEmailInfo; + /// + /// async fn run() -> octocrab::Result<(UserEmailInfo)> { + /// octocrab::instance() + /// .users("current_user") + /// .set_email_visibility(Public) // or Private + /// .await + /// } + pub async fn set_email_visibility( + &self, + visibility: EmailVisibilityState, + ) -> crate::Result> { + let route = String::from("/user/email/visibility"); + let params = serde_json::json!({ + "visibility": serde_json::to_string(&visibility).unwrap(), + }); + self.crab.patch(route, Some(¶ms)).await + } } diff --git a/src/models.rs b/src/models.rs index 7d55c8be..687fbf17 100644 --- a/src/models.rs +++ b/src/models.rs @@ -8,6 +8,7 @@ use chrono::{DateTime, Utc}; use serde::{de, Deserialize, Deserializer, Serialize}; use url::Url; +use crate::params::users::emails::EmailVisibilityState; pub use apps::App; pub mod actions; @@ -1099,3 +1100,11 @@ pub struct Rate { pub remaining: usize, pub reset: u64, } + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UserEmailInfo { + pub email: String, + pub primary: bool, + pub verified: bool, + pub visibility: EmailVisibilityState, +} diff --git a/src/params.rs b/src/params.rs index 21acdf41..6bae8590 100644 --- a/src/params.rs +++ b/src/params.rs @@ -603,4 +603,17 @@ pub mod users { Member, } } + + pub mod emails { + use serde::{Deserialize, Serialize}; + + ///Denotes whether an email is publicly visible. + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Copy)] + #[serde(rename_all = "snake_case")] + #[non_exhaustive] + pub enum EmailVisibilityState { + Public, + Private, + } + } } diff --git a/tests/resources/user_emails.json b/tests/resources/user_emails.json new file mode 100644 index 00000000..75478793 --- /dev/null +++ b/tests/resources/user_emails.json @@ -0,0 +1,8 @@ +[ + { + "email": "octocat@github.com", + "primary": true, + "verified": true, + "visibility": "private" + } +] diff --git a/tests/user_emails_tests.rs b/tests/user_emails_tests.rs new file mode 100644 index 00000000..15055aa4 --- /dev/null +++ b/tests/user_emails_tests.rs @@ -0,0 +1,140 @@ +use serde::{Deserialize, Serialize}; +use wiremock::{ + matchers::{method, path}, + Mock, MockServer, ResponseTemplate, +}; + +use mock_error::setup_error_handler; +use octocrab::models::UserEmailInfo; +use octocrab::params::users::emails::EmailVisibilityState; +use octocrab::Octocrab; + +/// Tests API calls related to check runs of a specific commit. +mod mock_error; + +#[derive(Serialize, Deserialize)] +struct FakePage { + items: Vec, +} + +async fn setup_emails_mock( + http_method: &str, + mocked_path: &str, + template: ResponseTemplate, +) -> MockServer { + let mock_server = MockServer::start().await; + + Mock::given(method(http_method)) + .and(path(mocked_path)) + .respond_with(template.clone()) + .mount(&mock_server) + .await; + setup_error_handler( + &mock_server, + &format!("http method {http_method} 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_respond_to_email_visibility() { + let mocked_response: Vec = + serde_json::from_str(include_str!("resources/user_emails.json")).unwrap(); + let template = ResponseTemplate::new(200).set_body_json(&mocked_response); + let mock_server = setup_emails_mock("PATCH", "/user/email/visibility", template).await; + let client = setup_octocrab(&mock_server.uri()); + let result = client + .users("some_other_user") + .set_email_visibility(EmailVisibilityState::Private) + .await; + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); + let response = result.unwrap(); + let visibility = response.first().unwrap().visibility; + assert_eq!(visibility, EmailVisibilityState::Private); +} +/* +#[tokio::test] +async fn should_return_list_of_blocked_by_user() { + let mocked_response: Vec = + serde_json::from_str(include_str!("resources/user_blocks.json")).unwrap(); + let template = ResponseTemplate::new(200).set_body_json(&mocked_response); + let mock_server = setup_emails_mock("GET", "/user/blocks", template).await; + let client = setup_octocrab(&mock_server.uri()); + let result = client.users("some-user").blocks().per_page(10).list().await; + + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); + + let response = result.unwrap(); + let items = response.items; + + assert_eq!(items.len(), 1); + + { + let item = &items[0]; + + assert_eq!("octocat", item.login); + assert_eq!( + "https://api.github.com/users/octocat/received_events", + item.received_events_url.as_str() + ); + } +} + +#[tokio::test] +async fn should_check_if_user_blocked() { + /* status 204 for blocked */ + let template = ResponseTemplate::new(200); + let mock_server = setup_emails_mock( + "GET", + format!("/user/blocks/{}", NOT_BLOCKED).as_str(), + template, + ) + .await; + let client = setup_octocrab(&mock_server.uri()); + let result = client.users("some-user").is_blocked(NOT_BLOCKED).await; + assert!(!result.is_ok_and(|is_blocked| is_blocked)); +} + +#[tokio::test] +async fn should_respond_user_blocked() { + /* status 204 for blocked */ + let template = ResponseTemplate::new(204); + let mock_server = setup_emails_mock( + "PUT", + format!("/user/blocks/{}", NOT_BLOCKED).as_str(), + template, + ) + .await; + let client = setup_octocrab(&mock_server.uri()); + let result = client.users("some-user").block_user(NOT_BLOCKED).await; + assert!(result.is_ok()); +} + +#[tokio::test] +async fn should_respond_user_unblocked() { + /* status 204 for unblocked */ + let template = ResponseTemplate::new(200); + let mock_server = setup_emails_mock( + "DELETE", + format!("/user/blocks/{}", NOT_BLOCKED).as_str(), + template, + ) + .await; + let client = setup_octocrab(&mock_server.uri()); + let result = client.users("some-user").unblock_user(NOT_BLOCKED).await; + assert!(result.is_err()); +} +*/ From c1bd503d722fa03691cbd9d186946e38bdc627a7 Mon Sep 17 00:00:00 2001 From: Dmytro Horskyi Date: Tue, 8 Oct 2024 19:13:26 +0300 Subject: [PATCH 2/5] added emails list, add, delete (GET, POST, DELETE /user/email) --- src/api/users.rs | 46 ++++++++++++- src/api/users/user_emails.rs | 125 +++++++++++++++++++++++++++++++++++ tests/user_emails_tests.rs | 112 +++++++++++++------------------ 3 files changed, 214 insertions(+), 69 deletions(-) create mode 100644 src/api/users/user_emails.rs diff --git a/src/api/users.rs b/src/api/users.rs index 21fdf4ab..dc12a160 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -8,12 +8,14 @@ use snafu::GenerateImplicitData; pub use self::follow::{ListUserFollowerBuilder, ListUserFollowingBuilder}; use self::user_repos::ListUserReposBuilder; use crate::api::users::user_blocks::BlockedUsersBuilder; +use crate::api::users::user_emails::UserEmailsOpsBuilder; use crate::models::UserId; use crate::params::users::emails::EmailVisibilityState; use crate::{error, GitHubError, Octocrab}; mod follow; mod user_blocks; +mod user_emails; mod user_repos; pub(crate) enum UserRef { @@ -158,10 +160,50 @@ impl<'octo> UserHandler<'octo> { /// async fn run() -> octocrab::Result<(UserEmailInfo)> { /// octocrab::instance() /// .users("current_user") - /// .set_email_visibility(Public) // or Private + /// .set_primary_email_visibility(Public) // or Private /// .await /// } - pub async fn set_email_visibility( + pub async fn set_primary_email_visibility( + &self, + visibility: EmailVisibilityState, + ) -> crate::Result> { + let route = String::from("/user/email/visibility"); + let params = serde_json::json!({ + "visibility": serde_json::to_string(&visibility).unwrap(), + }); + self.crab.patch(route, Some(¶ms)).await + } + + ///Email addresses operations builder + ///* List email addresses for the authenticated user + ///* Add an email address for the authenticated user + ///* Delete an email address for the authenticated user + pub fn emails(&self) -> UserEmailsOpsBuilder<'_, '_> { + UserEmailsOpsBuilder::new(self) + } + + ///## List public email addresses for the authenticated user + ///Lists your publicly visible email address, which you can set with [set_email_visibility()] + ///OAuth app tokens and personal access tokens (classic) need the `user:email` scope + ///works with the following token types: + ///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app) + ///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) + /// + ///The fine-grained token must have the following permission set: + /// + ///"Email addresses" user permissions (read) + /// + ///```no_run + /// use octocrab::params::users::emails::EmailVisibilityState::*; + /// use octocrab::models::UserEmailInfo; + /// + /// async fn run() -> octocrab::Result<(UserEmailInfo)> { + /// octocrab::instance() + /// .users("current_user") + /// .set_primary_email_visibility(Public) // or Private + /// .await + /// } + pub async fn list_email_visibility2( &self, visibility: EmailVisibilityState, ) -> crate::Result> { diff --git a/src/api/users/user_emails.rs b/src/api/users/user_emails.rs new file mode 100644 index 00000000..442b9f9b --- /dev/null +++ b/src/api/users/user_emails.rs @@ -0,0 +1,125 @@ +use crate::api::users::UserHandler; +use crate::models::UserEmailInfo; +use crate::FromResponse; + +#[derive(serde::Serialize)] +pub struct UserEmailsOpsBuilder<'octo, 'b> { + #[serde(skip)] + handler: &'b UserHandler<'octo>, + #[serde(skip_serializing_if = "Option::is_none")] + per_page: Option, + #[serde(skip_serializing_if = "Option::is_none")] + page: Option, +} + +impl<'octo, 'b> UserEmailsOpsBuilder<'octo, 'b> { + pub(crate) fn new(handler: &'b UserHandler<'octo>) -> Self { + Self { + handler, + per_page: None, + page: None, + } + } + + /// Results per page (max 100). + pub fn per_page(mut self, per_page: impl Into) -> Self { + self.per_page = Some(per_page.into()); + self + } + + /// Page number of the results to fetch. + pub fn page(mut self, page: impl Into) -> Self { + self.page = Some(page.into()); + self + } + + ///## List email addresses for the authenticated user + ///works with the following token types: + ///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app) + ///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) + /// + ///The fine-grained token must have the following permission set: + ///* "Email addresses" user permissions (read) + /// + ///```no_run + /// use octocrab::models::UserEmailInfo; + /// use octocrab::Result; + /// async fn run() -> Result> { + /// octocrab::instance() + /// .users("current_user") + /// .emails() + /// .per_page(42).page(3u32) + /// .list() + /// .await? + /// } + pub async fn list(&self) -> crate::Result> { + let route = "/user/emails".to_string(); + self.handler.crab.get(route, None::<&()>).await + } + + ///## Add an email address(es) for the authenticated user + ///works with the following fine-grained token types: + ///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app) + ///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) + /// + ///The fine-grained token must have the following permission set: + ///* "Email addresses" user permissions (write) + /// + ///```no_run + /// use octocrab::models::UserEmailInfo; + /// use octocrab::Result; + /// async fn run() -> Result> { + /// octocrab::instance() + /// .users("current_user") + /// .emails() + /// .add(vec!["newemail1@mail.com".to_string(), "newemail2@mail.com".to_string()]) + /// .await? + /// } + pub async fn add( + &self, + emails: Vec, + ) -> crate::Result> { + let route = "/user/emails".to_string(); + + let params = serde_json::json!({ + "emails": serde_json::Value::from(emails), + }); + let response = self.handler.crab._post(route, Some(¶ms)).await?; + if response.status() != http::StatusCode::CREATED { + return Err(crate::map_github_error(response).await.unwrap_err()); + } + + >::from_response(crate::map_github_error(response).await?).await + } + + ///## Delete an email address(es) for the authenticated user + ///works with the following fine-grained token types: + ///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app) + ///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) + /// + ///The fine-grained token must have the following permission set: + ///* "Email addresses" user permissions (write) + /// + ///```no_run + /// use octocrab::Result; + /// async fn run() -> Result<()> { + /// octocrab::instance() + /// .users("current_user") + /// .emails() + /// .delete(vec!["newemail1@mail.com".to_string(), "newemail2@mail.com".to_string()]) + /// .await? + /// } + pub async fn delete(&self, emails: Vec) -> crate::Result<()> { + let route = "/user/emails".to_string(); + + let params = serde_json::json!({ + "emails": serde_json::Value::from(emails), + }); + let response = self.handler.crab._delete(route, Some(¶ms)).await?; + if response.status() != http::StatusCode::NO_CONTENT { + return Err(crate::map_github_error(response).await.unwrap_err()); + } + + Ok(()) + } +} diff --git a/tests/user_emails_tests.rs b/tests/user_emails_tests.rs index 15055aa4..c80512d7 100644 --- a/tests/user_emails_tests.rs +++ b/tests/user_emails_tests.rs @@ -1,4 +1,4 @@ -use serde::{Deserialize, Serialize}; +use http::StatusCode; use wiremock::{ matchers::{method, path}, Mock, MockServer, ResponseTemplate, @@ -12,11 +12,6 @@ use octocrab::Octocrab; /// Tests API calls related to check runs of a specific commit. mod mock_error; -#[derive(Serialize, Deserialize)] -struct FakePage { - items: Vec, -} - async fn setup_emails_mock( http_method: &str, mocked_path: &str, @@ -42,7 +37,7 @@ fn setup_octocrab(uri: &str) -> Octocrab { } #[tokio::test] -async fn should_respond_to_email_visibility() { +async fn should_respond_to_primary_email_visibility() { let mocked_response: Vec = serde_json::from_str(include_str!("resources/user_emails.json")).unwrap(); let template = ResponseTemplate::new(200).set_body_json(&mocked_response); @@ -50,7 +45,7 @@ async fn should_respond_to_email_visibility() { let client = setup_octocrab(&mock_server.uri()); let result = client .users("some_other_user") - .set_email_visibility(EmailVisibilityState::Private) + .set_primary_email_visibility(EmailVisibilityState::Private) .await; assert!( result.is_ok(), @@ -61,80 +56,63 @@ async fn should_respond_to_email_visibility() { let visibility = response.first().unwrap().visibility; assert_eq!(visibility, EmailVisibilityState::Private); } -/* + #[tokio::test] -async fn should_return_list_of_blocked_by_user() { - let mocked_response: Vec = - serde_json::from_str(include_str!("resources/user_blocks.json")).unwrap(); +async fn should_respond_to_email_list() { + let mocked_response: Vec = + serde_json::from_str(include_str!("resources/user_emails.json")).unwrap(); let template = ResponseTemplate::new(200).set_body_json(&mocked_response); - let mock_server = setup_emails_mock("GET", "/user/blocks", template).await; + let mock_server = setup_emails_mock("GET", "/user/emails", template).await; let client = setup_octocrab(&mock_server.uri()); - let result = client.users("some-user").blocks().per_page(10).list().await; - + let result = client + .users("some_other_user") + .emails() + .per_page(42) + .page(3u32) + .list() + .await; assert!( result.is_ok(), "expected successful result, got error: {:#?}", result ); - let response = result.unwrap(); - let items = response.items; - - assert_eq!(items.len(), 1); - - { - let item = &items[0]; - - assert_eq!("octocat", item.login); - assert_eq!( - "https://api.github.com/users/octocat/received_events", - item.received_events_url.as_str() - ); - } -} - -#[tokio::test] -async fn should_check_if_user_blocked() { - /* status 204 for blocked */ - let template = ResponseTemplate::new(200); - let mock_server = setup_emails_mock( - "GET", - format!("/user/blocks/{}", NOT_BLOCKED).as_str(), - template, - ) - .await; - let client = setup_octocrab(&mock_server.uri()); - let result = client.users("some-user").is_blocked(NOT_BLOCKED).await; - assert!(!result.is_ok_and(|is_blocked| is_blocked)); + let visibility = response.first().unwrap().visibility; + assert_eq!(visibility, EmailVisibilityState::Private); } #[tokio::test] -async fn should_respond_user_blocked() { - /* status 204 for blocked */ - let template = ResponseTemplate::new(204); - let mock_server = setup_emails_mock( - "PUT", - format!("/user/blocks/{}", NOT_BLOCKED).as_str(), - template, - ) - .await; +async fn should_respond_to_emails_add() { + let mocked_response: Vec = + serde_json::from_str(include_str!("resources/user_emails.json")).unwrap(); + let template = ResponseTemplate::new(StatusCode::CREATED).set_body_json(&mocked_response); + let mock_server = setup_emails_mock("POST", "/user/emails", template).await; let client = setup_octocrab(&mock_server.uri()); - let result = client.users("some-user").block_user(NOT_BLOCKED).await; - assert!(result.is_ok()); + let result = client + .users("some_other_user") + .emails() + .add(vec!["newemail1@mail.com".to_string()]) + .await; + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); } #[tokio::test] -async fn should_respond_user_unblocked() { - /* status 204 for unblocked */ - let template = ResponseTemplate::new(200); - let mock_server = setup_emails_mock( - "DELETE", - format!("/user/blocks/{}", NOT_BLOCKED).as_str(), - template, - ) - .await; +async fn should_respond_to_emails_delete() { + let template = ResponseTemplate::new(StatusCode::NO_CONTENT); + let mock_server = setup_emails_mock("DELETE", "/user/emails", template).await; let client = setup_octocrab(&mock_server.uri()); - let result = client.users("some-user").unblock_user(NOT_BLOCKED).await; - assert!(result.is_err()); + let result = client + .users("some_other_user") + .emails() + .delete(vec!["newemail1@mail.com".to_string()]) + .await; + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); } -*/ From e4b5fc559376297f6e9faf5ee1cbb16be327ba4b Mon Sep 17 00:00:00 2001 From: Dmytro Horskyi Date: Tue, 8 Oct 2024 19:23:28 +0300 Subject: [PATCH 3/5] added public emails list (GET /user/public_emails) --- src/api/users/user_emails.rs | 32 +++++++++++++++++++++++++++++--- tests/user_emails_tests.rs | 26 +++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/api/users/user_emails.rs b/src/api/users/user_emails.rs index 442b9f9b..dc32e186 100644 --- a/src/api/users/user_emails.rs +++ b/src/api/users/user_emails.rs @@ -1,6 +1,6 @@ use crate::api::users::UserHandler; use crate::models::UserEmailInfo; -use crate::FromResponse; +use crate::{FromResponse, Page}; #[derive(serde::Serialize)] pub struct UserEmailsOpsBuilder<'octo, 'b> { @@ -52,9 +52,35 @@ impl<'octo, 'b> UserEmailsOpsBuilder<'octo, 'b> { /// .list() /// .await? /// } - pub async fn list(&self) -> crate::Result> { + pub async fn list(&self) -> crate::Result> { let route = "/user/emails".to_string(); - self.handler.crab.get(route, None::<&()>).await + self.handler.crab.get(route, Some(&self)).await + } + + ///## List public email addresses for the authenticated user + /// Lists your publicly visible email address, which you can set with the `Set primary email visibility`. + ///works with the following token types: + ///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app) + ///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) + /// + ///The fine-grained token must have the following permission set: + ///* "Email addresses" user permissions (read) + /// This method can be used without authentication or the aforementioned permissions if only public resources are requested. + /// + ///```no_run + /// use octocrab::models::UserEmailInfo; + /// use octocrab::Result; + /// async fn run() -> Result> { + /// octocrab::instance() + /// .users("current_user") + /// .emails() + /// .per_page(42).page(3u32) + /// .list_public() + /// .await? + /// } + pub async fn list_public(&self) -> crate::Result> { + let route = "/user/public_emails".to_string(); + self.handler.crab.get(route, Some(&self)).await } ///## Add an email address(es) for the authenticated user diff --git a/tests/user_emails_tests.rs b/tests/user_emails_tests.rs index c80512d7..baa78bca 100644 --- a/tests/user_emails_tests.rs +++ b/tests/user_emails_tests.rs @@ -77,7 +77,31 @@ async fn should_respond_to_email_list() { result ); let response = result.unwrap(); - let visibility = response.first().unwrap().visibility; + let visibility = response.items.first().unwrap().visibility; + assert_eq!(visibility, EmailVisibilityState::Private); +} + +#[tokio::test] +async fn should_respond_to_public_email_list() { + let mocked_response: Vec = + serde_json::from_str(include_str!("resources/user_emails.json")).unwrap(); + let template = ResponseTemplate::new(200).set_body_json(&mocked_response); + let mock_server = setup_emails_mock("GET", "/user/public_emails", template).await; + let client = setup_octocrab(&mock_server.uri()); + let result = client + .users("some_other_user") + .emails() + .per_page(42) + .page(3u32) + .list_public() + .await; + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); + let response = result.unwrap(); + let visibility = response.items.first().unwrap().visibility; assert_eq!(visibility, EmailVisibilityState::Private); } From df9b739e244a8861b00fa3523937424061ef94fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20P=C3=BCschel?= Date: Sun, 6 Oct 2024 19:29:46 +0200 Subject: [PATCH 4/5] fix: don't capture backtraces by default (#710) --- src/api/orgs/secrets.rs | 2 +- src/api/repos/secrets.rs | 2 +- src/api/users.rs | 2 +- src/body.rs | 2 +- src/etag.rs | 2 +- src/lib.rs | 8 ++++---- src/page.rs | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/api/orgs/secrets.rs b/src/api/orgs/secrets.rs index e3243c57..0c30048d 100644 --- a/src/api/orgs/secrets.rs +++ b/src/api/orgs/secrets.rs @@ -129,7 +129,7 @@ impl<'octo> OrgSecretsHandler<'octo> { status_code.as_str() ) .into(), - backtrace: snafu::Backtrace::generate(), + backtrace: snafu::Backtrace::capture(), }), } } diff --git a/src/api/repos/secrets.rs b/src/api/repos/secrets.rs index 94f127e0..e95b66cc 100644 --- a/src/api/repos/secrets.rs +++ b/src/api/repos/secrets.rs @@ -126,7 +126,7 @@ impl<'octo> RepoSecretsHandler<'octo> { status_code.as_str() ) .into(), - backtrace: snafu::Backtrace::generate(), + backtrace: snafu::Backtrace::capture(), }), } } diff --git a/src/api/users.rs b/src/api/users.rs index dc12a160..e8c61160 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -118,7 +118,7 @@ impl<'octo> UserHandler<'octo> { errors: None, message: "".to_string(), }, - backtrace: Backtrace::generate(), + backtrace: Backtrace::capture(), }), Err(_v) => Ok(()), } diff --git a/src/body.rs b/src/body.rs index 245310a3..d5f5c3d6 100644 --- a/src/body.rs +++ b/src/body.rs @@ -18,7 +18,7 @@ where try_downcast(body).unwrap_or_else(|body| { body.map_err(|e| crate::Error::Other { source: e.into(), - backtrace: Backtrace::generate(), + backtrace: Backtrace::capture(), }) .boxed() }) diff --git a/src/etag.rs b/src/etag.rs index 533811b1..5ef79d6a 100644 --- a/src/etag.rs +++ b/src/etag.rs @@ -82,7 +82,7 @@ impl EntityTag { .parse() .map_err(|err: InvalidHeaderValue| crate::Error::InvalidHeaderValue { source: err, - backtrace: snafu::Backtrace::generate(), + backtrace: snafu::Backtrace::capture(), })?, ); Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 82b7981c..4a5cf766 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -329,7 +329,7 @@ pub async fn map_github_error( errors, message, }, - backtrace: Backtrace::generate(), + backtrace: Backtrace::capture(), }) } } @@ -1015,7 +1015,7 @@ impl Octocrab { app_auth.clone() } else { return Err(Error::Installation { - backtrace: Backtrace::generate(), + backtrace: Backtrace::capture(), }); }; Ok(Octocrab { @@ -1502,7 +1502,7 @@ impl Octocrab { (app, installation, token) } else { return Err(Error::Installation { - backtrace: Backtrace::generate(), + backtrace: Backtrace::capture(), }); }; let mut request = Builder::new(); @@ -1534,7 +1534,7 @@ impl Octocrab { .map(|time| { DateTime::::from_str(&time).map_err(|e| error::Error::Other { source: Box::new(e), - backtrace: snafu::Backtrace::generate(), + backtrace: snafu::Backtrace::capture(), }) }) .transpose()?; diff --git a/src/page.rs b/src/page.rs index 68d0e1ca..75278f60 100644 --- a/src/page.rs +++ b/src/page.rs @@ -242,7 +242,7 @@ fn get_links(headers: &http::header::HeaderMap) -> crate::Result { if let Some(link) = headers.get("Link") { let links = link.to_str().map_err(|err| crate::Error::Other { source: Box::new(err), - backtrace: snafu::Backtrace::generate(), + backtrace: snafu::Backtrace::capture(), })?; for url_with_params in links.split(',') { From fd7d399123a579ed21492b05c350cdc90aa5bd20 Mon Sep 17 00:00:00 2001 From: Dmytro Horskyi Date: Tue, 8 Oct 2024 19:42:34 +0300 Subject: [PATCH 5/5] fixed ci/cd --- src/api/users.rs | 34 +--------------------------------- src/api/users/user_emails.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 41 deletions(-) diff --git a/src/api/users.rs b/src/api/users.rs index e8c61160..cda84b7d 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -157,7 +157,7 @@ impl<'octo> UserHandler<'octo> { /// use octocrab::params::users::emails::EmailVisibilityState::*; /// use octocrab::models::UserEmailInfo; /// - /// async fn run() -> octocrab::Result<(UserEmailInfo)> { + /// async fn run() -> octocrab::Result> { /// octocrab::instance() /// .users("current_user") /// .set_primary_email_visibility(Public) // or Private @@ -181,36 +181,4 @@ impl<'octo> UserHandler<'octo> { pub fn emails(&self) -> UserEmailsOpsBuilder<'_, '_> { UserEmailsOpsBuilder::new(self) } - - ///## List public email addresses for the authenticated user - ///Lists your publicly visible email address, which you can set with [set_email_visibility()] - ///OAuth app tokens and personal access tokens (classic) need the `user:email` scope - ///works with the following token types: - ///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app) - ///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) - /// - ///The fine-grained token must have the following permission set: - /// - ///"Email addresses" user permissions (read) - /// - ///```no_run - /// use octocrab::params::users::emails::EmailVisibilityState::*; - /// use octocrab::models::UserEmailInfo; - /// - /// async fn run() -> octocrab::Result<(UserEmailInfo)> { - /// octocrab::instance() - /// .users("current_user") - /// .set_primary_email_visibility(Public) // or Private - /// .await - /// } - pub async fn list_email_visibility2( - &self, - visibility: EmailVisibilityState, - ) -> crate::Result> { - let route = String::from("/user/email/visibility"); - let params = serde_json::json!({ - "visibility": serde_json::to_string(&visibility).unwrap(), - }); - self.crab.patch(route, Some(¶ms)).await - } } diff --git a/src/api/users/user_emails.rs b/src/api/users/user_emails.rs index dc32e186..b964790f 100644 --- a/src/api/users/user_emails.rs +++ b/src/api/users/user_emails.rs @@ -43,14 +43,14 @@ impl<'octo, 'b> UserEmailsOpsBuilder<'octo, 'b> { /// ///```no_run /// use octocrab::models::UserEmailInfo; - /// use octocrab::Result; - /// async fn run() -> Result> { + /// use octocrab::{Page, Result}; + /// async fn run() -> Result> { /// octocrab::instance() /// .users("current_user") /// .emails() /// .per_page(42).page(3u32) /// .list() - /// .await? + /// .await /// } pub async fn list(&self) -> crate::Result> { let route = "/user/emails".to_string(); @@ -69,14 +69,14 @@ impl<'octo, 'b> UserEmailsOpsBuilder<'octo, 'b> { /// ///```no_run /// use octocrab::models::UserEmailInfo; - /// use octocrab::Result; - /// async fn run() -> Result> { + /// use octocrab::{Page, Result}; + /// async fn run() -> Result> { /// octocrab::instance() /// .users("current_user") /// .emails() /// .per_page(42).page(3u32) /// .list_public() - /// .await? + /// .await /// } pub async fn list_public(&self) -> crate::Result> { let route = "/user/public_emails".to_string(); @@ -99,7 +99,7 @@ impl<'octo, 'b> UserEmailsOpsBuilder<'octo, 'b> { /// .users("current_user") /// .emails() /// .add(vec!["newemail1@mail.com".to_string(), "newemail2@mail.com".to_string()]) - /// .await? + /// .await /// } pub async fn add( &self, @@ -133,7 +133,7 @@ impl<'octo, 'b> UserEmailsOpsBuilder<'octo, 'b> { /// .users("current_user") /// .emails() /// .delete(vec!["newemail1@mail.com".to_string(), "newemail2@mail.com".to_string()]) - /// .await? + /// .await /// } pub async fn delete(&self, emails: Vec) -> crate::Result<()> { let route = "/user/emails".to_string();