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