bitwarden_organization_domains/
organization_domains_client.rs1use std::sync::Arc;
2
3use bitwarden_core::{
4 ApiError, Client, FromClient, MissingFieldError, OrganizationId, client::ApiConfigurations,
5 require,
6};
7use bitwarden_error::bitwarden_error;
8use thiserror::Error;
9
10#[bitwarden_error(flat)]
12#[derive(Debug, Error)]
13pub enum OrganizationDomainsError {
14 #[error(transparent)]
16 Api(#[from] ApiError),
17 #[error(transparent)]
19 MissingField(#[from] MissingFieldError),
20}
21
22#[bitwarden_ffi::wasm_object]
24#[derive(FromClient)]
25pub struct OrganizationDomainsClient {
26 pub(crate) api_configurations: Arc<ApiConfigurations>,
27}
28
29#[bitwarden_ffi::wasm_export]
30impl OrganizationDomainsClient {
31 pub async fn get_verified_domains(
42 &self,
43 organization_id: OrganizationId,
44 ) -> Result<Vec<String>, OrganizationDomainsError> {
45 let response = self
46 .api_configurations
47 .api_client
48 .organization_domain_api()
49 .get_all_mini(organization_id.into())
50 .await?;
51
52 require!(response.data)
53 .into_iter()
54 .filter(|domain| domain.verified_date.is_some())
55 .map(|domain| Ok(require!(domain.domain_name)))
56 .collect()
57 }
58}
59
60pub trait OrganizationDomainsClientExt {
62 fn organization_domains(&self) -> OrganizationDomainsClient;
64}
65
66impl OrganizationDomainsClientExt for Client {
67 fn organization_domains(&self) -> OrganizationDomainsClient {
68 OrganizationDomainsClient::from_client(self)
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use bitwarden_api_api::{
75 apis::ApiClient,
76 models::{
77 OrganizationDomainMiniResponseModel,
78 OrganizationDomainMiniResponseModelListResponseModel,
79 },
80 };
81 use bitwarden_core::client::ApiConfigurations;
82
83 use super::*;
84
85 fn client_with(
86 response: OrganizationDomainMiniResponseModelListResponseModel,
87 ) -> OrganizationDomainsClient {
88 let api_client = ApiClient::new_mocked(move |mock| {
89 mock.organization_domain_api
90 .expect_get_all_mini()
91 .returning(move |_org| Ok(response.clone()))
92 .once();
93 });
94
95 OrganizationDomainsClient {
96 api_configurations: Arc::new(ApiConfigurations::from_api_client(api_client)),
97 }
98 }
99
100 #[tokio::test]
101 async fn get_verified_domains_excludes_unverified_domains() {
102 let client = client_with(OrganizationDomainMiniResponseModelListResponseModel {
103 object: Some("list".to_string()),
104 data: Some(vec![
105 OrganizationDomainMiniResponseModel {
106 object: Some("organizationDomainMini".to_string()),
107 domain_name: Some("verified.com".to_string()),
108 verified_date: Some("2026-09-15T00:00:00Z".to_string()),
109 },
110 OrganizationDomainMiniResponseModel {
111 object: Some("organizationDomainMini".to_string()),
112 domain_name: Some("unverified.com".to_string()),
113 verified_date: None,
114 },
115 ]),
116 continuation_token: None,
117 });
118
119 let domains = client
120 .get_verified_domains(OrganizationId::new_v4())
121 .await
122 .unwrap();
123
124 assert_eq!(domains, vec!["verified.com".to_string()]);
125 }
126
127 #[tokio::test]
128 async fn get_verified_domains_returns_empty_when_the_org_has_none() {
129 let client = client_with(OrganizationDomainMiniResponseModelListResponseModel {
130 object: Some("list".to_string()),
131 data: Some(vec![]),
132 continuation_token: None,
133 });
134
135 let domains = client
136 .get_verified_domains(OrganizationId::new_v4())
137 .await
138 .unwrap();
139
140 assert!(domains.is_empty());
141 }
142
143 #[tokio::test]
144 async fn get_verified_domains_errors_when_data_is_missing() {
145 let client = client_with(OrganizationDomainMiniResponseModelListResponseModel {
146 object: Some("list".to_string()),
147 data: None,
148 continuation_token: None,
149 });
150
151 let result = client.get_verified_domains(OrganizationId::new_v4()).await;
152
153 assert!(matches!(
154 result,
155 Err(OrganizationDomainsError::MissingField(_))
156 ));
157 }
158
159 #[tokio::test]
160 async fn get_verified_domains_errors_when_a_verified_domain_name_is_missing() {
161 let client = client_with(OrganizationDomainMiniResponseModelListResponseModel {
162 object: Some("list".to_string()),
163 data: Some(vec![OrganizationDomainMiniResponseModel {
164 object: Some("organizationDomainMini".to_string()),
165 domain_name: None,
166 verified_date: Some("2026-09-15T00:00:00Z".to_string()),
167 }]),
168 continuation_token: None,
169 });
170
171 let result = client.get_verified_domains(OrganizationId::new_v4()).await;
172
173 assert!(matches!(
174 result,
175 Err(OrganizationDomainsError::MissingField(_))
176 ));
177 }
178}