lenso-capability-http-endpoint 0.2.0

Portable HTTP Endpoint Capability contract for Lenso vNext backend Modules.
Documentation
// @generated by lenso-contract-codegen; do not edit.
use std::{fmt, rc::Rc};
use futures::future::LocalBoxFuture;
use lenso_kernel::{InvocationContext, ModuleDependencies, NativeRequestEndpoint, NativeRequestFuture, NativeRequestHandle, RequestCapability, RuntimeFailure};

pub const CAPABILITY_ID: &str = "lenso.http.endpoint@1";
pub const DESCRIPTOR_VERSION: &str = "1.0.1";
pub const PORTABLE: bool = true;
pub const CROSS_LANE_TRANSFER: bool = true;
pub const ENDPOINT_CAPABILITY_ID: &str = CAPABILITY_ID;
pub const ENDPOINT_DESCRIPTOR_VERSION: &str = DESCRIPTOR_VERSION;

pub const DESCRIBE_OPERATION: &str = "describe";
pub const HANDLE_OPERATION: &str = "handle";

pub use lenso_contract_runtime::{Bytes, UnknownDomainError};
use lenso_contract_runtime::{decode_portable_json, encode_portable_json};

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct DescribeRequest {

}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct DescribeResponse {
    #[serde(rename = "routes")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub routes: Vec<DescribeResponseRoutesItem>,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct DescribeResponseRoutesItem {
    #[serde(rename = "method")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub method: String,
    #[serde(rename = "path")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub path: String,
    #[serde(rename = "route_id")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub route_id: String,
}

#[derive(Clone, Debug, PartialEq)]
pub enum DescribeError {
    InvalidConfiguration,
    Unknown(UnknownDomainError),
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct HandleRequest {
    #[serde(rename = "body")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub body: Bytes,
    #[serde(rename = "credential")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub credential: Option<HandleRequestCredential>,
    #[serde(rename = "headers")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub headers: Vec<HandleRequestHeadersItem>,
    #[serde(rename = "method")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub method: String,
    #[serde(rename = "path")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub path: String,
    #[serde(rename = "path_parameters")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub path_parameters: Vec<HandleRequestPathParametersItem>,
    #[serde(rename = "query")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub query: Option<String>,
    #[serde(rename = "request_id")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub request_id: String,
    #[serde(rename = "route_id")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub route_id: String,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct HandleRequestCredential {
    #[serde(rename = "scheme")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub scheme: String,
    #[serde(rename = "value")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub value: String,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct HandleRequestHeadersItem {
    #[serde(rename = "name")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub name: String,
    #[serde(rename = "value")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub value: String,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct HandleRequestPathParametersItem {
    #[serde(rename = "name")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub name: String,
    #[serde(rename = "value")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub value: String,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct HandleResponse {
    #[serde(rename = "body")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub body: Bytes,
    #[serde(rename = "headers")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub headers: Vec<HandleResponseHeadersItem>,
    #[serde(rename = "status")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub status: i64,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct HandleResponseHeadersItem {
    #[serde(rename = "name")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub name: String,
    #[serde(rename = "value")]
    #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
    pub value: String,
}

#[derive(Clone, Debug, PartialEq)]
pub enum HandleError {
    Rejected,
    Unknown(UnknownDomainError),
}

#[derive(Debug)]
pub struct EndpointDescribe;
impl RequestCapability for EndpointDescribe {
    type Request = DescribeRequest;
    type Response = DescribeResponse;
    type DomainError = DescribeError;
    const ID: &'static str = CAPABILITY_ID;
    const DESCRIPTOR_VERSION: &'static str = DESCRIPTOR_VERSION;

