use serde::{Deserialize, Serialize};
use super::encode_path;
use super::BugzillaClient;
use super::{UserSearchResponse, USER_FIELDS_BASIC, USER_FIELDS_DETAILED};
use crate::error::{BzrError, Result};
use crate::types::ApiMode;
#[derive(Serialize)]
struct GroupMembershipBody {
groups: GroupMembershipAction,
}
#[derive(Serialize)]
struct GroupMembershipAction {
#[serde(skip_serializing_if = "Vec::is_empty")]
add: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
remove: Vec<String>,
}
use crate::types::{BugzillaUser, CreateGroupParams, GroupInfo, UpdateGroupParams};
#[derive(Deserialize)]
struct GroupResponse {
groups: Vec<GroupInfo>,
}
impl BugzillaClient {
pub async fn get_group_members(
&self,
group_name: &str,
detailed: bool,
) -> Result<Vec<BugzillaUser>> {
let fields = if detailed {
USER_FIELDS_DETAILED
} else {
USER_FIELDS_BASIC
};
let data: UserSearchResponse = self
.get_json_query(
"user",
&[
("group", group_name),
("include_fields", fields),
("match", "*"),
],
)
.await?;
Ok(data.users)
}
pub async fn add_user_to_group(&self, user: &str, group: &str) -> Result<()> {
let body = GroupMembershipBody {
groups: GroupMembershipAction {
add: vec![group.to_string()],
remove: Vec::new(),
},
};
self.put_json(&format!("user/{}", encode_path(user)), &body)
.await
}
pub async fn remove_user_from_group(&self, user: &str, group: &str) -> Result<()> {
let body = GroupMembershipBody {
groups: GroupMembershipAction {
add: Vec::new(),
remove: vec![group.to_string()],
},
};
self.put_json(&format!("user/{}", encode_path(user)), &body)
.await
}
pub async fn get_group(&self, group: &str) -> Result<GroupInfo> {
match self.api_mode {
ApiMode::XmlRpc => return self.xmlrpc_client()?.get_group(group).await,
ApiMode::Rest | ApiMode::Hybrid => {}
}
match self.get_group_rest(group).await {
Ok(info) => Ok(info),
Err(BzrError::Api { code: 32610, .. }) => {
tracing::info!(
"REST Group.get blocked (32610), \
falling back to XML-RPC"
);
self.xmlrpc_client()?.get_group(group).await
}
Err(e) if self.api_mode == ApiMode::Hybrid && e.is_transport_failure() => {
tracing::info!(
"REST group lookup failed ({e}), \
retrying via XML-RPC"
);
self.xmlrpc_client()?.get_group(group).await
}
Err(e) => Err(e),
}
}
async fn get_group_rest(&self, group: &str) -> Result<GroupInfo> {
let req = self.apply_auth(
self.http
.get(self.url("group"))
.query(&[("names", group), ("membership", "1")]),
);
let resp = self.send(req).await?;
let data: GroupResponse = self.parse_json(resp).await?;
data.groups
.into_iter()
.next()
.ok_or_else(|| BzrError::NotFound {
resource: "group",
id: group.to_string(),
})
}
pub async fn create_group(&self, params: &CreateGroupParams) -> Result<u64> {
self.post_json_id("group", params).await
}
pub async fn update_group(&self, group: &str, updates: &UpdateGroupParams) -> Result<()> {
self.put_json(&format!("group/{}", encode_path(group)), updates)
.await
}
}
#[cfg(test)]
#[path = "group_tests.rs"]
mod tests;