saddle-framework 0.3.0-alpha.7

The single business-facing facade for Saddle applications
#![allow(dead_code)]

use prost::Message;
use saddle::{
    application,
    ingress::{
        FailureMessage, ProfuseGwCode, ProfuseGwContext, ProfuseGwDispatchError, ProfuseGwFailure,
        ProfuseGwResponse as Response,
    },
    profusecontract::{ExternalFunctionResult, testing::FakeProfuseContractBoundary},
};
use serde::{Deserialize, Serialize};

#[derive(Clone, PartialEq, Message, Deserialize)]
#[serde(deny_unknown_fields)]
struct QueryMyBoundAccountsRequest {}
#[derive(Clone, PartialEq, Message, Serialize)]
struct QueryMyBoundAccountsResult {
    #[prost(bool, tag = "1")]
    found: bool,
}
#[derive(Clone, PartialEq, Message)]
struct QueryBoundAccountsRequest {
    #[prost(string, tag = "1")]
    user_id: String,
}
#[derive(Clone, PartialEq, Message)]
struct QueryBoundAccountsResult {
    #[prost(uint64, tag = "1")]
    account_count: u64,
}

enum ApplicationCode {
    DependencyFailed,
}

impl ProfuseGwCode for ApplicationCode {
    const REGISTERED_CODES: &'static [&'static str] = &["TECHNICAL_DEPENDENCY_FAILED"];

    fn stable_code(&self) -> &'static str {
        match self {
            Self::DependencyFailed => "TECHNICAL_DEPENDENCY_FAILED",
        }
    }
}

fn technical_failure_response(
    failure: saddle::profusecontract::TechnicalFailure,
) -> Response<QueryMyBoundAccountsResult, ApplicationCode> {
    use saddle::profusecontract::{ExecutionCertainty::*, TechnicalFailureCode::*};
    let view = match (failure.code(), failure.certainty()) {
        (FunctionNotFound, NotExecuted) => "Function is unavailable",
        (FunctionNotFound, Executed) => "Function execution failed",
        (FunctionNotFound, MayHaveExecuted) => "Function outcome is unavailable",
        (FunctionRequestInvalid, NotExecuted) => "Request was rejected",
        (FunctionRequestInvalid, Executed) => "Request processing failed",
        (FunctionRequestInvalid, MayHaveExecuted) => "Request outcome is unavailable",
        (CapacityRejected, NotExecuted) => "Service is busy",
        (CapacityRejected, Executed) => "Service capacity failed",
        (CapacityRejected, MayHaveExecuted) => "Service outcome is unavailable",
        (DeadlineExceeded, NotExecuted) => "Request expired",
        (DeadlineExceeded, Executed) => "Response deadline expired",
        (DeadlineExceeded, MayHaveExecuted) => "Timed out with unknown outcome",
        (DependencyUnavailable, NotExecuted) => "Account lookup is unavailable",
        (DependencyUnavailable, Executed) => "Account lookup failed",
        (DependencyUnavailable, MayHaveExecuted) => "Account lookup outcome is unavailable",
        (ContractResultInvalid, NotExecuted) => "Contract result is invalid",
        (ContractResultInvalid, Executed) => "Contract response is invalid",
        (ContractResultInvalid, MayHaveExecuted) => "Contract outcome is invalid",
        (InternalFailure, NotExecuted) => "Service failed before execution",
        (InternalFailure, Executed) => "Service execution failed",
        (InternalFailure, MayHaveExecuted) => "Service outcome is unavailable",
        (TransportFailure, NotExecuted) => "Transport is unavailable",
        (TransportFailure, Executed) => "Transport failed after execution",
        (TransportFailure, MayHaveExecuted) => "Transport outcome is unavailable",
    };
    Response::failure(ProfuseGwFailure::new(
        ApplicationCode::DependencyFailed,
        FailureMessage::new(view).unwrap(),
    ))
}