    fn invoke_native(endpoint: &dyn NativeRequestEndpoint, operation: &str, request: Self::Request, context: InvocationContext) -> NativeRequestFuture<Self> {
        if operation != DESCRIBE_OPERATION {
            return lenso_kernel::invoke_typed_or_erased_native_request::<Self>(endpoint, operation, request, context);
        }
        let Some(typed_endpoint) = endpoint
            .typed_endpoint()
            .and_then(|endpoint| endpoint.downcast_ref::<EndpointRequestEndpoint>())
        else {
            return lenso_kernel::invoke_typed_or_erased_native_request::<Self>(endpoint, operation, request, context);
        };
        Rc::clone(&typed_endpoint.provider).describe(context, request)
    }
}

#[derive(Debug)]
pub struct EndpointHandle;
impl RequestCapability for EndpointHandle {
    type Request = HandleRequest;
    type Response = HandleResponse;
    type DomainError = HandleError;
    const ID: &'static str = CAPABILITY_ID;
    const DESCRIPTOR_VERSION: &'static str = DESCRIPTOR_VERSION;

    fn invoke_native(endpoint: &dyn NativeRequestEndpoint, operation: &str, request: Self::Request, context: InvocationContext) -> NativeRequestFuture<Self> {
        if operation != HANDLE_OPERATION {
            return lenso_kernel::invoke_typed_or_erased_native_request::<Self>(endpoint, operation, request, context);
        }
        let Some(typed_endpoint) = endpoint
            .typed_endpoint()
            .and_then(|endpoint| endpoint.downcast_ref::<EndpointRequestEndpoint>())
        else {
            return lenso_kernel::invoke_typed_or_erased_native_request::<Self>(endpoint, operation, request, context);
        };
        Rc::clone(&typed_endpoint.provider).handle(context, request)
    }
}

impl serde::Serialize for DescribeError {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        use serde::ser::SerializeMap;
        match self {
            Self::InvalidConfiguration => serializer.serialize_str("invalid_configuration"),
            Self::Unknown(value) => {
                let mut map = serializer.serialize_map(Some(1 + usize::from(value.payload.is_some()) + value.extra.len()))?;
                map.serialize_entry("code", &value.code)?;
                if let Some(payload) = &value.payload {
                    map.serialize_entry("payload", payload)?;
                }
                for (key, extra) in &value.extra {
                    map.serialize_entry(key, extra)?;
                }
                map.end()
            },
        }
    }
}

impl<'de> serde::Deserialize<'de> for DescribeError {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let value = <serde_json::Value as serde::Deserialize>::deserialize(deserializer)?;
        match value {
            serde_json::Value::String(code) => match code.as_str() {
                "invalid_configuration" => Ok(Self::InvalidConfiguration),
                _ => Ok(Self::Unknown(UnknownDomainError { code, payload: None, extra: std::collections::BTreeMap::new() })),
            },
            serde_json::Value::Object(mut object) => {
                let Some(code) = object.remove("code").and_then(|value| value.as_str().map(ToOwned::to_owned)) else {
                    return Err(serde::de::Error::custom("Domain Error object is missing a string code"));
                };
                let payload = object.remove("payload");
                let extra = object.into_iter().collect::<std::collections::BTreeMap<_, _>>();
                Ok(Self::Unknown(UnknownDomainError { code, payload, extra }))
            }
            other => Err(serde::de::Error::custom(format!("Domain Error must be a string or object, got {other}"))),
        }
    }
}

impl serde::Serialize for HandleError {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        use serde::ser::SerializeMap;
        match self {
            Self::Rejected => serializer.serialize_str("rejected"),
            Self::Unknown(value) => {
                let mut map = serializer.serialize_map(Some(1 + usize::from(value.payload.is_some()) + value.extra.len()))?;
                map.serialize_entry("code", &value.code)?;
                if let Some(payload) = &value.payload {
                    map.serialize_entry("payload", payload)?;
                }
                for (key, extra) in &value.extra {
                    map.serialize_entry(key, extra)?;
                }
                map.end()
            },
        }
    }
}

