api_schema: allow generic api handler functions
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
286f0d4099
commit
062d4916ff
|
@ -35,7 +35,7 @@ pub fn router() -> Router {
|
||||||
|
|
||||||
let route = Router::new()
|
let route = Router::new()
|
||||||
.get(ApiMethod::new(
|
.get(ApiMethod::new(
|
||||||
|_,_,_| Ok(json!([
|
|| Ok(json!([
|
||||||
{"subdir": "access"},
|
{"subdir": "access"},
|
||||||
{"subdir": "admin"},
|
{"subdir": "admin"},
|
||||||
{"subdir": "config"},
|
{"subdir": "config"},
|
||||||
|
|
|
@ -70,7 +70,7 @@ pub fn router() -> Router {
|
||||||
|
|
||||||
let route = Router::new()
|
let route = Router::new()
|
||||||
.get(ApiMethod::new(
|
.get(ApiMethod::new(
|
||||||
|_,_,_| Ok(json!([
|
|| Ok(json!([
|
||||||
{"subdir": "ticket"}
|
{"subdir": "ticket"}
|
||||||
])),
|
])),
|
||||||
ObjectSchema::new("Directory index.")))
|
ObjectSchema::new("Directory index.")))
|
||||||
|
|
|
@ -8,7 +8,7 @@ pub fn router() -> Router {
|
||||||
|
|
||||||
let route = Router::new()
|
let route = Router::new()
|
||||||
.get(ApiMethod::new(
|
.get(ApiMethod::new(
|
||||||
|_,_,_| Ok(json!([
|
|| Ok(json!([
|
||||||
{"subdir": "datastore"}
|
{"subdir": "datastore"}
|
||||||
])),
|
])),
|
||||||
ObjectSchema::new("Directory index.")))
|
ObjectSchema::new("Directory index.")))
|
||||||
|
|
|
@ -388,7 +388,7 @@ pub fn router() -> Router {
|
||||||
|
|
||||||
let datastore_info = Router::new()
|
let datastore_info = Router::new()
|
||||||
.get(ApiMethod::new(
|
.get(ApiMethod::new(
|
||||||
|_,_,_| Ok(json!([
|
|| Ok(json!([
|
||||||
{"subdir": "backups" },
|
{"subdir": "backups" },
|
||||||
{"subdir": "pxar" },
|
{"subdir": "pxar" },
|
||||||
{"subdir": "gc" },
|
{"subdir": "gc" },
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub fn router() -> Router {
|
||||||
|
|
||||||
let route = Router::new()
|
let route = Router::new()
|
||||||
.get(ApiMethod::new(
|
.get(ApiMethod::new(
|
||||||
|_,_,_| Ok(json!([
|
|| Ok(json!([
|
||||||
{"subdir": "datastore"},
|
{"subdir": "datastore"},
|
||||||
])),
|
])),
|
||||||
ObjectSchema::new("Directory index.")))
|
ObjectSchema::new("Directory index.")))
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub fn router() -> Router {
|
||||||
|
|
||||||
let route = Router::new()
|
let route = Router::new()
|
||||||
.get(ApiMethod::new(
|
.get(ApiMethod::new(
|
||||||
|_,_,_| Ok(json!([
|
|| Ok(json!([
|
||||||
{"subdir": "dns"},
|
{"subdir": "dns"},
|
||||||
{"subdir": "network"},
|
{"subdir": "network"},
|
||||||
{"subdir": "services"},
|
{"subdir": "services"},
|
||||||
|
|
|
@ -224,7 +224,7 @@ pub fn router() -> Router {
|
||||||
|
|
||||||
let service_api = Router::new()
|
let service_api = Router::new()
|
||||||
.get(ApiMethod::new(
|
.get(ApiMethod::new(
|
||||||
|_,_,_| {
|
|| {
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
for cmd in &["state", "start", "stop", "restart", "reload"] {
|
for cmd in &["state", "start", "stop", "restart", "reload"] {
|
||||||
result.push(json!({"subdir": cmd }));
|
result.push(json!({"subdir": cmd }));
|
||||||
|
|
|
@ -175,7 +175,7 @@ pub fn router() -> Router {
|
||||||
|
|
||||||
let upid_api = Router::new()
|
let upid_api = Router::new()
|
||||||
.get(ApiMethod::new(
|
.get(ApiMethod::new(
|
||||||
|_,_,_| {
|
|| {
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
for cmd in &["log", "status"] {
|
for cmd in &["log", "status"] {
|
||||||
result.push(json!({"subdir": cmd }));
|
result.push(json!({"subdir": cmd }));
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
mod schema;
|
mod schema;
|
||||||
pub use schema::*;
|
pub use schema::*;
|
||||||
|
|
||||||
|
pub mod api_handler;
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod router;
|
pub mod router;
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
use failure::Error;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use super::router::{ApiMethod, RpcEnvironment};
|
||||||
|
|
||||||
|
pub type ApiHandlerFn = Box<
|
||||||
|
dyn Fn(Value, &ApiMethod, &mut dyn RpcEnvironment) -> Result<Value, Error>
|
||||||
|
+ Send + Sync + 'static
|
||||||
|
>;
|
||||||
|
|
||||||
|
pub trait WrapApiHandler<Args, R, MetaArgs> {
|
||||||
|
fn wrap(self) -> ApiHandlerFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn()
|
||||||
|
impl<F, R> WrapApiHandler<(), R, ()> for F
|
||||||
|
where
|
||||||
|
F: Fn() -> Result<R, Error> + Send + Sync + 'static,
|
||||||
|
R: serde::Serialize,
|
||||||
|
{
|
||||||
|
fn wrap(self) -> ApiHandlerFn {
|
||||||
|
Box::new(move |_value, _method, _rpc_env| {
|
||||||
|
Ok(serde_json::to_value((self)()?)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn(Arg)
|
||||||
|
impl<F, A, R> WrapApiHandler<(A,), R, ()> for F
|
||||||
|
where
|
||||||
|
F: Fn(A) -> Result<R, Error> + Send + Sync + 'static,
|
||||||
|
A: serde::de::DeserializeOwned,
|
||||||
|
R: serde::Serialize,
|
||||||
|
{
|
||||||
|
fn wrap(self) -> ApiHandlerFn {
|
||||||
|
Box::new(move |value, _method, _rpc_env| {
|
||||||
|
Ok(serde_json::to_value((self)(serde_json::from_value(value)?)?)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn(&ApiMethod)
|
||||||
|
impl<F, R> WrapApiHandler<(), R, (ApiMethod,)> for F
|
||||||
|
where
|
||||||
|
F: Fn(&ApiMethod) -> Result<R, Error> + Send + Sync + 'static,
|
||||||
|
R: serde::Serialize,
|
||||||
|
{
|
||||||
|
fn wrap(self) -> ApiHandlerFn {
|
||||||
|
Box::new(move |_value, method, _rpc_env| {
|
||||||
|
Ok(serde_json::to_value((self)(method)?)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn(Arg, &ApiMethod)
|
||||||
|
impl<F, A, R> WrapApiHandler<(A,), R, (ApiMethod,)> for F
|
||||||
|
where
|
||||||
|
F: Fn(A, &ApiMethod) -> Result<R, Error> + Send + Sync + 'static,
|
||||||
|
A: serde::de::DeserializeOwned,
|
||||||
|
R: serde::Serialize,
|
||||||
|
{
|
||||||
|
fn wrap(self) -> ApiHandlerFn {
|
||||||
|
Box::new(move |value, method, _rpc_env| {
|
||||||
|
Ok(serde_json::to_value((self)(
|
||||||
|
serde_json::from_value(value)?,
|
||||||
|
method,
|
||||||
|
)?)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RpcEnvironment is a trait, so use a "marker" type for it instead:
|
||||||
|
pub struct RpcEnvArg();
|
||||||
|
|
||||||
|
// fn(&mut dyn RpcEnvironment)
|
||||||
|
impl<F, R> WrapApiHandler<(), R, (RpcEnvArg,)> for F
|
||||||
|
where
|
||||||
|
F: Fn(&mut dyn RpcEnvironment) -> Result<R, Error> + Send + Sync + 'static,
|
||||||
|
R: serde::Serialize,
|
||||||
|
{
|
||||||
|
fn wrap(self) -> ApiHandlerFn {
|
||||||
|
Box::new(move |_value, _method, rpc_env| {
|
||||||
|
Ok(serde_json::to_value((self)(rpc_env)?)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn(Arg, &mut dyn RpcEnvironment)
|
||||||
|
impl<F, A, R> WrapApiHandler<(A,), R, (RpcEnvArg,)> for F
|
||||||
|
where
|
||||||
|
F: Fn(A, &mut dyn RpcEnvironment) -> Result<R, Error> + Send + Sync + 'static,
|
||||||
|
A: serde::de::DeserializeOwned,
|
||||||
|
R: serde::Serialize,
|
||||||
|
{
|
||||||
|
fn wrap(self) -> ApiHandlerFn {
|
||||||
|
Box::new(move |value, _method, rpc_env| {
|
||||||
|
Ok(serde_json::to_value((self)(
|
||||||
|
serde_json::from_value(value)?,
|
||||||
|
rpc_env,
|
||||||
|
)?)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn(&ApiMethod, &mut dyn RpcEnvironment)
|
||||||
|
impl<F, R> WrapApiHandler<(), R, (ApiMethod, RpcEnvArg,)> for F
|
||||||
|
where
|
||||||
|
F: Fn(&ApiMethod, &mut dyn RpcEnvironment) -> Result<R, Error> + Send + Sync + 'static,
|
||||||
|
R: serde::Serialize,
|
||||||
|
{
|
||||||
|
fn wrap(self) -> ApiHandlerFn {
|
||||||
|
Box::new(move |_value, method, rpc_env| {
|
||||||
|
Ok(serde_json::to_value((self)(method, rpc_env)?)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn(Arg, &ApiMethod, &mut dyn RpcEnvironment)
|
||||||
|
impl<F, A, R> WrapApiHandler<(A,), R, (ApiMethod, RpcEnvArg,)> for F
|
||||||
|
where
|
||||||
|
F: Fn(A, &ApiMethod, &mut dyn RpcEnvironment) -> Result<R, Error> + Send + Sync + 'static,
|
||||||
|
A: serde::de::DeserializeOwned,
|
||||||
|
R: serde::Serialize,
|
||||||
|
{
|
||||||
|
fn wrap(self) -> ApiHandlerFn {
|
||||||
|
Box::new(move |value, method, rpc_env| {
|
||||||
|
Ok(serde_json::to_value((self)(
|
||||||
|
serde_json::from_value(value)?,
|
||||||
|
method,
|
||||||
|
rpc_env,
|
||||||
|
)?)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ use hyper::{Body, Response, StatusCode};
|
||||||
use hyper::rt::Future;
|
use hyper::rt::Future;
|
||||||
use hyper::http::request::Parts;
|
use hyper::http::request::Parts;
|
||||||
|
|
||||||
|
use super::api_handler::*;
|
||||||
|
|
||||||
pub type BoxFut = Box<Future<Item = Response<Body>, Error = failure::Error> + Send>;
|
pub type BoxFut = Box<Future<Item = Response<Body>, Error = failure::Error> + Send>;
|
||||||
|
|
||||||
/// Abstract Interface for API methods to interact with the environment
|
/// Abstract Interface for API methods to interact with the environment
|
||||||
|
@ -72,9 +74,10 @@ macro_rules! http_err {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApiHandlerFn = fn(Value, &ApiMethod, &mut dyn RpcEnvironment) -> Result<Value, Error>;
|
type ApiAsyncHandlerFn = Box<
|
||||||
|
dyn Fn(Parts, Body, Value, &ApiAsyncMethod, &mut dyn RpcEnvironment) -> Result<BoxFut, Error>
|
||||||
type ApiAsyncHandlerFn = fn(Parts, Body, Value, &ApiAsyncMethod, &mut dyn RpcEnvironment) -> Result<BoxFut, Error>;
|
+ Send + Sync + 'static
|
||||||
|
>;
|
||||||
|
|
||||||
/// This struct defines synchronous API call which returns the restulkt as json `Value`
|
/// This struct defines synchronous API call which returns the restulkt as json `Value`
|
||||||
pub struct ApiMethod {
|
pub struct ApiMethod {
|
||||||
|
@ -89,15 +92,28 @@ pub struct ApiMethod {
|
||||||
/// Return type Schema
|
/// Return type Schema
|
||||||
pub returns: Arc<Schema>,
|
pub returns: Arc<Schema>,
|
||||||
/// Handler function
|
/// Handler function
|
||||||
pub handler: ApiHandlerFn,
|
pub handler: Option<ApiHandlerFn>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApiMethod {
|
impl ApiMethod {
|
||||||
|
|
||||||
pub fn new(handler: ApiHandlerFn, parameters: ObjectSchema) -> Self {
|
pub fn new<F, Args, R, MetaArgs>(func: F, parameters: ObjectSchema) -> Self
|
||||||
|
where
|
||||||
|
F: WrapApiHandler<Args, R, MetaArgs>,
|
||||||
|
{
|
||||||
Self {
|
Self {
|
||||||
parameters,
|
parameters,
|
||||||
handler,
|
handler: Some(func.wrap()),
|
||||||
|
returns: Arc::new(Schema::Null),
|
||||||
|
protected: false,
|
||||||
|
reload_timezone: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_dummy(parameters: ObjectSchema) -> Self {
|
||||||
|
Self {
|
||||||
|
parameters,
|
||||||
|
handler: None,
|
||||||
returns: Arc::new(Schema::Null),
|
returns: Arc::new(Schema::Null),
|
||||||
protected: false,
|
protected: false,
|
||||||
reload_timezone: false,
|
reload_timezone: false,
|
||||||
|
@ -134,10 +150,14 @@ pub struct ApiAsyncMethod {
|
||||||
|
|
||||||
impl ApiAsyncMethod {
|
impl ApiAsyncMethod {
|
||||||
|
|
||||||
pub fn new(handler: ApiAsyncHandlerFn, parameters: ObjectSchema) -> Self {
|
pub fn new<F>(handler: F, parameters: ObjectSchema) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(Parts, Body, Value, &ApiAsyncMethod, &mut dyn RpcEnvironment) -> Result<BoxFut, Error>
|
||||||
|
+ Send + Sync + 'static,
|
||||||
|
{
|
||||||
Self {
|
Self {
|
||||||
parameters,
|
parameters,
|
||||||
handler,
|
handler: Box::new(handler),
|
||||||
returns: Arc::new(Schema::Null),
|
returns: Arc::new(Schema::Null),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use failure::*;
|
use failure::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
use crate::api_schema::*;
|
use crate::api_schema::*;
|
||||||
use crate::api_schema::router::*;
|
use crate::api_schema::router::*;
|
||||||
|
@ -268,7 +267,7 @@ fn handle_simple_command(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (cli_cmd.info.handler as *const fn()) == (dummy_help as *const fn()) {
|
if cli_cmd.info.handler.is_none() {
|
||||||
let prefix = prefix.split(' ').next().unwrap().to_string();
|
let prefix = prefix.split(' ').next().unwrap().to_string();
|
||||||
print_help(top_def, prefix, &rest, params["verbose"].as_bool());
|
print_help(top_def, prefix, &rest, params["verbose"].as_bool());
|
||||||
return;
|
return;
|
||||||
|
@ -282,7 +281,7 @@ fn handle_simple_command(
|
||||||
|
|
||||||
let mut rpcenv = CliEnvironment::new();
|
let mut rpcenv = CliEnvironment::new();
|
||||||
|
|
||||||
match (cli_cmd.info.handler)(params, &cli_cmd.info, &mut rpcenv) {
|
match (cli_cmd.info.handler.as_ref().unwrap())(params, &cli_cmd.info, &mut rpcenv) {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
println!("Result: {}", serde_json::to_string_pretty(&value).unwrap());
|
println!("Result: {}", serde_json::to_string_pretty(&value).unwrap());
|
||||||
}
|
}
|
||||||
|
@ -603,8 +602,7 @@ pub fn print_bash_completion(def: &CommandLineInterface) {
|
||||||
|
|
||||||
fn help_command_def() -> CliCommand {
|
fn help_command_def() -> CliCommand {
|
||||||
CliCommand::new(
|
CliCommand::new(
|
||||||
ApiMethod::new(
|
ApiMethod::new_dummy(
|
||||||
dummy_help,
|
|
||||||
ObjectSchema::new("Get help about specified command.")
|
ObjectSchema::new("Get help about specified command.")
|
||||||
.optional("verbose", BooleanSchema::new("Verbose help."))
|
.optional("verbose", BooleanSchema::new("Verbose help."))
|
||||||
)
|
)
|
||||||
|
@ -693,10 +691,6 @@ pub struct CliCommandMap {
|
||||||
pub commands: HashMap<String, CommandLineInterface>,
|
pub commands: HashMap<String, CommandLineInterface>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dummy_help(_param: Value, _info: &ApiMethod, _rpcenv: &mut RpcEnvironment) -> Result<Value, Error> {
|
|
||||||
panic!("internal error"); // this is just a place holder - never call this
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CliCommandMap {
|
impl CliCommandMap {
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
|
|
@ -239,7 +239,7 @@ fn handle_sync_api_request(
|
||||||
let resp = params
|
let resp = params
|
||||||
.and_then(move |params| {
|
.and_then(move |params| {
|
||||||
let mut delay = false;
|
let mut delay = false;
|
||||||
let resp = match (info.handler)(params, info, &mut rpcenv) {
|
let resp = match (info.handler.as_ref().unwrap())(params, info, &mut rpcenv) {
|
||||||
Ok(data) => (formatter.format_result)(data, &rpcenv),
|
Ok(data) => (formatter.format_result)(data, &rpcenv),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some(httperr) = err.downcast_ref::<HttpError>() {
|
if let Some(httperr) = err.downcast_ref::<HttpError>() {
|
||||||
|
|
Loading…
Reference in New Issue