ResourceProvider

Trait ResourceProvider 

Source
pub trait ResourceProvider {
    type Error: Error + Send + Sync + 'static;

    // Required methods
    fn create_resource(
        &self,
        resource_type: &str,
        data: Value,
        context: &RequestContext,
    ) -> impl Future<Output = Result<Resource, Self::Error>> + Send;
    fn get_resource(
        &self,
        resource_type: &str,
        id: &str,
        context: &RequestContext,
    ) -> impl Future<Output = Result<Option<Resource>, Self::Error>> + Send;
    fn update_resource(
        &self,
        resource_type: &str,
        id: &str,
        data: Value,
        context: &RequestContext,
    ) -> impl Future<Output = Result<Resource, Self::Error>> + Send;
    fn delete_resource(
        &self,
        resource_type: &str,
        id: &str,
        context: &RequestContext,
    ) -> impl Future<Output = Result<(), Self::Error>> + Send;
    fn list_resources(
        &self,
        resource_type: &str,
        _query: Option<&ListQuery>,
        context: &RequestContext,
    ) -> impl Future<Output = Result<Vec<Resource>, Self::Error>> + Send;
    fn find_resource_by_attribute(
        &self,
        resource_type: &str,
        attribute: &str,
        value: &Value,
        context: &RequestContext,
    ) -> impl Future<Output = Result<Option<Resource>, Self::Error>> + Send;
    fn resource_exists(
        &self,
        resource_type: &str,
        id: &str,
        context: &RequestContext,
    ) -> impl Future<Output = Result<bool, Self::Error>> + Send;

    // Provided methods
    fn conditional_update(
        &self,
        resource_type: &str,
        id: &str,
        data: Value,
        expected_version: &ScimVersion,
        context: &RequestContext,
    ) -> impl Future<Output = Result<ConditionalResult<VersionedResource>, Self::Error>> + Send
       where Self: Sync { ... }
    fn conditional_delete(
        &self,
        resource_type: &str,
        id: &str,
        expected_version: &ScimVersion,
        context: &RequestContext,
    ) -> impl Future<Output = Result<ConditionalResult<()>, Self::Error>> + Send
       where Self: Sync { ... }
    fn get_versioned_resource(
        &self,
        resource_type: &str,
        id: &str,
        context: &RequestContext,
    ) -> impl Future<Output = Result<Option<VersionedResource>, Self::Error>> + Send
       where Self: Sync { ... }
    fn patch_resource(
        &self,
        resource_type: &str,
        id: &str,
        patch_request: &Value,
        context: &RequestContext,
    ) -> impl Future<Output = Result<Resource, Self::Error>> + Send
       where Self: Sync { ... }
    fn apply_patch_operation(
        &self,
        _resource_data: &mut Value,
        _operation: &Value,
    ) -> Result<(), Self::Error> { ... }
}
Expand description

Unified resource provider trait supporting both single and multi-tenant operations.

This trait provides a unified interface for SCIM resource operations that works for both single-tenant and multi-tenant scenarios:

  • Single-tenant: Operations use RequestContext with tenant_context = None
  • Multi-tenant: Operations use RequestContext with tenant_context = Some(…)

The provider implementation can check context.tenant_id() to determine the effective tenant for the operation.

Required Associated Types§

Source

type Error: Error + Send + Sync + 'static

Error type returned by all provider operations

Required Methods§

Source

fn create_resource( &self, resource_type: &str, data: Value, context: &RequestContext, ) -> impl Future<Output = Result<Resource, Self::Error>> + Send

Create a resource for the tenant specified in the request context.

§Arguments
  • resource_type - The type of resource to create (e.g., “User”, “Group”)
  • data - The resource data as JSON
  • context - Request context containing tenant information (if multi-tenant)
§Returns

The created resource with any server-generated fields (id, metadata, etc.)

§Tenant Handling
  • Single-tenant: context.tenant_id() returns None
  • Multi-tenant: context.tenant_id() returns Some(tenant_id)
Source

fn get_resource( &self, resource_type: &str, id: &str, context: &RequestContext, ) -> impl Future<Output = Result<Option<Resource>, Self::Error>> + Send

Get a resource by ID from the tenant specified in the request context.

§Arguments
  • resource_type - The type of resource to retrieve
  • id - The unique identifier of the resource
  • context - Request context containing tenant information (if multi-tenant)
§Returns