async fn query_my_bound_accounts(
    _request: QueryMyBoundAccountsRequest,
    context: ProfuseGwContext,
    contract: PucProfuseContract,
) -> Response<QueryMyBoundAccountsResult, ApplicationCode> {
    let result = contract
        .query_bound_accounts(QueryBoundAccountsRequest {
            user_id: context.user_id().to_owned(),
        })
        .await;
    match result {
        ExternalFunctionResult::Completed(result) => {
            Response::success(QueryMyBoundAccountsResult {
                found: result.account_count > 0,
            })
        }
        ExternalFunctionResult::TechnicalFailure(failure) => technical_failure_response(failure),
    }
}

application! {
    schema "saddle-application/2";
    application PucBackend;
    deployment_app "puc-backend";
    profusecontract {
        contract_dir "examples/contracts";
        capability PucProfuseContract;
        response_code ApplicationCode;
        functions {
            QueryBoundAccounts => query_bound_accounts {
                business_unit "puc";
                function "查询用户绑定户号";
            } (
                QueryBoundAccountsRequest
            ) -> QueryBoundAccountsResult;
        }
    }
    service QueryMyBoundAccounts {
        ingress profusegw;
        operation_type "alipay.profuse.industry.puc.queryMyBoundAccounts";
        request QueryMyBoundAccountsRequest;
        response QueryMyBoundAccountsResult;
        handler query_my_bound_accounts;
        uses QueryBoundAccounts;
    }
}

