camel-component-keycloak 0.18.0

Keycloak integration for rust-camel — claim presets, URL builders (Admin API and event consumer in future phases)
Documentation
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[allow(non_snake_case)]
pub struct EventRepresentation {
    #[serde(default)]
    pub id: Option<String>,
    #[serde(default)]
    pub time: Option<u64>,
    #[serde(default)]
    #[serde(rename = "type")]
    pub event_type: Option<String>,
    #[serde(default)]
    pub realmId: Option<String>,
    #[serde(default)]
    pub clientId: Option<String>,
    #[serde(default)]
    pub userId: Option<String>,
    #[serde(default)]
    pub sessionId: Option<String>,
    #[serde(default)]
    pub ipAddress: Option<String>,
    #[serde(default)]
    pub error: Option<String>,
    #[serde(default)]
    pub details: Option<std::collections::HashMap<String, String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[allow(non_snake_case)]
pub struct AdminEventRepresentation {
    #[serde(default)]
    pub id: Option<String>,
    #[serde(default)]
    pub time: Option<u64>,
    #[serde(default)]
    pub realmId: Option<String>,
    #[serde(default)]
    pub authDetails: Option<AuthDetailsRepresentation>,
    #[serde(default)]
    pub operationType: Option<String>,
    #[serde(default)]
    pub resourceType: Option<String>,
    #[serde(default)]
    pub resourcePath: Option<String>,
    #[serde(default)]
    pub representation: Option<String>,
    #[serde(default)]
    pub error: Option<String>,
    #[serde(default)]
    pub details: Option<std::collections::HashMap<String, String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[allow(non_snake_case)]
pub struct AuthDetailsRepresentation {
    #[serde(default)]
    pub realmId: Option<String>,
    #[serde(default)]
    pub clientId: Option<String>,
    #[serde(default)]
    pub userId: Option<String>,
    #[serde(default)]
    pub ipAddress: Option<String>,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn deserialize_user_event() {
        let json = serde_json::json!({
            "id": "event-uuid-1",
            "time": 1700000000000_u64,
            "type": "LOGIN",
            "realmId": "test-realm",
            "clientId": "my-client",
            "userId": "user-123",
            "sessionId": "session-abc",
            "ipAddress": "192.168.1.1",
            "details": { "custom": "value" }
        });
        let event: EventRepresentation = serde_json::from_value(json).unwrap();
        assert_eq!(event.id.as_deref(), Some("event-uuid-1"));
        assert_eq!(event.time, Some(1700000000000));
        assert_eq!(event.event_type.as_deref(), Some("LOGIN"));
        assert_eq!(event.userId.as_deref(), Some("user-123"));
        assert_eq!(event.details.unwrap().get("custom").unwrap(), "value");
    }

    #[test]
    fn deserialize_user_event_minimal() {
        let json = serde_json::json!({});
        let event: EventRepresentation = serde_json::from_value(json).unwrap();
        assert!(event.id.is_none());
        assert!(event.time.is_none());
    }

    #[test]
    fn deserialize_admin_event() {
        let json = serde_json::json!({
            "id": "admin-event-1",
            "time": 1700000000001_u64,
            "realmId": "test-realm",
            "authDetails": {
                "realmId": "master",
                "clientId": "admin-cli",
                "userId": "admin-user",
                "ipAddress": "10.0.0.1"
            },
            "operationType": "CREATE",
            "resourceType": "USER",
            "resourcePath": "users/user-456"
        });
        let event: AdminEventRepresentation = serde_json::from_value(json).unwrap();
        assert_eq!(event.id.as_deref(), Some("admin-event-1"));
        assert_eq!(event.operationType.as_deref(), Some("CREATE"));
        assert_eq!(event.resourceType.as_deref(), Some("USER"));
        let auth = event.authDetails.unwrap();
        assert_eq!(auth.userId.as_deref(), Some("admin-user"));
    }

    #[test]
    fn deserialize_admin_event_minimal() {
        let json = serde_json::json!({});
        let event: AdminEventRepresentation = serde_json::from_value(json).unwrap();
        assert!(event.id.is_none());
        assert!(event.authDetails.is_none());
    }

    #[test]
    fn deserialize_event_array() {
        let json = serde_json::json!([
            { "id": "e1", "type": "LOGIN", "time": 100 },
            { "id": "e2", "type": "LOGOUT", "time": 200 }
        ]);
        let events: Vec<EventRepresentation> = serde_json::from_value(json).unwrap();
        assert_eq!(events.len(), 2);
        assert_eq!(events[0].event_type.as_deref(), Some("LOGIN"));
        assert_eq!(events[1].event_type.as_deref(), Some("LOGOUT"));
    }
}