mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-13 17:35:37 +00:00
feat(web): full-power RPC access + typed JSON proxy endpoint (#1983)
- extend web controller bindings to cover full RPC service set - update rpc_service API wiring and session/controller integration - generate trait-level json_call_method in rpc codegen - route restful proxy-rpc requests via scoped typed clients - add json-call regression tests and required Sync bound fixes~
This commit is contained in:
@@ -260,3 +260,99 @@ pub mod logger {
|
||||
pub mod manage {
|
||||
include!(concat!(env!("OUT_DIR"), "/api.manage.rs"));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bytes::Bytes;
|
||||
use prost::Message;
|
||||
|
||||
use super::manage::{
|
||||
ListNetworkInstanceRequest, ListNetworkInstanceResponse, WebClientService,
|
||||
WebClientServiceClient, WebClientServiceDescriptor, WebClientServiceMethodDescriptor,
|
||||
};
|
||||
use crate::proto::common::Uuid;
|
||||
use crate::proto::rpc_types::controller::BaseController;
|
||||
use crate::proto::rpc_types::descriptor::ServiceDescriptor;
|
||||
use crate::proto::rpc_types::error::Error;
|
||||
use crate::proto::rpc_types::handler::Handler;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct WebClientServiceJsonCallHandler;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Handler for WebClientServiceJsonCallHandler {
|
||||
type Descriptor = WebClientServiceDescriptor;
|
||||
type Controller = BaseController;
|
||||
|
||||
async fn call(
|
||||
&self,
|
||||
_ctrl: Self::Controller,
|
||||
method: <Self::Descriptor as ServiceDescriptor>::Method,
|
||||
input: Bytes,
|
||||
) -> crate::proto::rpc_types::error::Result<Bytes> {
|
||||
match method {
|
||||
WebClientServiceMethodDescriptor::ListNetworkInstance => {
|
||||
let _req = ListNetworkInstanceRequest::decode(input.as_ref()).unwrap();
|
||||
let resp = ListNetworkInstanceResponse {
|
||||
inst_ids: vec![Uuid {
|
||||
part1: 1,
|
||||
part2: 2,
|
||||
part3: 3,
|
||||
part4: 4,
|
||||
}],
|
||||
};
|
||||
Ok(Bytes::from(resp.encode_to_vec()))
|
||||
}
|
||||
_ => Err(Error::ExecutionError(anyhow::anyhow!(
|
||||
"unsupported method in test handler"
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn web_client_service_call_json_method_supports_snake_and_proto_method_name() {
|
||||
let client = WebClientServiceClient::new(WebClientServiceJsonCallHandler);
|
||||
|
||||
let snake_result = client
|
||||
.json_call_method(
|
||||
BaseController::default(),
|
||||
"list_network_instance",
|
||||
serde_json::json!({}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
snake_result["inst_ids"][0],
|
||||
serde_json::json!({
|
||||
"part1": 1,
|
||||
"part2": 2,
|
||||
"part3": 3,
|
||||
"part4": 4
|
||||
})
|
||||
);
|
||||
|
||||
let proto_result = client
|
||||
.json_call_method(
|
||||
BaseController::default(),
|
||||
"ListNetworkInstance",
|
||||
serde_json::json!({}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(proto_result["inst_ids"].as_array().unwrap().len(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn web_client_service_call_json_method_rejects_unknown_method() {
|
||||
let client = WebClientServiceClient::new(WebClientServiceJsonCallHandler);
|
||||
let ret = client
|
||||
.json_call_method(
|
||||
BaseController::default(),
|
||||
"not_exist_method",
|
||||
serde_json::json!({}),
|
||||
)
|
||||
.await;
|
||||
assert!(ret.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,101 @@ use tokio::task::JoinSet;
|
||||
|
||||
use super::rpc_impl::RpcController;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct GreetingJsonCallHandler;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl crate::proto::rpc_types::handler::Handler for GreetingJsonCallHandler {
|
||||
type Descriptor = GreetingDescriptor;
|
||||
type Controller = crate::proto::rpc_types::controller::BaseController;
|
||||
|
||||
async fn call(
|
||||
&self,
|
||||
_ctrl: Self::Controller,
|
||||
method: <Self::Descriptor as crate::proto::rpc_types::descriptor::ServiceDescriptor>::Method,
|
||||
input: bytes::Bytes,
|
||||
) -> crate::proto::rpc_types::error::Result<bytes::Bytes> {
|
||||
use prost::Message;
|
||||
match method {
|
||||
GreetingMethodDescriptor::SayHello => {
|
||||
let req = SayHelloRequest::decode(input)?;
|
||||
let resp = SayHelloResponse {
|
||||
greeting: format!("Hello {}!", req.name),
|
||||
};
|
||||
Ok(bytes::Bytes::from(resp.encode_to_vec()))
|
||||
}
|
||||
GreetingMethodDescriptor::SayGoodbye => {
|
||||
let req = SayGoodbyeRequest::decode(input)?;
|
||||
let resp = SayGoodbyeResponse {
|
||||
greeting: format!("Goodbye, {}!", req.name),
|
||||
};
|
||||
Ok(bytes::Bytes::from(resp.encode_to_vec()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn greeting_client_json_call_method_supports_snake_and_proto_method_name() {
|
||||
let client = GreetingClient::new(GreetingJsonCallHandler);
|
||||
|
||||
let snake = client
|
||||
.json_call_method(
|
||||
crate::proto::rpc_types::controller::BaseController::default(),
|
||||
"say_hello",
|
||||
serde_json::json!({"name": "world"}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(snake["greeting"], serde_json::json!("Hello world!"));
|
||||
|
||||
let proto = client
|
||||
.json_call_method(
|
||||
crate::proto::rpc_types::controller::BaseController::default(),
|
||||
"SayHello",
|
||||
serde_json::json!({"name": "world"}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(proto["greeting"], serde_json::json!("Hello world!"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn greeting_client_json_call_method_rejects_invalid_json() {
|
||||
let client = GreetingClient::new(GreetingJsonCallHandler);
|
||||
|
||||
let err = client
|
||||
.json_call_method(
|
||||
crate::proto::rpc_types::controller::BaseController::default(),
|
||||
"say_hello",
|
||||
serde_json::json!({"name": 123}),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
crate::proto::rpc_types::error::Error::MalformatRpcPacket(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn greeting_client_json_call_method_rejects_unknown_method() {
|
||||
let client = GreetingClient::new(GreetingJsonCallHandler);
|
||||
|
||||
let err = client
|
||||
.json_call_method(
|
||||
crate::proto::rpc_types::controller::BaseController::default(),
|
||||
"not_exist_method",
|
||||
serde_json::json!({"name": "world"}),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
crate::proto::rpc_types::error::Error::InvalidMethodIndex(0, _)
|
||||
));
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GreetingService {
|
||||
pub delay_ms: u64,
|
||||
|
||||
Reference in New Issue
Block a user