Skip to main content

bitwarden_member_administration/
organization_users_management_client.rs

1use std::sync::Arc;
2
3use bitwarden_api_api::models::OrganizationUserBulkRequestModel;
4use bitwarden_core::{
5    ApiError, Client, FromClient, MissingFieldError, OrganizationId, client::ApiConfigurations,
6};
7use bitwarden_error::bitwarden_error;
8use bitwarden_organizations::OrganizationUserId;
9use thiserror::Error;
10
11use crate::OrganizationUserBulkResponse;
12
13/// Errors returned from [`OrganizationUsersManagementClient`] operations.
14#[bitwarden_error(flat)]
15#[derive(Debug, Error)]
16pub enum OrganizationUsersManagementError {
17    /// The request failed as a whole.
18    #[error(transparent)]
19    Api(#[from] ApiError),
20    /// A required field was missing from the server response.
21    #[error(transparent)]
22    MissingField(#[from] MissingFieldError),
23}
24
25/// Client for administering the members of an organization.
26#[bitwarden_ffi::wasm_object]
27#[derive(FromClient)]
28pub struct OrganizationUsersManagementClient {
29    pub(crate) api_configurations: Arc<ApiConfigurations>,
30}
31
32#[bitwarden_ffi::wasm_export]
33impl OrganizationUsersManagementClient {
34    /// Sends invites to the given staged members, promoting them to invited and consuming a seat.
35    ///
36    /// Returns an `Err` if the entire request fails. Otherwise returns `Ok` containing success or
37    /// failure information for each member.
38    pub async fn send_staged_invites(
39        &self,
40        organization_id: OrganizationId,
41        organization_user_ids: Vec<OrganizationUserId>,
42    ) -> Result<Vec<OrganizationUserBulkResponse>, OrganizationUsersManagementError> {
43        let response = self
44            .api_configurations
45            .api_client
46            .organization_users_api()
47            .send_invite_to_staged_users(
48                organization_id.into(),
49                Some(bulk_request(organization_user_ids)),
50            )
51            .await?;
52
53        // A missing list is treated as empty, matching how the clients parse list responses.
54        response
55            .data
56            .unwrap_or_default()
57            .into_iter()
58            .map(|row| OrganizationUserBulkResponse::try_from(row).map_err(Into::into))
59            .collect()
60    }
61
62    /// Re-sends the invitation email to the given invited members.
63    ///
64    /// Returns an `Err` if the entire request fails. Otherwise returns `Ok` containing success or
65    /// failure information for each member.
66    pub async fn bulk_reinvite(
67        &self,
68        organization_id: OrganizationId,
69        organization_user_ids: Vec<OrganizationUserId>,
70    ) -> Result<Vec<OrganizationUserBulkResponse>, OrganizationUsersManagementError> {
71        let response = self
72            .api_configurations
73            .api_client
74            .organization_users_api()
75            .bulk_reinvite(
76                organization_id.into(),
77                Some(bulk_request(organization_user_ids)),
78            )
79            .await?;
80
81        // A missing list is treated as empty, matching how the clients parse list responses.
82        response
83            .data
84            .unwrap_or_default()
85            .into_iter()
86            .map(|row| OrganizationUserBulkResponse::try_from(row).map_err(Into::into))
87            .collect()
88    }
89
90    /// Re-sends the invitation email to a single invited member.
91    pub async fn reinvite(
92        &self,
93        organization_id: OrganizationId,
94        organization_user_id: OrganizationUserId,
95    ) -> Result<(), OrganizationUsersManagementError> {
96        self.api_configurations
97            .api_client
98            .organization_users_api()
99            .reinvite(organization_id.into(), organization_user_id.into())
100            .await?;
101
102        Ok(())
103    }
104}
105
106/// Builds the request body shared by the bulk member endpoints.
107fn bulk_request(
108    organization_user_ids: Vec<OrganizationUserId>,
109) -> OrganizationUserBulkRequestModel {
110    OrganizationUserBulkRequestModel::new(
111        organization_user_ids.into_iter().map(Into::into).collect(),
112    )
113}
114
115/// Extension trait exposing [`OrganizationUsersManagementClient`] on [`Client`].
116pub trait OrganizationUsersManagementClientExt {
117    /// Organization member administration operations.
118    fn organization_users_management(&self) -> OrganizationUsersManagementClient;
119}
120
121impl OrganizationUsersManagementClientExt for Client {
122    fn organization_users_management(&self) -> OrganizationUsersManagementClient {
123        OrganizationUsersManagementClient::from_client(self)
124    }
125}