1use crate::resource::{
46 ListQuery, RequestContext, Resource, ResourceProvider,
47 conditional_provider::VersionedResource,
48 version::{ConditionalResult, ScimVersion},
49};
50use crate::storage::{StorageProvider, StorageKey};
51use crate::providers::in_memory::{InMemoryError, InMemoryStats};
52use log::{debug, info, trace, warn};
53use serde_json::{Value, json};
54use std::collections::HashSet;
55
56
57
58
59#[derive(Debug, Clone)]
65pub struct StandardResourceProvider<S: StorageProvider> {
66 storage: S,
68}
69
70impl<S: StorageProvider> StandardResourceProvider<S> {
71 pub fn new(storage: S) -> Self {
73 Self {
74 storage,
75 }
76 }
77
78 fn effective_tenant_id(&self, context: &RequestContext) -> String {
82 context.tenant_id().unwrap_or("default").to_string()
83 }
84
85 async fn generate_resource_id(&self, _tenant_id: &str, _resource_type: &str) -> String {
87 uuid::Uuid::new_v4().to_string()
89 }
90
91 async fn check_username_duplicate(
93 &self,
94 tenant_id: &str,
95 username: &str,
96 exclude_id: Option<&str>,
97 ) -> Result<(), InMemoryError> {
98 let prefix = StorageKey::prefix(tenant_id, "User");
99 let matches = self.storage
100 .find_by_attribute(prefix, "userName", username)
101 .await
102 .map_err(|e| InMemoryError::Internal {
103 message: format!("Storage error during username check: {}", e),
104 })?;
105
106 for (key, _data) in matches {
107 if Some(key.resource_id()) != exclude_id {
109 return Err(InMemoryError::DuplicateAttribute {
110 resource_type: "User".to_string(),
111 attribute: "userName".to_string(),
112 value: username.to_string(),
113 tenant_id: tenant_id.to_string(),
114 });
115 }
116 }
117
118 Ok(())
119 }
120
121 fn add_scim_metadata(&self, mut resource: Resource) -> Resource {
123 if let Err(_e) = resource.create_meta("https://example.com/scim/v2") {
125 return resource;
126 }
127
128 if let Some(meta) = resource.get_meta().cloned() {
130 if let Some(id) = resource.get_id() {
131 let now = chrono::Utc::now();
132 let version = crate::resource::value_objects::Meta::generate_version(id, now);
133 if let Ok(meta_with_version) = meta.with_version(version) {
134 resource.set_meta(meta_with_version);
135 }
136 }
137 }
138
139 resource
140 }
141
142 pub async fn clear(&self) {
144 let common_tenants = vec!["default", "tenant-a", "tenant-b", "test"];
147 let common_types = vec!["User", "Group"];
148
149 for tenant_id in &common_tenants {
150 for resource_type in &common_types {
151 let prefix = StorageKey::prefix(*tenant_id, *resource_type);
152 if let Ok(results) = self.storage.list(prefix, 0, usize::MAX).await {
153 for (key, _value) in results {
154 let key_string = key.to_string();
155 if let Err(e) = self.storage.delete(key).await {
156 warn!("Failed to delete key {} during clear: {:?}", key_string, e);
157 }
158 }
159 }
160 }
161 }
162 }
163
164 pub async fn get_stats(&self) -> InMemoryStats {
166 let common_tenants = vec!["default", "tenant-a", "tenant-b", "test"];
169 let common_types = vec!["User", "Group"];
170
171 let mut tenant_set = HashSet::new();
172 let mut resource_type_set = HashSet::new();
173 let mut total_resources = 0;
174
175 for tenant_id in &common_tenants {
176 for resource_type in &common_types {
177 let prefix = StorageKey::prefix(*tenant_id, *resource_type);
178 if let Ok(results) = self.storage.list(prefix, 0, usize::MAX).await {
179 if !results.is_empty() {
180 tenant_set.insert(tenant_id.to_string());
181 resource_type_set.insert(resource_type.to_string());
182 total_resources += results.len();
183 }
184 }
185 }
186 }
187
188 let resource_types: Vec<String> = resource_type_set.into_iter().collect();
189
190 InMemoryStats {
191 tenant_count: tenant_set.len(),
192 total_resources,
193 resource_type_count: resource_types.len(),
194 resource_types,
195 }
196 }
197
198
199
200 pub async fn list_resources_in_tenant(
202 &self,
203 tenant_id: &str,
204 resource_type: &str,
205 ) -> Vec<Resource> {
206 let prefix = StorageKey::prefix(tenant_id, resource_type);
207 match self.storage.list(prefix, 0, usize::MAX).await {
208 Ok(storage_results) => {
209 let mut resources = Vec::new();
210 for (_key, data) in storage_results {
211 match Resource::from_json(resource_type.to_string(), data) {
212 Ok(resource) => resources.push(resource),
213 Err(e) => {
214 warn!("Failed to deserialize resource in list_resources_in_tenant: {}", e);
215 }
216 }
217 }
218 resources
219 }
220 Err(e) => {
221 warn!("Storage error in list_resources_in_tenant: {}", e);
222 Vec::new()
223 }
224 }
225 }
226
227 async fn count_resources_for_tenant(&self, tenant_id: &str, resource_type: &str) -> usize {
229 let prefix = StorageKey::prefix(tenant_id, resource_type);
230 match self.storage.count(prefix).await {
231 Ok(count) => count,
232 Err(e) => {
233 warn!("Storage error in count_resources_for_tenant: {}", e);
234 0
235 }
236 }
237 }
238}
239
240impl<S: StorageProvider> ResourceProvider for StandardResourceProvider<S> {
245 type Error = InMemoryError;
246
247 async fn create_resource(
248 &self,
249 resource_type: &str,
250 mut data: Value,
251 context: &RequestContext,
252 ) -> Result<Resource, Self::Error> {
253 let tenant_id = self.effective_tenant_id(context);
254
255 info!(
256 "Creating {} resource for tenant '{}' (request: '{}')",
257 resource_type, tenant_id, context.request_id
258 );
259 trace!(
260 "Create data: {}",
261 serde_json::to_string(&data).unwrap_or_else(|_| "invalid json".to_string())
262 );
263
264 context
266 .validate_operation("create")
267 .map_err(|e| InMemoryError::Internal { message: e })?;
268
269 if let Some(tenant_context) = &context.tenant_context {
271 if resource_type == "User" {
272 if let Some(max_users) = tenant_context.permissions.max_users {
273 let current_count = self.count_resources_for_tenant(&tenant_id, "User").await;
274 if current_count >= max_users {
275 return Err(InMemoryError::Internal {
276 message: format!(
277 "User limit exceeded: {}/{}",
278 current_count, max_users
279 ),
280 });
281 }
282 }
283 } else if resource_type == "Group" {
284 if let Some(max_groups) = tenant_context.permissions.max_groups {
285 let current_count = self.count_resources_for_tenant(&tenant_id, "Group").await;
286 if current_count >= max_groups {
287 return Err(InMemoryError::Internal {
288 message: format!(
289 "Group limit exceeded: {}/{}",
290 current_count, max_groups
291 ),
292 });
293 }
294 }
295 }
296 }
297
298 if data.get("id").is_none() {
300 let id = self.generate_resource_id(&tenant_id, resource_type).await;
301 if let Some(obj) = data.as_object_mut() {
302 obj.insert("id".to_string(), json!(id));
303 }
304 }
305
306 let resource = Resource::from_json(resource_type.to_string(), data).map_err(|e| {
308 InMemoryError::InvalidData {
309 message: format!("Failed to create resource: {}", e),
310 }
311 })?;
312
313 if resource_type == "User" {
315 if let Some(username) = resource.get_username() {
316 self.check_username_duplicate(&tenant_id, username, None)
317 .await?;
318 }
319 }
320
321 let resource_with_meta = self.add_scim_metadata(resource);
323 let resource_id = resource_with_meta.get_id().unwrap_or("unknown").to_string();
324
325 let key = StorageKey::new(&tenant_id, resource_type, &resource_id);
327 let stored_data = self.storage
328 .put(key, resource_with_meta.to_json().map_err(|e| InMemoryError::Internal {
329 message: format!("Failed to serialize resource: {}", e),
330 })?)
331 .await
332 .map_err(|e| InMemoryError::Internal {
333 message: format!("Storage error during create: {}", e),
334 })?;
335
336 Resource::from_json(resource_type.to_string(), stored_data)
338 .map_err(|e| InMemoryError::InvalidData {
339 message: format!("Failed to deserialize stored resource: {}", e),
340 })
341 }
342
343 async fn get_resource(
344 &self,
345 resource_type: &str,
346 id: &str,
347 context: &RequestContext,
348 ) -> Result<Option<Resource>, Self::Error> {
349 let tenant_id = self.effective_tenant_id(context);
350
351 debug!(
352 "Getting {} resource with ID '{}' for tenant '{}' (request: '{}')",
353 resource_type, id, tenant_id, context.request_id
354 );
355
356 context
358 .validate_operation("read")
359 .map_err(|e| InMemoryError::Internal { message: e })?;
360
361 let key = StorageKey::new(&tenant_id, resource_type, id);
362 let resource_data = self.storage
363 .get(key)
364 .await
365 .map_err(|e| InMemoryError::Internal {
366 message: format!("Storage error during get: {}", e),
367 })?;
368
369 let resource = match resource_data {
370 Some(data) => {
371 let resource = Resource::from_json(resource_type.to_string(), data)
372 .map_err(|e| InMemoryError::InvalidData {
373 message: format!("Failed to deserialize resource: {}", e),
374 })?;
375 trace!("Resource found and returned");
376 Some(resource)
377 }
378 None => {
379 debug!("Resource not found");
380 None
381 }
382 };
383
384 Ok(resource)
385 }
386
387 async fn update_resource(
388 &self,
389 resource_type: &str,
390 id: &str,
391 mut data: Value,
392 context: &RequestContext,
393 ) -> Result<Resource, Self::Error> {
394 let tenant_id = self.effective_tenant_id(context);
395
396 info!(
397 "Updating {} resource with ID '{}' for tenant '{}' (request: '{}')",
398 resource_type, id, tenant_id, context.request_id
399 );
400 trace!(
401 "Update data: {}",
402 serde_json::to_string(&data).unwrap_or_else(|_| "invalid json".to_string())
403 );
404
405 context
407 .validate_operation("update")
408 .map_err(|e| InMemoryError::Internal { message: e })?;
409
410 if let Some(obj) = data.as_object_mut() {
412 obj.insert("id".to_string(), json!(id));
413 }
414
415 let resource = Resource::from_json(resource_type.to_string(), data).map_err(|e| {
417 InMemoryError::InvalidData {
418 message: format!("Failed to update resource: {}", e),
419 }
420 })?;
421
422 if resource_type == "User" {
424 if let Some(username) = resource.get_username() {
425 self.check_username_duplicate(&tenant_id, username, Some(id))
426 .await?;
427 }
428 }
429
430 let key = StorageKey::new(&tenant_id, resource_type, id);
432 let exists = self.storage
433 .exists(key.clone())
434 .await
435 .map_err(|e| InMemoryError::Internal {
436 message: format!("Storage error during existence check: {}", e),
437 })?;
438
439 if !exists {
440 return Err(InMemoryError::ResourceNotFound {
441 resource_type: resource_type.to_string(),
442 id: id.to_string(),
443 tenant_id,
444 });
445 }
446
447 let resource_with_meta = self.add_scim_metadata(resource);
449
450 let stored_data = self.storage
452 .put(key, resource_with_meta.to_json().map_err(|e| InMemoryError::Internal {
453 message: format!("Failed to serialize resource: {}", e),
454 })?)
455 .await
456 .map_err(|e| InMemoryError::Internal {
457 message: format!("Storage error during update: {}", e),
458 })?;
459
460 Resource::from_json(resource_type.to_string(), stored_data)
462 .map_err(|e| InMemoryError::InvalidData {
463 message: format!("Failed to deserialize updated resource: {}", e),
464 })
465 }
466
467 async fn delete_resource(
468 &self,
469 resource_type: &str,
470 id: &str,
471 context: &RequestContext,
472 ) -> Result<(), Self::Error> {
473 let tenant_id = self.effective_tenant_id(context);
474
475 info!(
476 "Deleting {} resource with ID '{}' for tenant '{}' (request: '{}')",
477 resource_type, id, tenant_id, context.request_id
478 );
479
480 context
482 .validate_operation("delete")
483 .map_err(|e| InMemoryError::Internal { message: e })?;
484
485 let key = StorageKey::new(&tenant_id, resource_type, id);
487 let removed = self.storage
488 .delete(key)
489 .await
490 .map_err(|e| InMemoryError::Internal {
491 message: format!("Storage error during delete: {}", e),
492 })?;
493
494 if !removed {
495 warn!(
496 "Attempted to delete non-existent {} resource with ID '{}' for tenant '{}'",
497 resource_type, id, tenant_id
498 );
499 return Err(InMemoryError::ResourceNotFound {
500 resource_type: resource_type.to_string(),
501 id: id.to_string(),
502 tenant_id,
503 });
504 }
505
506 debug!(
507 "Successfully deleted {} resource with ID '{}' for tenant '{}'",
508 resource_type, id, tenant_id
509 );
510 Ok(())
511 }
512
513 async fn list_resources(
514 &self,
515 resource_type: &str,
516 query: Option<&ListQuery>,
517 context: &RequestContext,
518 ) -> Result<Vec<Resource>, Self::Error> {
519 let tenant_id = self.effective_tenant_id(context);
520
521 debug!(
522 "Listing {} resources for tenant '{}' (request: '{}')",
523 resource_type, tenant_id, context.request_id
524 );
525
526 context
528 .validate_operation("list")
529 .map_err(|e| InMemoryError::Internal { message: e })?;
530
531 let prefix = StorageKey::prefix(&tenant_id, resource_type);
533 let storage_results = self.storage
534 .list(prefix, 0, usize::MAX) .await
536 .map_err(|e| InMemoryError::Internal {
537 message: format!("Storage error during list: {}", e),
538 })?;
539
540 let mut resources = Vec::new();
542 for (_key, data) in storage_results {
543 match Resource::from_json(resource_type.to_string(), data) {
544 Ok(resource) => resources.push(resource),
545 Err(e) => {
546 warn!("Failed to deserialize resource during list: {}", e);
547 }
549 }
550 }
551
552 let mut filtered_resources = resources;
554
555 if let Some(q) = query {
556 if let Some(start_index) = q.start_index {
558 let start = (start_index.saturating_sub(1)) as usize; if start < filtered_resources.len() {
560 filtered_resources = filtered_resources.into_iter().skip(start).collect();
561 } else {
562 filtered_resources = Vec::new();
563 }
564 }
565
566 if let Some(count) = q.count {
567 filtered_resources.truncate(count as usize);
568 }
569 }
570
571 debug!(
572 "Found {} {} resources for tenant '{}' (after filtering)",
573 filtered_resources.len(),
574 resource_type,
575 tenant_id
576 );
577
578 Ok(filtered_resources)
579 }
580
581 async fn find_resource_by_attribute(
582 &self,
583 resource_type: &str,
584 attribute: &str,
585 value: &Value,
586 context: &RequestContext,
587 ) -> Result<Option<Resource>, Self::Error> {
588 let tenant_id = self.effective_tenant_id(context);
589
590 let prefix = StorageKey::prefix(&tenant_id, resource_type);
592 let value_str = match value {
593 Value::String(s) => s.clone(),
594 _ => value.to_string().trim_matches('"').to_string(),
595 };
596
597 let matches = self.storage
598 .find_by_attribute(prefix, attribute, &value_str)
599 .await
600 .map_err(|e| InMemoryError::Internal {
601 message: format!("Storage error during find by attribute: {}", e),
602 })?;
603
604 for (_key, data) in matches {
606 match Resource::from_json(resource_type.to_string(), data) {
607 Ok(resource) => return Ok(Some(resource)),
608 Err(e) => {
609 warn!("Failed to deserialize resource during find: {}", e);
610 continue;
611 }
612 }
613 }
614
615 Ok(None)
616 }
617
618 async fn resource_exists(
619 &self,
620 resource_type: &str,
621 id: &str,
622 context: &RequestContext,
623 ) -> Result<bool, Self::Error> {
624 let tenant_id = self.effective_tenant_id(context);
625
626 let key = StorageKey::new(&tenant_id, resource_type, id);
627 self.storage
628 .exists(key)
629 .await
630 .map_err(|e| InMemoryError::Internal {
631 message: format!("Storage error during exists check: {}", e),
632 })
633 }
634
635 async fn patch_resource(
636 &self,
637 resource_type: &str,
638 id: &str,
639 patch_request: &Value,
640 context: &RequestContext,
641 ) -> Result<Resource, Self::Error> {
642 let _tenant_id = self.effective_tenant_id(context);
643
644 let operations = patch_request
646 .get("Operations")
647 .and_then(|ops| ops.as_array())
648 .ok_or(InMemoryError::InvalidInput {
649 message: "PATCH request must contain Operations array".to_string(),
650 })?;
651
652 if operations.is_empty() {
654 return Err(InMemoryError::InvalidInput {
655 message: "Operations array cannot be empty".to_string(),
656 });
657 }
658
659 let tenant_id = self.effective_tenant_id(context);
661 let key = StorageKey::new(&tenant_id, resource_type, id);
662
663 match self.storage.get(key.clone()).await {
664 Ok(Some(mut current_data)) => {
665 for operation in operations {
667 self.apply_patch_operation(&mut current_data, operation)?;
668 }
669
670 let new_version = ScimVersion::from_content(serde_json::to_string(¤t_data).unwrap().as_bytes());
672 if let Some(obj) = current_data.as_object_mut() {
673 obj.insert("version".to_string(), json!(new_version.to_string()));
674 }
675
676 self.storage.put(key, current_data.clone()).await
678 .map_err(|_| InMemoryError::Internal {
679 message: "Failed to store patched resource".to_string(),
680 })?;
681
682 let updated_resource = Resource::from_json(resource_type.to_string(), current_data)
684 .map_err(|e| InMemoryError::InvalidInput {
685 message: format!("Failed to deserialize patched resource: {}", e),
686 })?;
687
688 Ok(updated_resource)
689 }
690 Ok(None) => {
691 Err(InMemoryError::NotFound {
692 resource_type: resource_type.to_string(),
693 id: id.to_string(),
694 })
695 }
696 Err(_) => {
697 Err(InMemoryError::Internal {
698 message: "Failed to retrieve resource for patch".to_string(),
699 })
700 }
701 }
702 }
703
704 fn apply_patch_operation(
706 &self,
707 resource_data: &mut Value,
708 operation: &Value,
709 ) -> Result<(), Self::Error> {
710 let op =
711 operation
712 .get("op")
713 .and_then(|v| v.as_str())
714 .ok_or(InMemoryError::InvalidInput {
715 message: "PATCH operation must have 'op' field".to_string(),
716 })?;
717
718 let path = operation.get("path").and_then(|v| v.as_str());
719 let value = operation.get("value");
720
721 match op.to_lowercase().as_str() {
722 "add" => self.apply_add_operation(resource_data, path, value),
723 "remove" => self.apply_remove_operation(resource_data, path),
724 "replace" => self.apply_replace_operation(resource_data, path, value),
725 _ => Err(InMemoryError::InvalidInput {
726 message: format!("Unsupported PATCH operation: {}", op),
727 }),
728 }
729 }
730}
731
732impl<S: StorageProvider> StandardResourceProvider<S> {
733 fn apply_add_operation(
735 &self,
736 resource_data: &mut Value,
737 path: Option<&str>,
738 value: Option<&Value>,
739 ) -> Result<(), InMemoryError> {
740 let value = value.ok_or(InMemoryError::InvalidInput {
741 message: "ADD operation requires a value".to_string(),
742 })?;
743
744 match path {
745 Some(path_str) => {
746 self.set_value_at_path(resource_data, path_str, value.clone())?;
747 }
748 None => {
749 if let (Some(current_obj), Some(value_obj)) =
751 (resource_data.as_object_mut(), value.as_object())
752 {
753 for (key, val) in value_obj {
754 current_obj.insert(key.clone(), val.clone());
755 }
756 }
757 }
758 }
759 Ok(())
760 }
761
762 fn apply_remove_operation(
764 &self,
765 resource_data: &mut Value,
766 path: Option<&str>,
767 ) -> Result<(), InMemoryError> {
768 if let Some(path_str) = path {
769 self.remove_value_at_path(resource_data, path_str)?;
770 }
771 Ok(())
772 }
773
774 fn apply_replace_operation(
776 &self,
777 resource_data: &mut Value,
778 path: Option<&str>,
779 value: Option<&Value>,
780 ) -> Result<(), InMemoryError> {
781 let value = value.ok_or(InMemoryError::InvalidInput {
782 message: "REPLACE operation requires a value".to_string(),
783 })?;
784
785 match path {
786 Some(path_str) => {
787 self.set_value_at_path(resource_data, path_str, value.clone())?;
788 }
789 None => {
790 *resource_data = value.clone();
792 }
793 }
794 Ok(())
795 }
796
797 fn set_value_at_path(
799 &self,
800 data: &mut Value,
801 path: &str,
802 value: Value,
803 ) -> Result<(), InMemoryError> {
804 let parts: Vec<&str> = path.split('.').collect();
805
806 if parts.len() == 1 {
807 if let Some(obj) = data.as_object_mut() {
809 let attribute_name = parts[0];
810
811 if Self::is_multivalued_attribute(attribute_name) {
813 if let Some(existing) = obj.get_mut(attribute_name) {
814 if let Some(existing_array) = existing.as_array_mut() {
815 if value.is_array() {
817 obj.insert(attribute_name.to_string(), value);
818 } else {
819 existing_array.push(value);
821 }
822 return Ok(());
823 }
824 }
825 let new_array = if value.is_array() {
827 value
828 } else {
829 json!([value])
830 };
831 obj.insert(attribute_name.to_string(), new_array);
832 } else {
833 obj.insert(attribute_name.to_string(), value);
835 }
836 }
837 return Ok(());
838 }
839
840 let mut current = data;
842
843 for part in &parts[..parts.len() - 1] {
844 if let Some(obj) = current.as_object_mut() {
845 let entry = obj
846 .entry(part.to_string())
847 .or_insert_with(|| Value::Object(serde_json::Map::new()));
848 current = entry;
849 } else {
850 return Err(InMemoryError::InvalidInput {
851 message: format!(
852 "Cannot navigate path '{}' - intermediate value is not an object",
853 path
854 ),
855 });
856 }
857 }
858
859 if let Some(obj) = current.as_object_mut() {
861 obj.insert(parts.last().unwrap().to_string(), value);
862 } else {
863 return Err(InMemoryError::InvalidInput {
864 message: format!(
865 "Cannot set value at path '{}' - target is not an object",
866 path
867 ),
868 });
869 }
870
871 Ok(())
872 }
873
874 fn remove_value_at_path(&self, data: &mut Value, path: &str) -> Result<(), InMemoryError> {
876 let parts: Vec<&str> = path.split('.').collect();
877
878 if parts.len() == 1 {
879 if let Some(obj) = data.as_object_mut() {
881 obj.remove(parts[0]);
882 }
883 return Ok(());
884 }
885
886 let mut current = data;
888
889 for part in &parts[..parts.len() - 1] {
890 if let Some(obj) = current.as_object_mut() {
891 match obj.get_mut(*part) {
893 Some(value) => current = value,
894 None => return Ok(()), }
896 } else {
897 return Err(InMemoryError::InvalidInput {
898 message: format!(
899 "Cannot navigate path '{}' - intermediate value is not an object",
900 path
901 ),
902 });
903 }
904 }
905
906 if let Some(obj) = current.as_object_mut() {
908 obj.remove(*parts.last().unwrap());
909 }
910
911 Ok(())
912 }
913
914
915
916 fn is_multivalued_attribute(attribute_name: &str) -> bool {
918 matches!(
919 attribute_name,
920 "emails" | "phoneNumbers" | "addresses" | "groups" | "members"
921 )
922 }
923}
924
925impl<S: StorageProvider> StandardResourceProvider<S> {
927 pub async fn conditional_update(
928 &self,
929 resource_type: &str,
930 id: &str,
931 data: Value,
932 expected_version: &ScimVersion,
933 context: &RequestContext,
934 ) -> Result<ConditionalResult<VersionedResource>, InMemoryError> {
935 let tenant_id = self.effective_tenant_id(context);
936 let key = StorageKey::new(&tenant_id, resource_type, id);
937
938 match self.storage.get(key.clone()).await {
940 Ok(Some(current_data)) => {
941 let current_resource = Resource::from_json(resource_type.to_string(), current_data.clone())
943 .map_err(|e| InMemoryError::InvalidInput {
944 message: format!("Failed to deserialize stored resource: {}", e),
945 })?;
946
947 let current_version = VersionedResource::new(current_resource.clone()).version().clone();
949 if ¤t_version != expected_version {
950 use crate::resource::version::VersionConflict;
951 return Ok(ConditionalResult::VersionMismatch(VersionConflict::new(expected_version.clone(), current_version, "Resource was modified by another client")));
952 }
953
954 let mut updated_resource = Resource::from_json(resource_type.to_string(), data)
956 .map_err(|e| InMemoryError::InvalidInput {
957 message: format!("Failed to create updated resource: {}", e),
958 })?;
959
960 if let Some(original_id) = current_resource.get_id() {
962 updated_resource.set_id(original_id)
963 .map_err(|e| InMemoryError::InvalidInput {
964 message: format!("Failed to set ID: {}", e),
965 })?;
966 }
967
968 let updated_data = updated_resource.to_json()
970 .map_err(|e| InMemoryError::InvalidInput {
971 message: format!("Failed to serialize updated resource: {}", e),
972 })?;
973
974 self.storage.put(key, updated_data).await
975 .map_err(|_| InMemoryError::Internal {
976 message: "Failed to store updated resource".to_string(),
977 })?;
978
979 Ok(ConditionalResult::Success(VersionedResource::new(updated_resource)))
980 }
981 Ok(None) => {
982 Err(InMemoryError::NotFound {
983 resource_type: resource_type.to_string(),
984 id: id.to_string(),
985 })
986 }
987 Err(_) => {
988 Err(InMemoryError::Internal {
989 message: "Failed to retrieve resource for conditional update".to_string(),
990 })
991 }
992 }
993 }
994
995 pub async fn conditional_delete(
996 &self,
997 resource_type: &str,
998 id: &str,
999 expected_version: &ScimVersion,
1000 context: &RequestContext,
1001 ) -> Result<ConditionalResult<()>, InMemoryError> {
1002 let tenant_id = self.effective_tenant_id(context);
1003 let key = StorageKey::new(&tenant_id, resource_type, id);
1004
1005 match self.storage.get(key.clone()).await {
1007 Ok(Some(current_data)) => {
1008 let current_resource = Resource::from_json(resource_type.to_string(), current_data)
1010 .map_err(|e| InMemoryError::InvalidInput {
1011 message: format!("Failed to deserialize stored resource: {}", e),
1012 })?;
1013
1014 let current_version = VersionedResource::new(current_resource.clone()).version().clone();
1016 if ¤t_version != expected_version {
1017 use crate::resource::version::VersionConflict;
1018 return Ok(ConditionalResult::VersionMismatch(VersionConflict::new(expected_version.clone(), current_version, "Resource was modified by another client")));
1019 }
1020
1021 self.storage.delete(key).await
1023 .map_err(|_| InMemoryError::Internal {
1024 message: "Failed to delete resource".to_string(),
1025 })?;
1026
1027 Ok(ConditionalResult::Success(()))
1028 }
1029 Ok(None) => {
1030 Err(InMemoryError::NotFound {
1031 resource_type: resource_type.to_string(),
1032 id: id.to_string(),
1033 })
1034 }
1035 Err(_) => {
1036 Err(InMemoryError::Internal {
1037 message: "Failed to retrieve resource for conditional delete".to_string(),
1038 })
1039 }
1040 }
1041 }
1042
1043 pub async fn conditional_patch_resource(
1044 &self,
1045 resource_type: &str,
1046 id: &str,
1047 patch_request: &Value,
1048 expected_version: &ScimVersion,
1049 context: &RequestContext,
1050 ) -> Result<ConditionalResult<VersionedResource>, InMemoryError> {
1051 let tenant_id = self.effective_tenant_id(context);
1052 let key = StorageKey::new(&tenant_id, resource_type, id);
1053
1054 match self.storage.get(key.clone()).await {
1056 Ok(Some(current_data)) => {
1057 let current_resource = Resource::from_json(resource_type.to_string(), current_data.clone())
1059 .map_err(|e| InMemoryError::InvalidInput {
1060 message: format!("Failed to deserialize stored resource: {}", e),
1061 })?;
1062
1063 let current_version = VersionedResource::new(current_resource.clone()).version().clone();
1065 if ¤t_version != expected_version {
1066 use crate::resource::version::VersionConflict;
1067 return Ok(ConditionalResult::VersionMismatch(VersionConflict::new(expected_version.clone(), current_version, "Resource was modified by another client")));
1068 }
1069
1070 let mut patched_data = current_data;
1072
1073 if let Some(operations) = patch_request.get("Operations") {
1075 if let Some(ops_array) = operations.as_array() {
1076 for operation in ops_array {
1077 self.apply_patch_operation(&mut patched_data, operation)?;
1078 }
1079 }
1080 }
1081
1082 let patched_resource = Resource::from_json(resource_type.to_string(), patched_data)
1084 .map_err(|e| InMemoryError::InvalidInput {
1085 message: format!("Failed to deserialize patched resource: {}", e),
1086 })?;
1087
1088 let patched_json = patched_resource.to_json()
1090 .map_err(|e| InMemoryError::InvalidInput {
1091 message: format!("Failed to serialize patched resource: {}", e),
1092 })?;
1093
1094 self.storage.put(key, patched_json).await
1095 .map_err(|_| InMemoryError::Internal {
1096 message: "Failed to store patched resource".to_string(),
1097 })?;
1098
1099 Ok(ConditionalResult::Success(VersionedResource::new(patched_resource)))
1100 }
1101 Ok(None) => {
1102 Err(InMemoryError::NotFound {
1103 resource_type: resource_type.to_string(),
1104 id: id.to_string(),
1105 })
1106 }
1107 Err(_) => {
1108 Err(InMemoryError::Internal {
1109 message: "Failed to retrieve resource for conditional patch".to_string(),
1110 })
1111 }
1112 }
1113 }
1114
1115 pub async fn get_versioned_resource(
1116 &self,
1117 resource_type: &str,
1118 id: &str,
1119 context: &RequestContext,
1120 ) -> Result<Option<VersionedResource>, InMemoryError> {
1121 match self.get_resource(resource_type, id, context).await? {
1122 Some(resource) => Ok(Some(VersionedResource::new(resource))),
1123 None => Ok(None),
1124 }
1125 }
1126
1127 pub async fn create_versioned_resource(
1128 &self,
1129 resource_type: &str,
1130 data: Value,
1131 context: &RequestContext,
1132 ) -> Result<VersionedResource, InMemoryError> {
1133 let resource = self.create_resource(resource_type, data, context).await?;
1134 Ok(VersionedResource::new(resource))
1135 }
1136}