scim_server/
schema_discovery.rs1use crate::error::{BuildError, BuildResult, ScimResult};
9
10use crate::schema::{Schema, SchemaRegistry};
11use serde::{Deserialize, Serialize};
12
13use std::marker::PhantomData;
14
15#[derive(Debug)]
19pub struct Uninitialized;
20
21#[derive(Debug)]
25pub struct Ready;
26
27pub struct SchemaDiscovery<State = Ready> {
55 inner: Option<DiscoveryInner>,
56 _state: PhantomData<State>,
57}
58
59impl<State> std::fmt::Debug for SchemaDiscovery<State> {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("SchemaDiscovery")
62 .field("inner", &self.inner.is_some())
63 .field("state", &std::any::type_name::<State>())
64 .finish()
65 }
66}
67
68struct DiscoveryInner {
70 schema_registry: SchemaRegistry,
71 service_config: ServiceProviderConfig,
72}
73
74impl std::fmt::Debug for DiscoveryInner {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 f.debug_struct("DiscoveryInner")
77 .field("schema_registry", &"SchemaRegistry")
78 .field("service_config", &self.service_config)
79 .finish()
80 }
81}
82
83impl SchemaDiscovery<Uninitialized> {
84 pub fn new() -> BuildResult<SchemaDiscovery<Ready>> {
90 let schema_registry =
91 SchemaRegistry::with_embedded_schemas().map_err(|_e| BuildError::SchemaLoadError {
92 schema_id: "Core".to_string(),
93 })?;
94
95 let service_config = ServiceProviderConfig::default();
96
97 let inner = DiscoveryInner {
98 schema_registry,
99 service_config,
100 };
101
102 Ok(SchemaDiscovery {
103 inner: Some(inner),
104 _state: PhantomData,
105 })
106 }
107}
108
109impl SchemaDiscovery<Ready> {
110 pub async fn get_schemas(&self) -> ScimResult<Vec<Schema>> {
117 let inner = self.inner.as_ref().expect("Server should be initialized");
118 Ok(inner
119 .schema_registry
120 .get_schemas()
121 .into_iter()
122 .cloned()
123 .collect())
124 }
125
126 pub async fn get_schema(&self, id: &str) -> ScimResult<Option<Schema>> {
135 let inner = self.inner.as_ref().expect("Server should be initialized");
136 Ok(inner.schema_registry.get_schema(id).cloned())
137 }
138
139 pub async fn get_service_provider_config(&self) -> ScimResult<ServiceProviderConfig> {
144 let inner = self.inner.as_ref().expect("Server should be initialized");
145 Ok(inner.service_config.clone())
146 }
147
148 pub fn schema_registry(&self) -> &SchemaRegistry {
153 let inner = self
154 .inner
155 .as_ref()
156 .expect("Discovery component should be initialized");
157 &inner.schema_registry
158 }
159
160 pub fn service_config(&self) -> &ServiceProviderConfig {
162 let inner = self
163 .inner
164 .as_ref()
165 .expect("Discovery component should be initialized");
166 &inner.service_config
167 }
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
175pub struct ServiceProviderConfig {
176 #[serde(rename = "patch")]
178 pub patch_supported: bool,
179
180 #[serde(rename = "bulk")]
182 pub bulk_supported: bool,
183
184 #[serde(rename = "filter")]
186 pub filter_supported: bool,
187
188 #[serde(rename = "changePassword")]
190 pub change_password_supported: bool,
191
192 #[serde(rename = "sort")]
194 pub sort_supported: bool,
195
196 #[serde(rename = "etag")]
198 pub etag_supported: bool,
199
200 #[serde(rename = "authenticationSchemes")]
202 pub authentication_schemes: Vec<AuthenticationScheme>,
203
204 #[serde(rename = "bulk.maxOperations")]
206 pub bulk_max_operations: Option<u32>,
207
208 #[serde(rename = "bulk.maxPayloadSize")]
210 pub bulk_max_payload_size: Option<u64>,
211
212 #[serde(rename = "filter.maxResults")]
214 pub filter_max_results: Option<u32>,
215}
216
217impl Default for ServiceProviderConfig {
218 fn default() -> Self {
219 Self {
220 patch_supported: false,
221 bulk_supported: false,
222 filter_supported: false,
223 change_password_supported: false,
224 sort_supported: false,
225 etag_supported: false,
226 authentication_schemes: vec![],
227 bulk_max_operations: None,
228 bulk_max_payload_size: None,
229 filter_max_results: Some(200),
230 }
231 }
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
236pub struct AuthenticationScheme {
237 pub name: String,
239 pub description: String,
241 #[serde(rename = "specUri")]
243 pub spec_uri: Option<String>,
244 #[serde(rename = "documentationUri")]
246 pub documentation_uri: Option<String>,
247 #[serde(rename = "type")]
249 pub auth_type: String,
250 pub primary: bool,
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[tokio::test]
259 async fn test_discovery_creation() {
260 let discovery = SchemaDiscovery::new().expect("Failed to create discovery component");
261
262 let schemas = discovery
264 .get_schemas()
265 .await
266 .expect("Failed to get schemas");
267 assert!(!schemas.is_empty());
268 }
269
270 #[tokio::test]
271 async fn test_schema_access() {
272 let discovery = SchemaDiscovery::new().expect("Failed to create discovery component");
273
274 let user_schema = discovery
276 .get_schema("urn:ietf:params:scim:schemas:core:2.0:User")
277 .await
278 .expect("Failed to get schema");
279
280 assert!(user_schema.is_some());
281 if let Some(schema) = user_schema {
282 assert_eq!(schema.name, "User");
283 }
284 }
285
286 #[test]
287 fn test_service_provider_config() {
288 let config = ServiceProviderConfig::default();
289 assert!(!config.patch_supported);
290 assert!(!config.bulk_supported);
291 assert!(!config.filter_supported);
292 }
293
294 #[tokio::test]
295 async fn test_tutorial_example_works() {
296 let discovery = SchemaDiscovery::new()
301 .expect("SchemaDiscovery::new() should work with embedded schemas");
302
303 let schemas = discovery
305 .get_schemas()
306 .await
307 .expect("get_schemas() should work");
308 assert!(
309 !schemas.is_empty(),
310 "Should have at least one schema available"
311 );
312 println!("Available schemas: {}", schemas.len());
313
314 let config = discovery
316 .get_service_provider_config()
317 .await
318 .expect("get_service_provider_config() should work");
319 println!("Bulk operations supported: {}", config.bulk_supported);
320
321 let user_schema = discovery
323 .get_schema("urn:ietf:params:scim:schemas:core:2.0:User")
324 .await
325 .expect("Should be able to get User schema");
326 assert!(user_schema.is_some(), "User schema should be available");
327
328 }
331}