#[tokio::main]
async fn main() {
    assert_eq!(PucBackend::PROFUSECONTRACT_DIR, "examples/contracts");
    assert!(!PucBackend::__PROFUSECONTRACT_DESCRIPTOR.is_empty());
    assert_eq!(
        QueryMyBoundAccounts::OPERATION_TYPE,
        "alipay.profuse.industry.puc.queryMyBoundAccounts"
    );
    assert_eq!(
        PucBackend::dispatch_profusegw_operation(
            "alipay.profuse.industry.puc.queryMyBoundAccounts"
        ),
        Some(ProfuseGwDispatch::QueryMyBoundAccounts)
    );
    assert_eq!(PucBackend::dispatch_profusegw_operation("unknown"), None);

    let identity = saddle_boundary::ingress::IngressIdentity::new(
        "request-1",
        "ingress-call",
        1_800_000_000_000,
    )
    .unwrap();
    let accepted = PucBackend::__profusegw_listener_adapter()
        .accept(
            saddle_boundary::ingress::METHOD,
            saddle_boundary::ingress::PATH,
            saddle_boundary::ingress::MEDIA_TYPE,
            identity,
            br#"{
              "target":{"app":"puc-backend","interfaceId":"alipay.profuse.industry.puc.queryMyBoundAccounts"},
              "profuseGwContext":{"userInfo":{"userId":"2088-user"}},
              "requestData":{}
            }"#,
        )
        .unwrap();
    let fake =
        FakeProfuseContractBoundary::completed(QueryBoundAccountsResult { account_count: 2 });
    let observed = fake.clone();
    let seal = fake.bind_accepted(&accepted).unwrap().into_contract_seal();
    let response = PucBackend::__dispatch_accepted_profusegw(
        accepted,
        PucProfuseContract::__from_framework(seal),
    )
    .await
    .unwrap();
    assert_eq!(
        serde_json::to_string(&response).unwrap(),
        r#"{"success":true,"data":{"found":true}}"#
    );
    assert_eq!(
        response.__encode_profusegw_http1().unwrap(),
        b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: 38\r\nConnection: close\r\n\r\n{\"success\":true,\"data\":{\"found\":true}}"
    );
    let failure =
        ProfuseGwResponse::QueryMyBoundAccounts(Response::failure(ProfuseGwFailure::new(
            ApplicationCode::DependencyFailed,
            FailureMessage::new("Dependency failed").unwrap(),
        )));
    assert_eq!(
        serde_json::to_string(&failure).unwrap(),
        r#"{"success":false,"failure":{"code":"TECHNICAL_DEPENDENCY_FAILED","message":"Dependency failed"}}"#
    );
    assert_eq!(
        failure.__encode_profusegw_http1().unwrap(),
        b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: 96\r\nConnection: close\r\n\r\n{\"success\":false,\"failure\":{\"code\":\"TECHNICAL_DEPENDENCY_FAILED\",\"message\":\"Dependency failed\"}}"
    );
    match response {
        ProfuseGwResponse::QueryMyBoundAccounts(response) => {
            assert!(response.success_data().unwrap().found)
        }
    }
    let attempts = observed.attempts();
    assert_eq!(attempts[0].request_id, "request-1");
    assert_eq!(attempts[0].call_id, "ingress-call-1");
    assert_eq!(attempts[0].user_id, "2088-user");

    let foreign = PucBackend::__profusegw_listener_adapter()
        .accept(
            saddle_boundary::ingress::METHOD,
            saddle_boundary::ingress::PATH,
            saddle_boundary::ingress::MEDIA_TYPE,
            saddle_boundary::ingress::IngressIdentity::new("foreign", "call", 1_800_000_000_000)
                .unwrap(),
            br#"{
              "target":{"app":"other-app","interfaceId":"alipay.profuse.industry.puc.queryMyBoundAccounts"},
              "profuseGwContext":{"userInfo":{"userId":"2088-user"}},
              "requestData":{}
            }"#,
        )
        .unwrap_err();
    assert_eq!(foreign.code, "APPLICATION_NOT_FOUND");

    let unknown = PucBackend::__profusegw_listener_adapter()
        .accept(
            saddle_boundary::ingress::METHOD,
            saddle_boundary::ingress::PATH,
            saddle_boundary::ingress::MEDIA_TYPE,
            saddle_boundary::ingress::IngressIdentity::new("unknown", "call", 1_800_000_000_000)
                .unwrap(),
            br#"{
              "target":{"app":"puc-backend","interfaceId":"unknown.interface"},
              "profuseGwContext":{"userInfo":{"userId":"2088-user"}},
              "requestData":{}
            }"#,
        )
        .unwrap();
    let seal =
        FakeProfuseContractBoundary::completed(QueryBoundAccountsResult { account_count: 0 })
            .bind_accepted(&unknown)
            .unwrap()
            .into_contract_seal();
    assert!(matches!(
        PucBackend::__dispatch_accepted_profusegw(
            unknown,
            PucProfuseContract::__from_framework(seal)
        )
        .await,
        Err(ProfuseGwDispatchError::InterfaceNotFound)
    ));

    let invalid_request = PucBackend::__profusegw_listener_adapter()
        .accept(
            saddle_boundary::ingress::METHOD,
            saddle_boundary::ingress::PATH,
            saddle_boundary::ingress::MEDIA_TYPE,
            saddle_boundary::ingress::IngressIdentity::new("invalid", "call", 1_800_000_000_000)
                .unwrap(),
            br#"{
              "target":{"app":"puc-backend","interfaceId":"alipay.profuse.industry.puc.queryMyBoundAccounts"},
              "profuseGwContext":{"userInfo":{"userId":"2088-user"}},
              "requestData":{"unexpected":true}
            }"#,
        )
        .unwrap();
    let seal =
        FakeProfuseContractBoundary::completed(QueryBoundAccountsResult { account_count: 0 })
            .bind_accepted(&invalid_request)
            .unwrap()
            .into_contract_seal();
    assert!(matches!(
        PucBackend::__dispatch_accepted_profusegw(
            invalid_request,
            PucProfuseContract::__from_framework(seal)
        )
        .await,
        Err(ProfuseGwDispatchError::RequestDataInvalid)
    ));

    let binding =
        FakeProfuseContractBoundary::completed(QueryBoundAccountsResult { account_count: 2 })
            .bind("2088-user", 1_800_000_000_000);
    let context = binding.profusegw_context();
    let seal = binding.into_contract_seal();
    let response = query_my_bound_accounts(
        QueryMyBoundAccountsRequest {},
        context,
        PucProfuseContract::__from_framework(seal),
    )
    .await;
    assert!(response.success_data().unwrap().found);
}