use super::core::ScimServer;
use crate::error::ScimResult;
use crate::resource::{RequestContext, Resource, ResourceProvider, ScimOperation};
use log::{debug, info, warn};
use serde_json::Value;
impl<P: ResourceProvider + Sync> ScimServer<P> {
pub async fn create_resource(
&self,
resource_type: &str,
data: Value,
context: &RequestContext,
) -> ScimResult<Resource> {
info!(
"SCIM create {} operation initiated (request: '{}')",
resource_type, context.request_id
);
self.ensure_operation_supported(resource_type, &ScimOperation::Create)?;
let schema = self.get_schema_for_resource_type(resource_type)?;
self.schema_registry.validate_resource(&schema, &data)?;
let result = self
.provider
.create_resource(resource_type, data, context)
.await
.map_err(|e| crate::error::ScimError::ProviderError(e.to_string()));
match &result {
Ok(resource) => {
info!(
"SCIM create {} operation completed successfully: ID '{}' (request: '{}')",
resource_type,
resource.get_id().unwrap_or("unknown"),
context.request_id
);
}
Err(e) => {
warn!(
"SCIM create {} operation failed: {} (request: '{}')",
resource_type, e, context.request_id
);
}
}
result
}
pub async fn get_resource(
&self,
resource_type: &str,
id: &str,
context: &RequestContext,
) -> ScimResult<Option<Resource>> {
debug!(
"SCIM get {} operation initiated for ID '{}' (request: '{}')",
resource_type, id, context.request_id
);
self.ensure_operation_supported(resource_type, &ScimOperation::Read)?;
let result = self
.provider
.get_resource(resource_type, id, context)
.await
.map_err(|e| crate::error::ScimError::ProviderError(e.to_string()));
match &result {
Ok(Some(_)) => {
debug!(
"SCIM get {} operation completed: found resource with ID '{}' (request: '{}')",
resource_type, id, context.request_id
);
}
Ok(None) => {
debug!(
"SCIM get {} operation completed: resource with ID '{}' not found (request: '{}')",
resource_type, id, context.request_id
);
}
Err(e) => {
warn!(
"SCIM get {} operation failed for ID '{}': {} (request: '{}')",
resource_type, id, e, context.request_id
);
}
}
result
}
pub async fn update_resource(
&self,
resource_type: &str,
id: &str,
data: Value,
context: &RequestContext,
) -> ScimResult<Resource> {
info!(
"SCIM update {} operation initiated for ID '{}' (request: '{}')",
resource_type, id, context.request_id
);
self.ensure_operation_supported(resource_type, &ScimOperation::Update)?;
let schema = self.get_schema_for_resource_type(resource_type)?;
self.schema_registry.validate_resource(&schema, &data)?;
let result = self
.provider
.update_resource(resource_type, id, data, context)
.await
.map_err(|e| crate::error::ScimError::ProviderError(e.to_string()));
match &result {
Ok(_) => {
info!(
"SCIM update {} operation completed successfully for ID '{}' (request: '{}')",
resource_type, id, context.request_id
);
}
Err(e) => {
warn!(
"SCIM update {} operation failed for ID '{}': {} (request: '{}')",
resource_type, id, e, context.request_id
);
}
}
result
}
pub async fn delete_resource(
&self,
resource_type: &str,
id: &str,
context: &RequestContext,
) -> ScimResult<()> {
info!(
"SCIM delete {} operation initiated for ID '{}' (request: '{}')",
resource_type, id, context.request_id
);
self.ensure_operation_supported(resource_type, &ScimOperation::Delete)?;
let result = self
.provider
.delete_resource(resource_type, id, context)
.await
.map_err(|e| crate::error::ScimError::ProviderError(e.to_string()));
match &result {
Ok(_) => {
info!(
"SCIM delete {} operation completed successfully for ID '{}' (request: '{}')",
resource_type, id, context.request_id
);
}
Err(e) => {
warn!(
"SCIM delete {} operation failed for ID '{}': {} (request: '{}')",
resource_type, id, e, context.request_id
);
}
}
result
}
pub async fn list_resources(
&self,
resource_type: &str,
context: &RequestContext,
) -> ScimResult<Vec<Resource>> {
debug!(
"SCIM list {} operation initiated (request: '{}')",
resource_type, context.request_id
);
self.ensure_operation_supported(resource_type, &ScimOperation::List)?;
let result = self
.provider
.list_resources(resource_type, None, context)
.await
.map_err(|e| crate::error::ScimError::internal(format!("Provider error: {}", e)));
match &result {
Ok(resources) => {
debug!(
"SCIM list {} operation completed: found {} resources (request: '{}')",
resource_type,
resources.len(),
context.request_id
);
}
Err(e) => {
warn!(
"SCIM list {} operation failed: {} (request: '{}')",
resource_type, e, context.request_id
);
}
}
result
}
pub async fn find_resource_by_attribute(
&self,
resource_type: &str,
attribute: &str,
value: &Value,
context: &RequestContext,
) -> ScimResult<Option<Resource>> {
debug!(
"SCIM find {} operation initiated for {}='{}' (request: '{}')",
resource_type, attribute, value, context.request_id
);
self.ensure_operation_supported(resource_type, &ScimOperation::Search)?;
let result = self
.provider
.find_resource_by_attribute(resource_type, attribute, value, context)
.await
.map_err(|e| crate::error::ScimError::ProviderError(e.to_string()));
match &result {
Ok(Some(resource)) => {
debug!(
"SCIM find {} operation completed: found resource with ID '{}' (request: '{}')",
resource_type,
resource.get_id().unwrap_or("unknown"),
context.request_id
);
}
Ok(None) => {
debug!(
"SCIM find {} operation completed: no resource found for {}='{}' (request: '{}')",
resource_type, attribute, value, context.request_id
);
}
Err(e) => {
warn!(
"SCIM find {} operation failed for {}='{}': {} (request: '{}')",
resource_type, attribute, value, e, context.request_id
);
}
}
result
}
pub async fn resource_exists(
&self,
resource_type: &str,
id: &str,
context: &RequestContext,
) -> ScimResult<bool> {
debug!(
"SCIM resource exists check for {} with ID '{}' (request: '{}')",
resource_type, id, context.request_id
);
self.provider
.resource_exists(resource_type, id, context)
.await
.map_err(|e| crate::error::ScimError::ProviderError(e.to_string()))
}
pub async fn patch_resource(
&self,
resource_type: &str,
id: &str,
patch_request: &Value,
context: &RequestContext,
) -> ScimResult<Resource> {
info!(
"SCIM patch {} operation initiated for ID '{}' (request: '{}')",
resource_type, id, context.request_id
);
self.ensure_operation_supported(resource_type, &ScimOperation::Patch)?;
if !patch_request
.get("Operations")
.and_then(|ops| ops.as_array())
.is_some()
{
return Err(crate::error::ScimError::invalid_request(
"PATCH request must contain Operations array".to_string(),
));
}
let result = self
.provider
.patch_resource(resource_type, id, patch_request, context)
.await
.map_err(|e| crate::error::ScimError::ProviderError(e.to_string()));
match &result {
Ok(resource) => {
info!(
"SCIM patch {} operation completed successfully: ID '{}' (request: '{}')",
resource_type,
resource.get_id().unwrap_or("unknown"),
context.request_id
);
}
Err(e) => {
warn!(
"SCIM patch {} operation failed for ID '{}': {} (request: '{}')",
resource_type, id, e, context.request_id
);
}
}
result
}
}