Skip to content

Commit

Permalink
#552 (emails, public_emails, visibility) (#712)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmgorsky authored Oct 9, 2024
1 parent 89256f9 commit 6faf198
Show file tree
Hide file tree
Showing 6 changed files with 366 additions and 3 deletions.
46 changes: 43 additions & 3 deletions src/api/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ 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::api::users::user_emails::UserEmailsOpsBuilder;
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_emails;
mod user_repos;

pub(crate) enum UserRef {
Expand Down Expand Up @@ -141,4 +143,42 @@ 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<Vec<UserEmailInfo>> {
/// octocrab::instance()
/// .users("current_user")
/// .set_primary_email_visibility(Public) // or Private
/// .await
/// }
pub async fn set_primary_email_visibility(
&self,
visibility: EmailVisibilityState,
) -> crate::Result<Vec<crate::models::UserEmailInfo>> {
let route = String::from("/user/email/visibility");
let params = serde_json::json!({
"visibility": serde_json::to_string(&visibility).unwrap(),
});
self.crab.patch(route, Some(&params)).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)
}
}
151 changes: 151 additions & 0 deletions src/api/users/user_emails.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use crate::api::users::UserHandler;
use crate::models::UserEmailInfo;
use crate::{FromResponse, Page};

#[derive(serde::Serialize)]
pub struct UserEmailsOpsBuilder<'octo, 'b> {
#[serde(skip)]
handler: &'b UserHandler<'octo>,
#[serde(skip_serializing_if = "Option::is_none")]
per_page: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
page: Option<u32>,
}

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<u8>) -> Self {
self.per_page = Some(per_page.into());
self
}

/// Page number of the results to fetch.
pub fn page(mut self, page: impl Into<u32>) -> 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::{Page, Result};
/// async fn run() -> Result<Page<UserEmailInfo>> {
/// octocrab::instance()
/// .users("current_user")
/// .emails()
/// .per_page(42).page(3u32)
/// .list()
/// .await
/// }
pub async fn list(&self) -> crate::Result<Page<crate::models::UserEmailInfo>> {
let route = "/user/emails".to_string();
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::{Page, Result};
/// async fn run() -> Result<Page<UserEmailInfo>> {
/// octocrab::instance()
/// .users("current_user")
/// .emails()
/// .per_page(42).page(3u32)
/// .list_public()
/// .await
/// }
pub async fn list_public(&self) -> crate::Result<Page<crate::models::UserEmailInfo>> {
let route = "/user/public_emails".to_string();
self.handler.crab.get(route, Some(&self)).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<Vec<UserEmailInfo>> {
/// octocrab::instance()
/// .users("current_user")
/// .emails()
/// .add(vec!["[email protected]".to_string(), "[email protected]".to_string()])
/// .await
/// }
pub async fn add(
&self,
emails: Vec<String>,
) -> crate::Result<Vec<crate::models::UserEmailInfo>> {
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(&params)).await?;
if response.status() != http::StatusCode::CREATED {
return Err(crate::map_github_error(response).await.unwrap_err());
}

<Vec<UserEmailInfo>>::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!["[email protected]".to_string(), "[email protected]".to_string()])
/// .await
/// }
pub async fn delete(&self, emails: Vec<String>) -> 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(&params)).await?;
if response.status() != http::StatusCode::NO_CONTENT {
return Err(crate::map_github_error(response).await.unwrap_err());
}

Ok(())
}
}
9 changes: 9 additions & 0 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
}
13 changes: 13 additions & 0 deletions src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
}
8 changes: 8 additions & 0 deletions tests/resources/user_emails.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"email": "[email protected]",
"primary": true,
"verified": true,
"visibility": "private"
}
]
Loading

0 comments on commit 6faf198

Please sign in to comment.