impl<'de> serde::Deserialize<'de> for HandleError {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let value = <serde_json::Value as serde::Deserialize>::deserialize(deserializer)?;
        match value {
            serde_json::Value::String(code) => match code.as_str() {
                "rejected" => Ok(Self::Rejected),
                _ => Ok(Self::Unknown(UnknownDomainError { code, payload: None, extra: std::collections::BTreeMap::new() })),
            },
            serde_json::Value::Object(mut object) => {
                let Some(code) = object.remove("code").and_then(|value| value.as_str().map(ToOwned::to_owned)) else {
                    return Err(serde::de::Error::custom("Domain Error object is missing a string code"));
                };
                let payload = object.remove("payload");
                let extra = object.into_iter().collect::<std::collections::BTreeMap<_, _>>();
                Ok(Self::Unknown(UnknownDomainError { code, payload, extra }))
            }
            other => Err(serde::de::Error::custom(format!("Domain Error must be a string or object, got {other}"))),
        }
    }
}

pub fn encode_describe_request(value: &DescribeRequest) -> Result<String, serde_json::Error> { encode_portable_json(value) }
pub fn decode_describe_request(wire: &str) -> Result<DescribeRequest, serde_json::Error> { decode_portable_json(wire) }
pub fn encode_describe_response(value: &DescribeResponse) -> Result<String, serde_json::Error> { encode_portable_json(value) }
pub fn decode_describe_response(wire: &str) -> Result<DescribeResponse, serde_json::Error> { decode_portable_json(wire) }
pub fn encode_describe_error(value: &DescribeError) -> Result<String, serde_json::Error> { encode_portable_json(value) }
pub fn decode_describe_error(wire: &str) -> Result<DescribeError, serde_json::Error> { decode_portable_json(wire) }

pub fn encode_handle_request(value: &HandleRequest) -> Result<String, serde_json::Error> { encode_portable_json(value) }
pub fn decode_handle_request(wire: &str) -> Result<HandleRequest, serde_json::Error> { decode_portable_json(wire) }
pub fn encode_handle_response(value: &HandleResponse) -> Result<String, serde_json::Error> { encode_portable_json(value) }
pub fn decode_handle_response(wire: &str) -> Result<HandleResponse, serde_json::Error> { decode_portable_json(wire) }
pub fn encode_handle_error(value: &HandleError) -> Result<String, serde_json::Error> { encode_portable_json(value) }
pub fn decode_handle_error(wire: &str) -> Result<HandleError, serde_json::Error> { decode_portable_json(wire) }

pub trait EndpointProvider: fmt::Debug + 'static {
    fn describe(&self, context: InvocationContext, request: DescribeRequest) -> NativeRequestFuture<EndpointDescribe>;
    fn handle(&self, context: InvocationContext, request: HandleRequest) -> NativeRequestFuture<EndpointHandle>;
}

#[derive(Debug)]
struct EndpointRequestEndpoint { provider: Rc<dyn EndpointProvider> }

#[derive(Debug)]
pub struct EndpointEndpoint<P: EndpointProvider> { provider: Rc<P>, request_endpoint: EndpointRequestEndpoint }
impl<P: EndpointProvider> EndpointEndpoint<P> {
    pub fn new(provider: P) -> Self {
        let provider = Rc::new(provider);
        let request_provider: Rc<dyn EndpointProvider> = provider.clone();
        Self { provider, request_endpoint: EndpointRequestEndpoint { provider: request_provider } }
    }
}

