scim_server/operation_handler/
errors.rs

1//! Error handling utilities for operation handlers
2//!
3//! This module contains shared error response creation utilities used across
4//! all operation handlers.
5
6use crate::{
7    ScimError,
8    operation_handler::core::{OperationMetadata, ScimOperationResponse},
9    resource::version::VersionConflict,
10};
11use serde_json::Value;
12use std::collections::HashMap;
13
14/// Create an error response from a ScimError.
15pub fn create_error_response(error: ScimError, request_id: String) -> ScimOperationResponse {
16    let (error_message, error_code) = match &error {
17        ScimError::Validation(ve) => (
18            format!("Validation error: {}", ve),
19            Some("VALIDATION_ERROR"),
20        ),
21        ScimError::ResourceNotFound { resource_type, id } => (
22            format!("Resource not found: {} with ID {}", resource_type, id),
23            Some("RESOURCE_NOT_FOUND"),
24        ),
25        ScimError::SchemaNotFound { schema_id } => (
26            format!("Schema not found: {}", schema_id),
27            Some("SCHEMA_NOT_FOUND"),
28        ),
29        ScimError::UnsupportedResourceType(resource_type) => (
30            format!("Unsupported resource type: {}", resource_type),
31            Some("UNSUPPORTED_RESOURCE_TYPE"),
32        ),
33        ScimError::UnsupportedOperation {
34            resource_type,
35            operation,
36        } => (
37            format!(
38                "Unsupported operation {} for resource type {}",
39                operation, resource_type
40            ),
41            Some("UNSUPPORTED_OPERATION"),
42        ),
43        ScimError::InvalidRequest { message } => (
44            format!("Invalid request: {}", message),
45            Some("INVALID_REQUEST"),
46        ),
47        ScimError::Provider(provider_error) => (
48            format!("Provider error: {}", provider_error),
49            Some("PROVIDER_ERROR"),
50        ),
51        ScimError::Internal { message } => (
52            format!("Internal error: {}", message),
53            Some("INTERNAL_ERROR"),
54        ),
55        _ => (error.to_string(), Some("UNKNOWN_ERROR")),
56    };
57
58    ScimOperationResponse {
59        success: false,
60        data: None,
61        error: Some(error_message),
62        error_code: error_code.map(|s| s.to_string()),
63        metadata: OperationMetadata {
64            resource_type: None,
65            resource_id: None,
66            resource_count: None,
67            total_results: None,
68            request_id,
69            tenant_id: None,
70            schemas: None,
71            additional: HashMap::new(),
72        },
73    }
74}
75
76/// Create a response for version conflicts.
77pub fn create_version_conflict_response(
78    conflict: VersionConflict,
79    request_id: String,
80    resource_type: Option<String>,
81    resource_id: Option<String>,
82) -> ScimOperationResponse {
83    let mut additional = HashMap::new();
84    additional.insert(
85        "expected_version".to_string(),
86        Value::String(conflict.expected.as_str().to_string()),
87    );
88    additional.insert(
89        "current_version".to_string(),
90        Value::String(conflict.current.as_str().to_string()),
91    );
92    additional.insert(
93        "expected_etag".to_string(),
94        Value::String(conflict.expected.to_http_header()),
95    );
96    additional.insert(
97        "current_etag".to_string(),
98        Value::String(conflict.current.to_http_header()),
99    );
100
101    ScimOperationResponse {
102        success: false,
103        data: None,
104        error: Some(conflict.message),
105        error_code: Some("version_mismatch".to_string()),
106        metadata: OperationMetadata {
107            resource_type,
108            resource_id,
109            resource_count: None,
110            total_results: None,
111            request_id,
112            tenant_id: None,
113            schemas: None,
114            additional,
115        },
116    }
117}