use std::collections::HashMap;
use std::rc::Rc;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use reqwest::{get, header, Client, Method, Request, StatusCode, Url};
use serde::{Deserialize, Serialize};
use yup_oauth2::AccessToken;
use cio_api::{BuildingConfig, ResourceConfig, UserConfig};
const DIRECTORY_ENDPOINT: &str =
"https://www.googleapis.com/admin/directory/v1/";
const GROUPS_SETTINGS_ENDPOINT: &str =
"https://www.googleapis.com/groups/v1/groups/";
pub struct GSuite {
customer: String,
domain: String,
token: AccessToken,
client: Rc<Client>,
}
impl GSuite {
pub fn new(customer: &str, domain: &str, token: AccessToken) -> Self {
let client = Client::builder().build().expect("creating client failed");
Self {
customer: customer.to_string(),
domain: domain.to_string(),
token,
client: Rc::new(client),
}
}
pub fn get_token(&self) -> &AccessToken {
&self.token
}
fn request<B>(
&self,
endpoint: &str,
method: Method,
path: &str,
body: B,
query: Option<&[(&str, &str)]>,
) -> Request
where
B: Serialize,
{
let base = Url::parse(endpoint).unwrap();
let url = base.join(path).unwrap();
if self.token.is_expired() {
panic!("token is expired");
}
let bt = format!("Bearer {}", self.token.as_str());
let bearer = header::HeaderValue::from_str(&bt).unwrap();
let mut headers = header::HeaderMap::new();
headers.append(header::AUTHORIZATION, bearer);
headers.append(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"),
);
headers.append(
header::ACCEPT,
header::HeaderValue::from_static("application/json"),
);
let mut rb = self.client.request(method.clone(), url).headers(headers);
if let Some(val) = query {
rb = rb.query(&val);
}
if method != Method::GET && method != Method::DELETE {
rb = rb.json(&body);
}
rb.build().unwrap()
}
pub async fn list_groups(&self) -> Vec<Group> {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::GET,
"groups",
(),
Some(&[("customer", &self.customer), ("domain", &self.domain)]),
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
let value: Groups = resp.json().await.unwrap();
value.groups.unwrap()
}
pub async fn get_group_settings(&self, group_email: &str) -> GroupSettings {
let request = self.request(
GROUPS_SETTINGS_ENDPOINT,
Method::GET,
group_email,
(),
Some(&[("alt", "json")]),
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
resp.json().await.unwrap()
}
pub async fn update_group(&self, group: &Group) {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::PUT,
&format!("groups/{}", group.id.as_ref().unwrap()),
group,
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
}
pub async fn update_group_settings(&self, settings: &GroupSettings) {
let request = self.request(
GROUPS_SETTINGS_ENDPOINT,
Method::PUT,
settings.email.as_ref().unwrap(),
settings,
Some(&[("alt", "json")]),
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
}
pub async fn create_group(&self, group: &Group) -> Group {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::POST,
"groups",
group,
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
resp.json().await.unwrap()
}
pub async fn update_group_aliases<A>(&self, group_key: &str, aliases: A)
where
A: IntoIterator,
A::Item: AsRef<str>,
{
for alias in aliases {
self.update_group_alias(group_key, alias.as_ref()).await;
}
}
pub async fn update_group_alias(&self, group_key: &str, alias: &str) {
let mut a: HashMap<&str, &str> = HashMap::new();
a.insert("alias", alias);
let request = self.request(
DIRECTORY_ENDPOINT,
Method::POST,
&format!("groups/{}/aliases", group_key),
a,
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => {
let body = resp.text().await.unwrap();
if body.contains("duplicate") {
return;
}
panic!(
"received response status: {:?}\nresponse body: {:?}",
s, body,
);
}
};
}
pub async fn group_has_member(&self, group_id: &str, email: &str) -> bool {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::GET,
&format!("groups/{}/hasMember/{}", group_id, email),
(),
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
let value: MembersHasMember = resp.json().await.unwrap();
value.is_member.unwrap()
}
pub async fn group_update_member(
&self,
group_id: &str,
email: &str,
role: &str,
) {
let mut member: Member = Default::default();
member.role = Some(role.to_string());
member.email = Some(email.to_string());
member.delivery_settings = Some("ALL_MAIL".to_string());
let request = self.request(
DIRECTORY_ENDPOINT,
Method::PUT,
&format!("groups/{}/members/{}", group_id, email),
member,
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
}
pub async fn group_insert_member(
&self,
group_id: &str,
email: &str,
role: &str,
) {
let mut member: Member = Default::default();
member.role = Some(role.to_string());
member.email = Some(email.to_string());
member.delivery_settings = Some("ALL_MAIL".to_string());
let request = self.request(
DIRECTORY_ENDPOINT,
Method::POST,
&format!("groups/{}/members", group_id),
member,
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
}
pub async fn group_remove_member(&self, group_id: &str, email: &str) {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::DELETE,
&format!("groups/{}/members/{}", group_id, email),
(),
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
}
pub async fn list_users(&self) -> Vec<User> {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::GET,
"users",
(),
Some(&[
("customer", &self.customer),
("domain", &self.domain),
("projection", "full"),
]),
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
let value: Users = resp.json().await.unwrap();
value.users.unwrap()
}
pub async fn update_user(&self, user: &User) {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::PUT,
&format!("users/{}", user.id.as_ref().unwrap()),
user,
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
}
pub async fn create_user(&self, user: &User) -> User {
let request =
self.request(DIRECTORY_ENDPOINT, Method::POST, "users", user, None);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
resp.json().await.unwrap()
}
pub async fn update_user_aliases<A>(&self, user_id: &str, aliases: A)
where
A: IntoIterator,
A::Item: AsRef<str>,
{
for alias in aliases {
self.update_user_alias(user_id, alias.as_ref()).await;
}
}
pub async fn update_user_alias(&self, user_id: &str, alias: &str) {
let mut a: HashMap<&str, &str> = HashMap::new();
a.insert("alias", alias);
let request = self.request(
DIRECTORY_ENDPOINT,
Method::POST,
&format!("users/{}/aliases", user_id),
a,
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => {
let body = resp.text().await.unwrap();
if body.contains("duplicate") {
return;
}
panic!(
"received response status: {:?}\nresponse body: {:?}",
s, body,
);
}
};
}
pub async fn list_calendar_resources(&self) -> Vec<CalendarResource> {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::GET,
&format!("customer/{}/resources/calendars", self.customer),
(),
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
let value: CalendarResources = resp.json().await.unwrap();
value.items.unwrap()
}
pub async fn update_calendar_resource(&self, resource: &CalendarResource) {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::PUT,
&format!(
"customer/{}/resources/calendars/{}",
self.customer, resource.id
),
resource,
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
}
pub async fn create_calendar_resource(&self, resource: &CalendarResource) {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::POST,
&format!("customer/{}/resources/calendars", self.customer),
resource,
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
}
pub async fn list_buildings(&self) -> Vec<Building> {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::GET,
&format!("customer/{}/resources/buildings", self.customer),
(),
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
let value: Buildings = resp.json().await.unwrap();
value.buildings.unwrap()
}
pub async fn update_building(&self, building: &Building) {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::PUT,
&format!(
"customer/{}/resources/buildings/{}",
self.customer, building.id
),
building,
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
}
pub async fn create_building(&self, building: &Building) {
let request = self.request(
DIRECTORY_ENDPOINT,
Method::POST,
&format!("customer/{}/resources/buildings", self.customer),
building,
None,
);
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => panic!(
"received response status: {:?}\nbody: {}",
s,
resp.text().await.unwrap()
),
};
}
}
pub fn generate_password() -> String {
thread_rng().sample_iter(&Alphanumeric).take(30).collect()
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct Group {
#[serde(
skip_serializing_if = "Option::is_none",
rename = "nonEditableAliases"
)]
pub non_editable_aliases: Option<Vec<String>>,
pub kind: Option<String>,
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "adminCreated")]
pub admin_created: Option<bool>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "directMembersCount"
)]
pub direct_members_count: Option<String>,
pub email: Option<String>,
pub etag: Option<String>,
pub aliases: Option<Vec<String>>,
pub id: Option<String>,
pub name: Option<String>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct GroupSettings {
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanBanUsers"
)]
pub who_can_ban_users: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanAssistContent"
)]
pub who_can_assist_content: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "allowExternalMembers"
)]
pub allow_external_members: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanEnterFreeFormTags"
)]
pub who_can_enter_free_form_tags: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanApproveMessages"
)]
pub who_can_approve_messages: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanMarkDuplicate"
)]
pub who_can_mark_duplicate: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "whoCanJoin")]
pub who_can_join: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanModifyTagsAndCategories"
)]
pub who_can_modify_tags_and_categories: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanMarkNoResponseNeeded"
)]
pub who_can_mark_no_response_needed: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanUnmarkFavoriteReplyOnAnyTopic"
)]
pub who_can_unmark_favorite_reply_on_any_topic: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanModerateContent"
)]
pub who_can_moderate_content: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "primaryLanguage"
)]
pub primary_language: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanMarkFavoriteReplyOnOwnTopic"
)]
pub who_can_mark_favorite_reply_on_own_topic: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanViewMembership"
)]
pub who_can_view_membership: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "favoriteRepliesOnTop"
)]
pub favorite_replies_on_top: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanMarkFavoriteReplyOnAnyTopic"
)]
pub who_can_mark_favorite_reply_on_any_topic: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "includeCustomFooter"
)]
pub include_custom_footer: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanMoveTopicsOut"
)]
pub who_can_move_topics_out: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "defaultMessageDenyNotificationText"
)]
pub default_message_deny_notification_text: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "includeInGlobalAddressList"
)]
pub include_in_global_address_list: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "archiveOnly")]
pub archive_only: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanDeleteTopics"
)]
pub who_can_delete_topics: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanDeleteAnyPost"
)]
pub who_can_delete_any_post: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "isArchived")]
pub is_archived: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "membersCanPostAsTheGroup"
)]
pub members_can_post_as_the_group: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanMakeTopicsSticky"
)]
pub who_can_make_topics_sticky: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "customRolesEnabledForSettingsToBeMerged"
)]
pub custom_roles_enabled_for_settings_to_be_merged: Option<String>,
pub email: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanDiscoverGroup"
)]
pub who_can_discover_group: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanModifyMembers"
)]
pub who_can_modify_members: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "messageModerationLevel"
)]
pub message_moderation_level: Option<String>,
pub description: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanUnassignTopic"
)]
pub who_can_unassign_topic: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "replyTo")]
pub reply_to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "customReplyTo")]
pub custom_reply_to: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "sendMessageDenyNotification"
)]
pub send_message_deny_notification: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "enableCollaborativeInbox"
)]
pub enable_collaborative_inbox: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanContactOwner"
)]
pub who_can_contact_owner: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "messageDisplayFont"
)]
pub message_display_font: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanLeaveGroup"
)]
pub who_can_leave_group: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "whoCanAdd")]
pub who_can_add: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanPostMessage"
)]
pub who_can_post_message: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanMoveTopicsIn"
)]
pub who_can_move_topics_in: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanTakeTopics"
)]
pub who_can_take_topics: Option<String>,
pub name: Option<String>,
pub kind: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "maxMessageBytes"
)]
pub max_message_bytes: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none", rename = "whoCanInvite")]
pub who_can_invite: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanApproveMembers"
)]
pub who_can_approve_members: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "spamModerationLevel"
)]
pub spam_moderation_level: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "allowWebPosting"
)]
pub allow_web_posting: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanModerateMembers"
)]
pub who_can_moderate_members: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanAddReferences"
)]
pub who_can_add_references: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanViewGroup"
)]
pub who_can_view_group: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "showInGroupGSuite"
)]
pub show_in_group_directory: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanPostAnnouncements"
)]
pub who_can_post_announcements: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanLockTopics"
)]
pub who_can_lock_topics: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanAssignTopics"
)]
pub who_can_assign_topics: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "customFooterText"
)]
pub custom_footer_text: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "allowGoogleCommunication"
)]
pub allow_google_communication: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "whoCanHideAbuse"
)]
pub who_can_hide_abuse: Option<String>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
struct Groups {
#[serde(skip_serializing_if = "Option::is_none", rename = "nextPageToken")]
pub next_page_token: Option<String>,
pub kind: Option<String>,
pub etag: Option<String>,
pub groups: Option<Vec<Group>>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
struct MembersHasMember {
#[serde(skip_serializing_if = "Option::is_none", rename = "isMember")]
pub is_member: Option<bool>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
struct Members {
#[serde(skip_serializing_if = "Option::is_none", rename = "nextPageToken")]
pub next_page_token: Option<String>,
pub kind: Option<String>,
pub etag: Option<String>,
pub members: Option<Vec<Member>>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
struct Member {
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
pub delivery_settings: Option<String>,
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub etag: Option<String>,
pub role: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "type")]
pub type_: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct User {
#[serde(skip_serializing_if = "Option::is_none")]
pub addresses: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "posixAccounts")]
pub posix_accounts: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub phones: Option<Vec<UserPhone>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub locations: Option<Vec<UserLocation>>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "isDelegatedAdmin"
)]
pub is_delegated_admin: Option<bool>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "thumbnailPhotoEtag"
)]
pub thumbnail_photo_etag: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub suspended: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keywords: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub aliases: Option<Vec<String>>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "nonEditableAliases"
)]
pub non_editable_aliases: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub archived: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none", rename = "deletionTime")]
pub deletion_time: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "suspensionReason"
)]
pub suspension_reason: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "thumbnailPhotoUrl"
)]
pub thumbnail_photo_url: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "isEnrolledIn2Sv"
)]
pub is_enrolled_in2_sv: Option<bool>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "includeInGlobalAddressList"
)]
pub include_in_global_address_list: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub relations: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub languages: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "isAdmin")]
pub is_admin: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub etag: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "lastLoginTime")]
pub last_login_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "orgUnitPath")]
pub org_unit_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "agreedToTerms")]
pub agreed_to_terms: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none", rename = "externalIds")]
pub external_ids: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "ipWhitelisted")]
pub ip_whitelisted: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none", rename = "sshPublicKeys")]
pub ssh_public_keys: Option<Vec<UserSSHKey>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "customSchemas")]
pub custom_schemas: Option<HashMap<String, UserCustomProperties>>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "isEnforcedIn2Sv"
)]
pub is_enforced_in2_sv: Option<bool>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "isMailboxSetup"
)]
pub is_mailbox_setup: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub password: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub emails: Option<Vec<UserEmail>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub organizations: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "primaryEmail")]
pub primary_email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "hashFunction")]
pub hash_function: Option<String>,
pub name: Option<UserName>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gender: Option<UserGender>,
#[serde(skip_serializing_if = "Option::is_none")]
pub notes: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "creationTime")]
pub creation_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub websites: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "changePasswordAtNextLogin"
)]
pub change_password_at_next_login: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ims: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "customerId")]
pub customer_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "recoveryEmail")]
pub recovery_email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "recoveryPhone")]
pub recovery_phone: Option<String>,
}
impl User {
pub async fn update(
mut self,
user: &UserConfig,
domain: &str,
change_password: bool,
) -> User {
self.name = Some(UserName {
full_name: Some(format!("{} {}", user.first_name, user.last_name)),
given_name: Some(user.first_name.clone()),
family_name: Some(user.last_name.clone()),
});
if let Some(val) = &user.recovery_email {
self.recovery_email = Some(val.clone());
let mut has_home_email = false;
match self.emails {
Some(mut emails) => {
for (index, email) in emails.iter().enumerate() {
match &email.typev {
Some(typev) => {
if typev == "home" {
emails[index].address = val.clone();
has_home_email = true;
break;
}
}
None => (),
};
}
if !has_home_email {
emails.push(UserEmail {
typev: Some("home".to_string()),
address: val.to_string(),
primary: Some(false),
});
}
self.emails = Some(emails);
}
None => {
self.emails = Some(vec![UserEmail {
typev: Some("home".to_string()),
address: val.to_string(),
primary: Some(false),
}]);
}
}
} else {
self.recovery_email = None;
}
if let Some(val) = &user.recovery_phone {
self.recovery_phone = Some(val.to_string());
self.phones = Some(vec![UserPhone {
typev: "home".to_string(),
value: val.to_string(),
primary: true,
}]);
} else {
self.recovery_phone = None;
}
self.primary_email = Some(format!("{}@{}", user.username, domain));
let mut aliases: Vec<String> = Default::default();
for alias in user.aliases.as_ref().unwrap() {
aliases.push(format!("{}@{}", alias, domain));
}
self.aliases = Some(aliases);
if change_password {
self.change_password_at_next_login = Some(true);
let password = generate_password();
self.password = Some(password);
}
self.gender = if let Some(val) = &user.gender {
let mut gender: UserGender = Default::default();
gender.typev = val.to_string();
Some(gender)
} else {
None
};
self.locations = if let Some(val) = &user.building {
let mut location: UserLocation = Default::default();
location.typev = "desk".to_string();
location.building_id = Some(val.to_string());
location.floor_name = Some("1".to_string());
Some(vec![location])
} else {
None
};
let mut cs: HashMap<String, UserCustomProperties> = HashMap::new();
if let Some(val) = &user.github {
let mut gh: HashMap<String, String> = HashMap::new();
gh.insert("GitHub_Username".to_string(), val.clone());
cs.insert("Contact".to_string(), UserCustomProperties(Some(gh)));
let ssh_keys = get_github_user_public_ssh_keys(val).await;
self.ssh_public_keys = Some(ssh_keys);
}
if let Some(val) = &user.chat {
let mut chat: HashMap<String, String> = HashMap::new();
chat.insert("Matrix_Chat_Username".to_string(), val.clone());
cs.insert("Contact".to_string(), UserCustomProperties(Some(chat)));
}
self.custom_schemas = Some(cs);
self
}
}
async fn get_github_user_public_ssh_keys(handle: &str) -> Vec<UserSSHKey> {
let body = get(&format!("https://github.com/{}.keys", handle))
.await
.unwrap()
.text()
.await
.unwrap();
body.lines()
.filter_map(|key| {
let kt = key.trim();
if !kt.is_empty() {
Some(UserSSHKey {
key: kt.to_string(),
expiration_time_usec: None,
})
} else {
None
}
})
.collect()
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct UserEmail {
#[serde(skip_serializing_if = "Option::is_none", rename = "type")]
pub typev: Option<String>,
pub address: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub primary: Option<bool>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct UserPhone {
#[serde(rename = "type")]
pub typev: String,
pub value: String,
pub primary: bool,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct UserName {
#[serde(skip_serializing_if = "Option::is_none", rename = "fullName")]
pub full_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "givenName")]
pub given_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "familyName")]
pub family_name: Option<String>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct UserSSHKey {
pub key: String,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "expirationTimeUsec"
)]
pub expiration_time_usec: Option<i128>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct UserCustomProperties(pub Option<HashMap<String, String>>);
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
struct Users {
#[serde(skip_serializing_if = "Option::is_none", rename = "nextPageToken")]
pub next_page_token: Option<String>,
pub kind: Option<String>,
pub etag: Option<String>,
pub trigger_event: Option<String>,
pub users: Option<Vec<User>>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct UserLocation {
#[serde(rename = "type")]
pub typev: String,
pub area: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "buildingId")]
pub building_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "floorName")]
pub floor_name: Option<String>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct UserGender {
#[serde(rename = "type")]
pub typev: String,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct CalendarResource {
pub kind: Option<String>,
pub capacity: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none", rename = "resourceType")]
pub typev: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "resourceDescription"
)]
pub description: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "generatedResourceName"
)]
pub generated_resource_name: Option<String>,
pub etags: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "resourceCategory"
)]
pub category: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "resourceEmail")]
pub email: Option<String>,
#[serde(rename = "resourceName")]
pub name: String,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "featureInstances"
)]
pub feature_instances: Option<Vec<CalendarFeatures>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "floorSection")]
pub floor_section: Option<String>,
#[serde(rename = "resourceId")]
pub id: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "buildingId")]
pub building_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "floorName")]
pub floor_name: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "userVisibleDescription"
)]
pub user_visible_description: Option<String>,
}
impl CalendarResource {
pub fn update(
mut self,
resource: &ResourceConfig,
id: &str,
) -> CalendarResource {
self.id = id.to_string();
self.typev = Some(resource.typev.clone());
self.name = resource.name.clone();
self.building_id = Some(resource.building.clone());
self.description = Some(resource.description.clone());
self.user_visible_description = Some(resource.description.clone());
self.capacity = Some(resource.capacity);
self.floor_name = Some(resource.floor.clone());
self.floor_section = Some(resource.section.clone());
self.category = Some("CONFERENCE_ROOM".to_string());
self
}
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
struct CalendarResources {
#[serde(rename = "nextPageToken")]
pub next_page_token: Option<String>,
pub items: Option<Vec<CalendarResource>>,
pub kind: Option<String>,
pub etag: Option<String>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct CalendarFeature {
pub name: Option<String>,
pub kind: Option<String>,
pub etags: Option<String>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct CalendarFeatures {
pub feature: Option<CalendarFeature>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct Building {
pub kind: Option<String>,
#[serde(rename = "buildingName")]
pub name: String,
pub coordinates: Option<BuildingCoordinates>,
pub etags: Option<String>,
pub address: Option<BuildingAddress>,
#[serde(rename = "floorNames")]
pub floor_names: Option<Vec<String>>,
#[serde(rename = "buildingId")]
pub id: String,
pub description: Option<String>,
}
impl Building {
pub fn update(mut self, building: &BuildingConfig, id: &str) -> Building {
self.id = id.to_string();
self.name = building.name.clone();
self.description = Some(building.description.clone());
self.address = Some(BuildingAddress {
address_lines: Some(vec![building.address.clone()]),
locality: Some(building.city.clone()),
administrative_area: Some(building.state.clone()),
postal_code: Some(building.zipcode.clone()),
region_code: Some(building.country.clone()),
language_code: Some("en".to_string()),
sublocality: None,
});
self.floor_names = Some(building.floors.clone());
self
}
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
struct Buildings {
#[serde(rename = "nextPageToken")]
pub next_page_token: Option<String>,
pub buildings: Option<Vec<Building>>,
pub etag: Option<String>,
pub kind: Option<String>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct BuildingCoordinates {
pub latitude: Option<f64>,
pub longitude: Option<f64>,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct BuildingAddress {
#[serde(rename = "languageCode")]
pub language_code: Option<String>,
#[serde(rename = "administrativeArea")]
pub administrative_area: Option<String>,
#[serde(rename = "regionCode")]
pub region_code: Option<String>,
pub locality: Option<String>,
#[serde(rename = "postalCode")]
pub postal_code: Option<String>,
pub sublocality: Option<String>,
#[serde(rename = "addressLines")]
pub address_lines: Option<Vec<String>>,
}