The resource if found, None if not found within the tenant scope

Source

fn update_resource( &self, resource_type: &str, id: &str, data: Value, context: &RequestContext, ) -> impl Future<Output = Result<Resource, Self::Error>> + Send

Update a resource in the tenant specified in the request context.

§Arguments
  • resource_type - The type of resource to update
  • id - The unique identifier of the resource
  • data - The updated resource data as JSON
  • context - Request context containing tenant information (if multi-tenant)
§Returns

The updated resource

Source

fn delete_resource( &self, resource_type: &str, id: &str, context: &RequestContext, ) -> impl Future<Output = Result<(), Self::Error>> + Send

Delete a resource from the tenant specified in the request context.

§Arguments
  • resource_type - The type of resource to delete
  • id - The unique identifier of the resource
  • context - Request context containing tenant information (if multi-tenant)
Source

fn list_resources( &self, resource_type: &str, _query: Option<&ListQuery>, context: &RequestContext, ) -> impl Future<Output = Result<Vec<Resource>, Self::Error>> + Send

List resources from the tenant specified in the request context.

§Arguments
  • resource_type - The type of resources to list
  • query - Optional query parameters for filtering, sorting, pagination
  • context - Request context containing tenant information (if multi-tenant)
§Returns

A vector of resources from the specified tenant

Source

fn find_resource_by_attribute( &self, resource_type: &str, attribute: &str, value: &Value, context: &RequestContext, ) -> impl Future<Output = Result<Option<Resource>, Self::Error>> + Send

Find a resource by attribute value within the tenant specified in the request context.

§Arguments
  • resource_type - The type of resource to search
  • attribute - The attribute name to search by
  • value - The attribute value to search for
  • context - Request context containing tenant information (if multi-tenant)
§Returns

The first matching resource, if found within the tenant scope

Source

fn resource_exists( &self, resource_type: &str, id: &str, context: &RequestContext, ) -> impl Future<Output = Result<bool, Self::Error>> + Send

Check if a resource exists within the tenant specified in the request context.

§Arguments
  • resource_type - The type of resource to check
  • id - The unique identifier of the resource
  • context - Request context containing tenant information (if multi-tenant)
§Returns

True if the resource exists within the tenant scope, false otherwise

Provided Methods§

Source

fn conditional_update( &self, resource_type: &str, id: &str, data: Value, expected_version: &ScimVersion, context: &RequestContext, ) -> impl Future<Output = Result<ConditionalResult<VersionedResource>, Self::Error>> + Send
where Self: Sync,

Conditionally update a resource if the version matches.

This operation will only succeed if the current resource version matches the expected version, preventing accidental overwriting of modified resources. This provides optimistic concurrency control for SCIM operations.

§ETag Concurrency Control

This method implements the core of ETag-based conditional operations:

  • Fetches the current resource and its version
  • Compares the current version with the expected version
  • Only proceeds with the update if versions match
  • Returns version conflict information if they don’t match
§Arguments
  • resource_type - The type of resource to update
  • id - The unique identifier of the resource
  • data - The updated resource data as JSON
  • expected_version - The version the client expects the resource to have
  • context - Request context containing tenant information
§Returns
  • Success(VersionedResource) - Update succeeded with new version
  • VersionMismatch(VersionConflict) - Resource was modified by another client
  • NotFound - Resource does not exist
§Default Implementation

The default implementation provides automatic conditional update support by checking the current resource version before performing the update. Providers can override this for more efficient implementations that perform version checking at the storage layer.

§Examples
use scim_server::resource::{
    provider::ResourceProvider,
    version::{ScimVersion, ConditionalResult},
    conditional_provider::VersionedResource,
    RequestContext,
};
use serde_json::json;

let context = RequestContext::with_generated_id();
let expected_version = ScimVersion::from_hash("abc123");
let update_data = json!({"userName": "new.name", "active": false});

match provider.conditional_update("User", "123", update_data, &expected_version, &context).await? {
    ConditionalResult::Success(versioned_resource) => {
        println!("Update successful, new version: {}",
                versioned_resource.version().to_http_header());
    },
    ConditionalResult::VersionMismatch(conflict) => {
        println!("Version conflict: expected {}, current {}",
                conflict.expected, conflict.current);
    },
    ConditionalResult::NotFound => {
        println!("Resource not found");
    }
}
Source

