api/router.rs: allow different types of api methods

Added a prototype for file/backup uploads.
This commit is contained in:
Dietmar Maurer 2019-01-14 12:26:04 +01:00
parent b41d1aab8c
commit 7e21da6e23
5 changed files with 133 additions and 25 deletions

View File

@ -21,21 +21,18 @@ impl ApiConfig {
} }
} }
pub fn find_method(&self, components: &[&str], method: Method, uri_param: &mut HashMap<String, String>) -> Option<&'static ApiMethod> { pub fn find_method(&self, components: &[&str], method: Method, uri_param: &mut HashMap<String, String>) -> &'static MethodDefinition {
if let Some(info) = self.router.find_route(components, uri_param) { if let Some(info) = self.router.find_route(components, uri_param) {
let opt_api_method = match method { return match method {
Method::GET => &info.get, Method::GET => &info.get,
Method::PUT => &info.put, Method::PUT => &info.put,
Method::POST => &info.post, Method::POST => &info.post,
Method::DELETE => &info.delete, Method::DELETE => &info.delete,
_ => &None, _ => &MethodDefinition::None,
}; };
if let Some(api_method) = opt_api_method {
return Some(&api_method);
} }
} &MethodDefinition::None
None
} }
pub fn find_alias(&self, components: &[&str]) -> PathBuf { pub fn find_alias(&self, components: &[&str]) -> PathBuf {

View File

@ -5,8 +5,15 @@ use serde_json::{Value};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use hyper::{Body, Response};
use hyper::rt::Future;
pub type BoxFut = Box<Future<Item = Response<Body>, Error = failure::Error> + Send>;
type ApiHandlerFn = fn(Value, &ApiMethod) -> Result<Value, Error>; type ApiHandlerFn = fn(Value, &ApiMethod) -> Result<Value, Error>;
type ApiUploadHandlerFn = fn(hyper::Body, Value, &ApiUploadMethod) -> BoxFut;
pub struct ApiMethod { pub struct ApiMethod {
pub parameters: ObjectSchema, pub parameters: ObjectSchema,
pub returns: Arc<Schema>, pub returns: Arc<Schema>,
@ -29,7 +36,30 @@ impl ApiMethod {
self self
} }
}
pub struct ApiUploadMethod {
pub parameters: ObjectSchema,
pub returns: Arc<Schema>,
pub handler: ApiUploadHandlerFn,
}
impl ApiUploadMethod {
pub fn new(handler: ApiUploadHandlerFn, parameters: ObjectSchema) -> Self {
Self {
parameters,
handler,
returns: Arc::new(Schema::Null),
}
}
pub fn returns<S: Into<Arc<Schema>>>(mut self, schema: S) -> Self {
self.returns = schema.into();
self
}
} }
pub enum SubRoute { pub enum SubRoute {
@ -38,11 +68,17 @@ pub enum SubRoute {
MatchAll { router: Box<Router>, param_name: String }, MatchAll { router: Box<Router>, param_name: String },
} }
pub enum MethodDefinition {
None,
Simple(ApiMethod),
Upload(ApiUploadMethod),
}
pub struct Router { pub struct Router {
pub get: Option<ApiMethod>, pub get: MethodDefinition,
pub put: Option<ApiMethod>, pub put: MethodDefinition,
pub post: Option<ApiMethod>, pub post: MethodDefinition,
pub delete: Option<ApiMethod>, pub delete: MethodDefinition,
pub subroute: SubRoute, pub subroute: SubRoute,
} }
@ -50,10 +86,10 @@ impl Router {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
get: None, get: MethodDefinition::None,
put: None, put: MethodDefinition::None,
post: None, post: MethodDefinition::None,
delete: None, delete: MethodDefinition::None,
subroute: SubRoute::None subroute: SubRoute::None
} }
} }
@ -86,22 +122,27 @@ impl Router {
} }
pub fn get(mut self, m: ApiMethod) -> Self { pub fn get(mut self, m: ApiMethod) -> Self {
self.get = Some(m); self.get = MethodDefinition::Simple(m);
self self
} }
pub fn put(mut self, m: ApiMethod) -> Self { pub fn put(mut self, m: ApiMethod) -> Self {
self.put = Some(m); self.put = MethodDefinition::Simple(m);
self self
} }
pub fn post(mut self, m: ApiMethod) -> Self { pub fn post(mut self, m: ApiMethod) -> Self {
self.post = Some(m); self.post = MethodDefinition::Simple(m);
self
}
pub fn upload(mut self, m: ApiUploadMethod) -> Self {
self.post = MethodDefinition::Upload(m);
self self
} }
pub fn delete(mut self, m: ApiMethod) -> Self { pub fn delete(mut self, m: ApiMethod) -> Self {
self.delete = Some(m); self.delete = MethodDefinition::Simple(m);
self self
} }