impl<P: EndpointProvider> NativeRequestEndpoint for EndpointEndpoint<P> {
    fn capability_id(&self) -> &'static str { CAPABILITY_ID }
    fn descriptor_version(&self) -> &'static str { DESCRIPTOR_VERSION }
    fn operations(&self) -> &'static [&'static str] { &[
        DESCRIBE_OPERATION,
        HANDLE_OPERATION,
    ] }
    fn typed_endpoint(&self) -> Option<&dyn std::any::Any> { Some(&self.request_endpoint) }
    fn invoke(&self, operation: &str, request: Box<dyn std::any::Any>, context: InvocationContext) -> LocalBoxFuture<'static, Result<Result<Box<dyn std::any::Any>, Box<dyn std::any::Any>>, RuntimeFailure>> {
        match operation {
            DESCRIBE_OPERATION => {
                let Ok(request) = request.downcast::<DescribeRequest>() else {
                    return Box::pin(futures::future::ready(Err(RuntimeFailure::ProtocolViolation { capability: CAPABILITY_ID })));
                };
                let invocation = Rc::clone(&self.provider).describe(context, *request);
                Box::pin(async move {
                    invocation.await.map(|result| {
                        result
                            .map(|value| Box::new(value) as Box<dyn std::any::Any>)
                            .map_err(|error| Box::new(error) as Box<dyn std::any::Any>)
                    })
                })
            },
            HANDLE_OPERATION => {
                let Ok(request) = request.downcast::<HandleRequest>() else {
                    return Box::pin(futures::future::ready(Err(RuntimeFailure::ProtocolViolation { capability: CAPABILITY_ID })));
                };
                let invocation = Rc::clone(&self.provider).handle(context, *request);
                Box::pin(async move {
                    invocation.await.map(|result| {
                        result
                            .map(|value| Box::new(value) as Box<dyn std::any::Any>)
                            .map_err(|error| Box::new(error) as Box<dyn std::any::Any>)
                    })
                })
            }
            _ => Box::pin(futures::future::ready(Err(RuntimeFailure::UnknownOperation { capability: CAPABILITY_ID, operation: operation.to_owned() }))),
        }
    }
}

#[derive(Debug)]
pub struct EndpointClient {
    describe: NativeRequestHandle<EndpointDescribe>,
    handle: NativeRequestHandle<EndpointHandle>,
}
impl EndpointClient {
    pub fn from_dependencies(dependencies: &ModuleDependencies) -> Result<Self, RuntimeFailure> {
        Ok(Self {
            describe: dependencies.one::<EndpointDescribe>()?,
            handle: dependencies.one::<EndpointHandle>()?,
        })
    }

    pub async fn describe(&self, request: DescribeRequest) -> Result<DescribeResponse, EndpointDescribeInvocationError> {
        self.describe.invoke(DESCRIBE_OPERATION, request).await
            .map_err(EndpointDescribeInvocationError::Runtime)?
            .map_err(EndpointDescribeInvocationError::Domain)
    }

    pub async fn describe_with_context(&self, context: InvocationContext, request: DescribeRequest) -> Result<DescribeResponse, EndpointDescribeInvocationError> {
        self.describe.invoke_with_context(DESCRIBE_OPERATION, context, request).await
            .map_err(EndpointDescribeInvocationError::Runtime)?
            .map_err(EndpointDescribeInvocationError::Domain)
    }

    pub async fn handle(&self, request: HandleRequest) -> Result<HandleResponse, EndpointHandleInvocationError> {
        self.handle.invoke(HANDLE_OPERATION, request).await
            .map_err(EndpointHandleInvocationError::Runtime)?
            .map_err(EndpointHandleInvocationError::Domain)
    }

    pub async fn handle_with_context(&self, context: InvocationContext, request: HandleRequest) -> Result<HandleResponse, EndpointHandleInvocationError> {
        self.handle.invoke_with_context(HANDLE_OPERATION, context, request).await
            .map_err(EndpointHandleInvocationError::Runtime)?
            .map_err(EndpointHandleInvocationError::Domain)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum EndpointDescribeInvocationError {
    Domain(DescribeError),
    Runtime(RuntimeFailure),
}
#[derive(Clone, Debug, PartialEq)]
pub enum EndpointHandleInvocationError {
    Domain(HandleError),
    Runtime(RuntimeFailure),
}