#![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);
}