View File

@ -2,8 +2,12 @@ use failure::*;
use crate::api::schema::*; use crate::api::schema::*;
use crate::api::router::*; use crate::api::router::*;
use crate::server::rest::*;
use serde_json::{json, Value}; use serde_json::{json, Value};
use hyper::StatusCode;
use hyper::rt::{Future, Stream};
use crate::config::datastore; use crate::config::datastore;
use crate::backup::datastore::*; use crate::backup::datastore::*;
@ -48,6 +52,40 @@ pub fn api_method_garbage_collection_status() -> ApiMethod {
) )
} }
fn upload_catar(req_body: hyper::Body, param: Value, _info: &ApiUploadMethod) -> BoxFut {
let name = param["name"].as_str().unwrap();
println!("Upload .catar to {}", name);
let resp = req_body
.map_err(|err| http_err!(BAD_REQUEST, format!("Promlems reading request body: {}", err)))
.for_each(|chunk| {
println!("UPLOAD Chunk {}", chunk.len());
Ok(())
})
.and_then(|()| {
println!("UPLOAD DATA Sucessful");
let response = http::Response::builder()
.status(200)
.body(hyper::Body::empty())
.unwrap();
Ok(response)
});
Box::new(resp)
}
fn api_method_upload_catar() -> ApiUploadMethod {
ApiUploadMethod::new(
upload_catar,
ObjectSchema::new("Upload .catar backup file.")
.required("name", StringSchema::new("Datastore name."))
)
}
fn get_datastore_list(_param: Value, _info: &ApiMethod) -> Result<Value, Error> { fn get_datastore_list(_param: Value, _info: &ApiMethod) -> Result<Value, Error> {
let config = datastore::config()?; let config = datastore::config()?;
@ -67,6 +105,7 @@ pub fn router() -> Router {
ObjectSchema::new("Directory index.") ObjectSchema::new("Directory index.")
.required("name", StringSchema::new("Datastore name."))) .required("name", StringSchema::new("Datastore name.")))
) )
.upload(api_method_upload_catar())
.subdir( .subdir(
"gc", "gc",
Router::new() Router::new()

View File

@ -21,9 +21,11 @@ pub mod api {
pub mod config; pub mod config;
} }
#[macro_use]
pub mod server { pub mod server {
pub mod formatter; pub mod formatter;
#[macro_use]
pub mod rest; pub mod rest;
} }

View File

@ -82,8 +82,6 @@ impl Service for ApiService {
} }
} }
type BoxFut = Box<Future<Item = Response<Body>, Error = failure::Error> + Send>;
#[derive(Debug, Fail)] #[derive(Debug, Fail)]
pub struct HttpError { pub struct HttpError {
pub code: StatusCode, pub code: StatusCode,
@ -180,6 +178,31 @@ fn handle_sync_api_request(
Box::new(resp) Box::new(resp)
} }
fn handle_upload_api_request(
info: &'static ApiUploadMethod,
formatter: &'static OutputFormatter,
parts: Parts,
req_body: Body,
uri_param: HashMap<String, String>,
) -> BoxFut
{
// fixme: convert parameters to Json
let mut param_list: Vec<(String, String)> = vec![];
for (k, v) in uri_param {
param_list.push((k.clone(), v.clone()));
}
let params = match parse_parameter_strings(&param_list, &info.parameters, true) {
Ok(v) => v,
Err(err) => {
return Box::new(future::err(err.into()));
}
};
(info.handler)(req_body, params, info)
}
fn get_index() -> BoxFut { fn get_index() -> BoxFut {
let nodename = "unknown"; let nodename = "unknown";
@ -362,10 +385,16 @@ pub fn handle_request(api: Arc<ApiConfig>, req: Request<Body>) -> BoxFut {
let mut uri_param = HashMap::new(); let mut uri_param = HashMap::new();
if let Some(api_method) = api.find_method(&components[2..], method, &mut uri_param) {
// fixme: handle auth // fixme: handle auth
match api.find_method(&components[2..], method, &mut uri_param) {
MethodDefinition::None => {}
MethodDefinition::Simple(api_method) => {
return handle_sync_api_request(api_method, formatter, parts, body, uri_param); return handle_sync_api_request(api_method, formatter, parts, body, uri_param);
} }
MethodDefinition::Upload(upload_method) => {
return handle_upload_api_request(upload_method, formatter, parts, body, uri_param);
}
}
} }
} else { } else {
// not Auth for accessing files! // not Auth for accessing files!