]> git.proxmox.com Git - proxmox-backup.git/blob - src/api_schema/router.rs
e4c61bd7c88a6811c8ff4c435ea877baa1e1ffe8
[proxmox-backup.git] / src / api_schema / router.rs
1 use failure::*;
2
3 use crate::api_schema::*;
4 use serde_json::{Value};
5 use std::collections::HashMap;
6 use std::sync::Arc;
7 use std::fmt;
8
9 use hyper::{Body, Response, StatusCode};
10 use hyper::rt::Future;
11 use hyper::http::request::Parts;
12
13 pub type BoxFut = Box<Future<Item = Response<Body>, Error = failure::Error> + Send>;
14
15 /// Abstract Interface for API methods to interact with the environment
16 pub trait RpcEnvironment {
17
18 /// Use this to pass additional result data. It is up to the environment
19 /// how the data is used.
20 fn set_result_attrib(&mut self, name: &str, value: Value);
21
22 /// Query additional result data.
23 fn get_result_attrib(&self, name: &str) -> Option<&Value>;
24
25 /// The environment type
26 fn env_type(&self) -> RpcEnvironmentType;
27
28 /// Set user name
29 fn set_user(&mut self, user: Option<String>);
30
31 /// Get user name
32 fn get_user(&self) -> Option<String>;
33 }
34
35 #[derive(PartialEq, Copy, Clone)]
36 pub enum RpcEnvironmentType {
37 /// command started from command line
38 CLI,
39 /// access from public acessable server
40 PUBLIC,
41 /// ... access from priviledged server (run as root)
42 PRIVILEDGED,
43 }
44
45 #[derive(Debug, Fail)]
46 pub struct HttpError {
47 pub code: StatusCode,
48 pub message: String,
49 }
50
51 impl HttpError {
52 pub fn new(code: StatusCode, message: String) -> Self {
53 HttpError { code, message }
54 }
55 }
56
57 impl fmt::Display for HttpError {
58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 write!(f, "Error {}: {}", self.code, self.message)
60 }
61 }
62
63 macro_rules! http_err {
64 ($status:ident, $msg:expr) => {{
65 Error::from(HttpError::new(StatusCode::$status, $msg))
66 }}
67 }
68
69 type ApiHandlerFn = fn(Value, &ApiMethod, &mut dyn RpcEnvironment) -> Result<Value, Error>;
70
71 type ApiAsyncHandlerFn = fn(Parts, Body, Value, &ApiAsyncMethod, &mut dyn RpcEnvironment) -> Result<BoxFut, Error>;
72
73 /// This struct defines synchronous API call which returns the restulkt as json `Value`
74 pub struct ApiMethod {
75 /// The protected flag indicates that the provides function should be forwarded
76 /// to the deaemon running in priviledged mode.
77 pub protected: bool,
78 /// This flag indicates that the provided method may change the local timezone, so the server
79 /// should do a tzset afterwards
80 pub reload_timezone: bool,
81 /// Parameter type Schema
82 pub parameters: ObjectSchema,
83 /// Return type Schema
84 pub returns: Arc<Schema>,
85 /// Handler function
86 pub handler: ApiHandlerFn,
87 }
88
89 impl ApiMethod {
90
91 pub fn new(handler: ApiHandlerFn, parameters: ObjectSchema) -> Self {
92 Self {
93 parameters,
94 handler,
95 returns: Arc::new(Schema::Null),
96 protected: false,
97 reload_timezone: false,
98 }
99 }
100
101 pub fn returns<S: Into<Arc<Schema>>>(mut self, schema: S) -> Self {
102
103 self.returns = schema.into();
104
105 self
106 }
107
108 pub fn protected(mut self, protected: bool) -> Self {
109
110 self.protected = protected;
111
112 self
113 }
114
115 pub fn reload_timezone(mut self, reload_timezone: bool) -> Self {
116
117 self.reload_timezone = reload_timezone;
118
119 self
120 }
121 }
122
123 pub struct ApiAsyncMethod {
124 pub parameters: ObjectSchema,
125 pub returns: Arc<Schema>,
126 pub handler: ApiAsyncHandlerFn,
127 }
128
129 impl ApiAsyncMethod {
130
131 pub fn new(handler: ApiAsyncHandlerFn, parameters: ObjectSchema) -> Self {
132 Self {
133 parameters,
134 handler,
135 returns: Arc::new(Schema::Null),
136 }
137 }
138
139 pub fn returns<S: Into<Arc<Schema>>>(mut self, schema: S) -> Self {
140
141 self.returns = schema.into();
142
143 self
144 }
145 }
146
147 pub enum SubRoute {
148 None,
149 Hash(HashMap<String, Router>),
150 MatchAll { router: Box<Router>, param_name: String },
151 }
152
153 pub enum MethodDefinition {
154 None,
155 Simple(ApiMethod),
156 Async(ApiAsyncMethod),
157 }
158
159 pub struct Router {
160 pub get: MethodDefinition,
161 pub put: MethodDefinition,
162 pub post: MethodDefinition,
163 pub delete: MethodDefinition,
164 pub subroute: SubRoute,
165 }
166
167 impl Router {
168
169 pub fn new() -> Self {
170 Self {
171 get: MethodDefinition::None,
172 put: MethodDefinition::None,
173 post: MethodDefinition::None,
174 delete: MethodDefinition::None,
175 subroute: SubRoute::None
176 }
177 }
178
179 pub fn subdir<S: Into<String>>(mut self, subdir: S, router: Router) -> Self {
180 if let SubRoute::None = self.subroute {
181 self.subroute = SubRoute::Hash(HashMap::new());
182 }
183 match self.subroute {
184 SubRoute::Hash(ref mut map) => {
185 map.insert(subdir.into(), router);
186 }
187 _ => panic!("unexpected subroute type"),
188 }
189 self
190 }
191
192 pub fn subdirs(mut self, map: HashMap<String, Router>) -> Self {
193 self.subroute = SubRoute::Hash(map);
194 self
195 }
196
197 pub fn match_all<S: Into<String>>(mut self, param_name: S, router: Router) -> Self {
198 if let SubRoute::None = self.subroute {
199 self.subroute = SubRoute::MatchAll { router: Box::new(router), param_name: param_name.into() };
200 } else {
201 panic!("unexpected subroute type");
202 }
203 self
204 }
205
206 pub fn get(mut self, m: ApiMethod) -> Self {
207 self.get = MethodDefinition::Simple(m);
208 self
209 }
210
211 pub fn put(mut self, m: ApiMethod) -> Self {
212 self.put = MethodDefinition::Simple(m);
213 self
214 }
215
216 pub fn post(mut self, m: ApiMethod) -> Self {
217 self.post = MethodDefinition::Simple(m);
218 self
219 }
220
221 pub fn upload(mut self, m: ApiAsyncMethod) -> Self {
222 self.post = MethodDefinition::Async(m);
223 self
224 }
225
226 pub fn download(mut self, m: ApiAsyncMethod) -> Self {
227 self.get = MethodDefinition::Async(m);
228 self
229 }
230
231
232 pub fn delete(mut self, m: ApiMethod) -> Self {
233 self.delete = MethodDefinition::Simple(m);
234 self
235 }
236
237 pub fn find_route(&self, components: &[&str], uri_param: &mut HashMap<String, String>) -> Option<&Router> {
238
239 if components.len() == 0 { return Some(self); };
240
241 let (dir, rest) = (components[0], &components[1..]);
242
243 match self.subroute {
244 SubRoute::None => {},
245 SubRoute::Hash(ref dirmap) => {
246 if let Some(ref router) = dirmap.get(dir) {
247 println!("FOUND SUBDIR {}", dir);
248 return router.find_route(rest, uri_param);
249 }
250 }
251 SubRoute::MatchAll { ref router, ref param_name } => {
252 println!("URI PARAM {} = {}", param_name, dir); // fixme: store somewhere
253 uri_param.insert(param_name.clone(), dir.into());
254 return router.find_route(rest, uri_param);
255 },
256 }
257
258 None
259 }
260 }