fn conditional_delete( &self, resource_type: &str, id: &str, expected_version: &ScimVersion, context: &RequestContext, ) -> impl Future<Output = Result<ConditionalResult<()>, Self::Error>> + Send
where Self: Sync,

Conditionally delete a resource if the version matches.

This operation will only succeed if the current resource version matches the expected version, preventing accidental deletion of modified resources. This is critical for maintaining data integrity in concurrent environments.

§ETag Concurrency Control

This method prevents accidental deletion of resources that have been modified by other clients:

  • Fetches the current resource and its version
  • Compares the current version with the expected version
  • Only proceeds with the deletion if versions match
  • Ensures the client is deleting the resource they intended to delete
§Arguments
  • resource_type - The type of resource to delete
  • id - The unique identifier of the resource
  • expected_version - The version the client expects the resource to have
  • context - Request context containing tenant information
§Returns
  • Success(()) - Delete succeeded
  • VersionMismatch(VersionConflict) - Resource was modified by another client
  • NotFound - Resource does not exist
§Default Implementation

The default implementation provides automatic conditional delete support by checking the current resource version before performing the delete. Providers can override this for more efficient implementations that perform version checking at the storage layer.

§Examples
use scim_server::resource::{
    provider::ResourceProvider,
    version::{ScimVersion, ConditionalResult},
    RequestContext,
};

let context = RequestContext::with_generated_id();
let expected_version = ScimVersion::from_hash("def456");

match provider.conditional_delete("User", "123", &expected_version, &context).await? {
    ConditionalResult::Success(()) => {
        println!("User deleted successfully");
    },
    ConditionalResult::VersionMismatch(conflict) => {
        println!("Cannot delete: resource was modified. Expected {}, current {}",
                conflict.expected, conflict.current);
    },
    ConditionalResult::NotFound => {
        println!("User not found");
    }
}
Source

fn get_versioned_resource( &self, resource_type: &str, id: &str, context: &RequestContext, ) -> impl Future<Output = Result<Option<VersionedResource>, Self::Error>> + Send
where Self: Sync,

Get a resource with its version information.

This is a convenience method that returns both the resource and its version information wrapped in a VersionedResource. This is useful when you need both the resource data and its version for subsequent conditional operations.

The default implementation calls the existing get_resource method and automatically wraps the result in a VersionedResource with a computed version.

§Arguments
  • resource_type - The type of resource to retrieve
  • id - The unique identifier of the resource
  • context - Request context containing tenant information
§Returns

The versioned resource if found, None if not found

§Examples
use scim_server::resource::{
    provider::ResourceProvider,
    RequestContext,
};

let context = RequestContext::with_generated_id();

if let Some(versioned_resource) = provider.get_versioned_resource("User", "123", &context).await? {
    println!("Resource ID: {}", versioned_resource.resource().get_id().unwrap_or("unknown"));
    println!("Resource version: {}", versioned_resource.version().to_http_header());

    // Can use the version for subsequent conditional operations
    let current_version = versioned_resource.version().clone();
    // ... use current_version for conditional_update or conditional_delete
}
Source

fn patch_resource( &self, resource_type: &str, id: &str, patch_request: &Value, context: &RequestContext, ) -> impl Future<Output = Result<Resource, Self::Error>> + Send
where Self: Sync,

Apply PATCH operations to a resource within the tenant specified in the request context.

§Arguments
  • resource_type - The type of resource to patch
  • id - The unique identifier of the resource
  • patch_request - The PATCH operation request as JSON (RFC 7644 Section 3.5.2)
  • context - Request context containing tenant information (if multi-tenant)
§Returns

The updated resource after applying the patch operations

§PATCH Operations

Supports the three SCIM PATCH operations:

  • add - Add new attribute values
  • remove - Remove attribute values
  • replace - Replace existing attribute values
§Default Implementation

The default implementation provides basic PATCH operation support by:

  1. Fetching the current resource
  2. Applying each operation in sequence
  3. Updating the resource with the modified data
Source

fn apply_patch_operation( &self, _resource_data: &mut Value, _operation: &Value, ) -> Result<(), Self::Error>

Apply a single PATCH operation to resource data.

This is a helper method used by the default patch_resource implementation. Providers can override this method to customize patch operation behavior.

§Arguments
  • resource_data - Mutable reference to the resource JSON data
  • operation - The patch operation to apply
§Returns

Result indicating success or failure of the operation

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§