#![feature(async_await)] use bytes::Bytes; use proxmox_api::Router; #[test] fn basic() { let info: &proxmox_api::ApiMethod = &methods::GET_PEOPLE; let get_subpath: &proxmox_api::ApiMethod = &methods::GET_SUBPATH; let router: Router = Router::new() .subdir( "people", Router::new().parameter_subdir("person", Router::new().get(info)), ) .subdir( "wildcard", Router::new().wildcard("subpath").get(get_subpath), ); check_with_matched_params(&router, "people/foo", "person", "foo", "foo"); check_with_matched_params(&router, "people//foo", "person", "foo", "foo"); check_with_matched_params(&router, "wildcard", "subpath", "", ""); check_with_matched_params(&router, "wildcard/", "subpath", "", ""); check_with_matched_params(&router, "wildcard//", "subpath", "", ""); check_with_matched_params(&router, "wildcard/dir1", "subpath", "dir1", "dir1"); check_with_matched_params( &router, "wildcard/dir1/dir2", "subpath", "dir1/dir2", "dir1/dir2", ); check_with_matched_params(&router, "wildcard/dir1//2", "subpath", "dir1//2", "dir1//2"); } fn check_with_matched_params( router: &Router, path: &str, param_name: &str, param_value: &str, expected_body: &str, ) { let (target, params) = router .lookup(path) .expect(&format!("must be able to lookup '{}'", path)); let params = params.expect(&format!( "expected parameters to be matched into '{}'", param_name, )); let arg = params[param_name].as_str().expect(&format!( "expected lookup() to fill the '{}' parameter", param_name )); assert_eq!( arg, param_value, "lookup of '{}' should set '{}' to '{}'", path, param_name, param_value, ); let apifut = target .get .as_ref() .expect(&format!("expected GET method on {}", path)) .call(params); let response = futures::executor::block_on(apifut) .expect("expected the simple test api function to be ready immediately"); assert_eq!(response.status(), 200, "response status must be 200"); let body = std::str::from_utf8(response.body().as_ref()).expect("expected a valid utf8 repsonse body"); assert_eq!( body, expected_body, "response of {} should be '{}', got '{}'", path, expected_body, body, ); } #[cfg(test)] mod methods { use bytes::Bytes; use failure::{bail, Error}; use http::Response; use lazy_static::lazy_static; use serde_derive::{Deserialize, Serialize}; use serde_json::Value; use proxmox_api::{ get_type_info, ApiFuture, ApiMethod, ApiOutput, ApiType, Parameter, TypeInfo, }; pub async fn get_people(value: Value) -> ApiOutput { Ok(Response::builder() .status(200) .header("content-type", "application/json") .body(value["person"].as_str().unwrap().into())?) } pub async fn get_subpath(value: Value) -> ApiOutput { Ok(Response::builder() .status(200) .header("content-type", "application/json") .body(value["subpath"].as_str().unwrap().into())?) } lazy_static! { static ref GET_PEOPLE_PARAMS: Vec = { vec![Parameter { name: "person", description: "the person to get", type_info: String::type_info, }] }; pub static ref GET_PEOPLE: ApiMethod = { ApiMethod { description: "get some people", parameters: &GET_PEOPLE_PARAMS, return_type: get_type_info::(), protected: false, reload_timezone: false, handler: |value: Value| -> ApiFuture { Box::pin(get_people(value)) }, } }; static ref GET_SUBPATH_PARAMS: Vec = { vec![Parameter { name: "subpath", description: "the matched relative subdir path", type_info: String::type_info, }] }; pub static ref GET_SUBPATH: ApiMethod = { ApiMethod { description: "get the 'subpath' parameter returned back", parameters: &GET_SUBPATH_PARAMS, return_type: get_type_info::(), protected: false, reload_timezone: false, handler: |value: Value| -> ApiFuture { Box::pin(get_subpath(value)) }, } }; } #[derive(Deserialize, Serialize)] pub struct CubicMeters(f64); // We don't bother with the CLI interface in this test: proxmox_api::no_cli_type! {CubicMeters} proxmox_api::unconstrained_api_type! {CubicMeters} #[derive(Deserialize, Serialize)] pub struct Thing { shape: String, size: CubicMeters, } impl ApiType for Thing { fn type_info() -> &'static TypeInfo { const INFO: TypeInfo = TypeInfo { name: "Thing", description: "A thing", complete_fn: None, parse_cli: None, }; &INFO } fn verify(&self) -> Result<(), Error> { if self.shape == "flat" { bail!("flat shapes not allowed..."); } Ok(()) } } }