From a64832aac2721e431e78a923810c71d9dd2d9806 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Wed, 12 Jun 2019 17:11:44 +0200 Subject: [PATCH] testing a Router::api_dump method Signed-off-by: Wolfgang Bumiller --- api-test/src/main.rs | 6 ++++ proxmox-api/src/api_type.rs | 39 +++++++++++++++++++++++++- proxmox-api/src/router.rs | 55 ++++++++++++++++++++++++++++++++++++- 3 files changed, 98 insertions(+), 2 deletions(-) diff --git a/api-test/src/main.rs b/api-test/src/main.rs index 989cddd4..8a916163 100644 --- a/api-test/src/main.rs +++ b/api-test/src/main.rs @@ -46,6 +46,12 @@ fn main() { let www_dir = args.next().expect("expected a www/ subdirectory"); api::set_www_dir(www_dir.to_string()); + // show our api info: + println!( + "{}", + serde_json::to_string_pretty(&api::ROUTER.api_dump()).unwrap() + ); + // Construct our SocketAddr to listen on... let addr = ([0, 0, 0, 0], 3000).into(); diff --git a/proxmox-api/src/api_type.rs b/proxmox-api/src/api_type.rs index b6da00c9..d41169d9 100644 --- a/proxmox-api/src/api_type.rs +++ b/proxmox-api/src/api_type.rs @@ -5,7 +5,7 @@ use std::sync::Once; use failure::Error; use http::Response; -use serde_json::Value; +use serde_json::{json, Value}; /// Method entries in a `Router` are actually just `&dyn ApiMethodInfo` trait objects. /// This contains all the info required to call, document, or command-line-complete parameters for @@ -31,6 +31,18 @@ pub struct Parameter { pub type_info: fn() -> &'static TypeInfo, } +impl Parameter { + pub fn api_dump(&self) -> (&'static str, Value) { + ( + self.name, + json!({ + "description": self.description, + "type": (self.type_info)().name, + }), + ) + } +} + /// Bare type info. Types themselves should also have a description, even if a method's parameter /// usually overrides it. Ideally we can hyperlink the parameter to the type information in the /// generated documentation. @@ -40,6 +52,12 @@ pub struct TypeInfo { pub complete_fn: Option, } +impl TypeInfo { + pub fn api_dump(&self) -> Value { + Value::String(self.name.to_string()) + } +} + /// Until we can slap `#[api]` onto all the functions we can start translating our existing /// `ApiMethod` structs to this new layout. /// Otherwise this is mostly there so we can run the tests in the tests subdirectory without @@ -80,6 +98,25 @@ impl ApiMethodInfo for ApiMethod { } } +impl dyn ApiMethodInfo + Send + Sync { + pub fn api_dump(&self) -> Value { + let parameters = Value::Object(std::iter::FromIterator::from_iter( + self.parameters() + .iter() + .map(|p| p.api_dump()) + .map(|(name, value)| (name.to_string(), value)), + )); + + json!({ + "description": self.description(), + "protected": self.protected(), + "reload-timezone": self.reload_timezone(), + "parameters": parameters, + //"returns": self.return_type().api_dump()?, <- add api_dump() to TypeInfo + }) + } +} + /// We're supposed to only use types in the API which implement `ApiType`, which forces types ot /// have a `verify` method. The idea is that all parameters used in the API are documented /// somewhere with their formats and limits, which are checked when entering and leaving API entry diff --git a/proxmox-api/src/router.rs b/proxmox-api/src/router.rs index 77dec989..39fec8f0 100644 --- a/proxmox-api/src/router.rs +++ b/proxmox-api/src/router.rs @@ -2,7 +2,8 @@ use std::collections::HashMap; -use serde_json::Value; +use failure::Error; +use serde_json::{json, Value}; use super::ApiMethodInfo; @@ -110,6 +111,58 @@ where Some((this, matched_params.map(Value::Object))) } + pub fn api_dump(&self) -> Value { + let mut this = serde_json::Map::::new(); + + if let Some(get) = self.get { + this.insert("GET".to_string(), get.api_dump()); + } + + if let Some(put) = self.put { + this.insert("PUT".to_string(), put.api_dump()); + } + + if let Some(post) = self.post { + this.insert("POST".to_string(), post.api_dump()); + } + + if let Some(delete) = self.delete { + this.insert("DELETE".to_string(), delete.api_dump()); + } + + match &self.subroute { + None => (), + Some(SubRoute::Wildcard(name)) => { + this.insert("wildcard".to_string(), Value::String(name.to_string())); + } + Some(SubRoute::Directories(subdirs)) => { + for (dir, router) in subdirs.iter() { + this.insert(dir.to_string(), router.api_dump()); + } + } + Some(SubRoute::Parameter(name, other)) => { + this.insert( + "sub-router".to_string(), + json!({ + "parameter": name, + "router": other.api_dump(), + }), + ); + } + } + + Value::Object(this) + } +} + +// +// Router as a builder methods: +// + +impl Router +where + Self: Default, +{ /// Builder method to provide a `GET` method info. pub fn get(mut self, method: &'static I) -> Self where