rust-server.server.mustache Maven / Gradle / Ivy
#![allow(unused_extern_crates)]
extern crate serde_ignored;
extern crate iron;
extern crate router;
extern crate bodyparser;
extern crate urlencoded;
extern crate uuid;
extern crate chrono;
{{#apiHasFile}}extern crate multipart;{{/apiHasFile}}
use futures::Future;
use futures::future;
use futures::{stream, Stream};
use hyper;
use hyper::header::{Headers, ContentType};
use self::iron::prelude::*;
use self::iron::{status, modifiers, BeforeMiddleware};
use self::iron::url::percent_encoding::percent_decode;
use self::router::Router;
use self::urlencoded::UrlEncodedQuery;
use mimetypes;
{{#apiHasFile}}use multipart::server::{Multipart, SaveResult};{{/apiHasFile}}
use serde_json;
{{#usesXml}}use serde_xml_rs;{{/usesXml}}
#[allow(unused_imports)]
use std::collections::{HashMap, BTreeMap};
#[allow(unused_imports)]
use swagger;
use std::io::Error;
#[allow(unused_imports)]
use std::collections::BTreeSet;
pub use swagger::auth::Authorization;
use swagger::auth::{AuthData, Scopes};
use swagger::{ApiError, Context, XSpanId};
use {Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
{{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
};
#[allow(unused_imports)]
use models;
header! { (Warning, "Warning") => [String] }
/// Create a new router for `Api`
pub fn router(api: T) -> Router where T: Api + Send + Sync + Clone + 'static {
let mut router = Router::new();
add_routes(&mut router, api);
router
}
/// Add routes for `Api` to a provided router.
///
/// Note that these routes are added straight onto the router. This means that if the router
/// already has a route for an endpoint which clashes with those provided by this API, then the
/// old route will be lost.
///
/// It is generally a bad idea to add routes in this way to an existing router, which may have
/// routes on it for other APIs. Distinct APIs should be behind distinct paths to encourage
/// separation of interfaces, which this function does not enforce. APIs should not overlap.
///
/// Alternative approaches include:
///
/// - generate an `iron::middleware::Handler` (usually a `router::Router` or
/// `iron::middleware::chain`) for each interface, and add those handlers inside an existing
/// router, mounted at different paths - so the interfaces are separated by path
/// - use a different instance of `iron::Iron` for each interface - so the interfaces are
/// separated by the address/port they listen on
///
/// This function exists to allow legacy code, which doesn't separate its APIs properly, to make
/// use of this crate.
#[deprecated(note="APIs should not overlap - only for use in legacy code.")]
pub fn route(router: &mut Router, api: T) where T: Api + Send + Sync + Clone + 'static {
add_routes(router, api)
}
/// Add routes for `Api` to a provided router
fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone + 'static {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
let api_clone = api.clone();
router.{{#vendorExtensions}}{{httpmethod}}{{/vendorExtensions}}(
"{{#vendorExtensions}}{{basePathWithoutHost}}{{path}}{{/vendorExtensions}}",
move |req: &mut Request| {
let mut context = Context::default();
// Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists).
fn handle_request(req: &mut Request, api: &T, context: &mut Context) -> Result where T: Api {
context.x_span_id = Some(req.headers.get::().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()));
context.auth_data = req.extensions.remove::();
context.authorization = req.extensions.remove::();
{{#hasAuthMethods}}
let authorization = context.authorization.as_ref().ok_or_else(|| {
Response::with((
status::Forbidden,
"Unauthenticated".to_string()
))
})?;
{{#authMethods}}
{{#isOAuth}}
// Authorization
if let Scopes::Some(ref scopes) = authorization.scopes {
let required_scopes: BTreeSet = vec![
{{#scopes}}
"{{scope}}".to_string(), // {{description}}
{{/scopes}}
].into_iter().collect();
if !required_scopes.is_subset(scopes) {
let missing_scopes = required_scopes.difference(scopes);
return Err(Response::with((
status::Forbidden,
missing_scopes.fold(
"Insufficient authorization, missing scopes".to_string(),
|s, scope| format!("{} {}", s, scope)
)
)));
}
}
{{/isOAuth}}
{{/authMethods}}
{{/hasAuthMethods}}
{{#pathParams}}{{#-first}}
// Path parameters
{{/-first}} let param_{{paramName}} = {
let param = req.extensions.get::().ok_or_else(|| Response::with((status::InternalServerError, "An internal error occurred".to_string())))?
.find("{{baseName}}").ok_or_else(|| Response::with((status::BadRequest, "Missing path parameter {{baseName}}".to_string())))?;
percent_decode(param.as_bytes()).decode_utf8()
.map_err(|_| Response::with((status::BadRequest, format!("Couldn't percent-decode path parameter as UTF-8: {}", param))))?
.parse().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse path parameter {{baseName}}: {}", e))))?
};
{{/pathParams}}
{{#headerParams}}{{#-first}}
// Header parameters
{{/-first}} header! { (Request{{vendorExtensions.typeName}}, "{{baseName}}") => {{#isListContainer}}({{{baseType}}})*{{/isListContainer}}{{^isListContainer}}[{{{dataType}}}]{{/isListContainer}} }
{{#required}} let param_{{paramName}} = req.headers.get::().ok_or_else(|| Response::with((status::BadRequest, "Missing or invalid required header {{baseName}}".to_string())))?.0.clone();
{{/required}}{{^required}} let param_{{paramName}} = req.headers.get::().map(|header| header.0.clone());
{{/required}}{{/headerParams}}
{{#queryParams}}{{#-first}}
// Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response)
let query_params = req.get::().unwrap_or_default();
{{/-first}} let param_{{paramName}} = query_params.get("{{baseName}}")
{{#required}} .ok_or_else(|| Response::with((status::BadRequest, "Missing required query parameter {{baseName}}".to_string())))?
{{#isListContainer}} .iter().flat_map(|x| x.parse::<{{{baseType}}}>()).collect::>();
{{/isListContainer}}{{^isListContainer}} .first().ok_or_else(|| Response::with((status::BadRequest, "Required query parameter {{baseName}} was empty".to_string())))?
.parse::<{{{dataType}}}>().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse query parameter {{baseName}} - doesn't match schema: {}", e))))?;
{{/isListContainer}}{{/required}}{{^required}}{{#isListContainer}} .map(|list| list.iter().flat_map(|x| x.parse::<{{{baseType}}}>()).collect::>());
{{/isListContainer}}{{^isListContainer}} .and_then(|list| list.first()).and_then(|x| x.parse::<{{{dataType}}}>().ok());
{{/isListContainer}}{{/required}}{{/queryParams}}
{{#bodyParams}}{{#-first}} // Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields.
{{/-first}}{{#required}}
let param_{{paramName}} = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter {{baseName}} - not valid UTF-8: {}", e))))?;
{{/required}}{{^required}}
let param_{{paramName}} = req.get::().unwrap_or(None);
{{/required}}{{#vendorExtensions}}{{^consumesPlainText}}
let mut unused_elements = Vec::new();
let param_{{paramName}} = if let Some(param_{{paramName}}_raw) = param_{{paramName}} { {{#consumesXml}}
let deserializer = &mut serde_xml_rs::de::Deserializer::new_from_reader(param_{{paramName}}_raw.as_bytes());{{/consumesXml}}{{#consumesJson}}
let deserializer = &mut serde_json::Deserializer::from_str(¶m_{{paramName}}_raw);{{/consumesJson}}
let param_{{paramName}}: Option<{{{dataType}}}> = serde_ignored::deserialize(deserializer, |path| {
warn!("Ignoring unknown field in body: {}", path);
unused_elements.push(path.to_string());
}){{#required}}.map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter {{baseName}} - doesn't match schema: {}", e))))?{{/required}}{{^required}}.unwrap_or(None){{/required}};
param_{{paramName}}
} else {
None
};{{/consumesPlainText}}{{/vendorExtensions}}{{#required}}
let param_{{paramName}} = param_{{paramName}}.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter {{baseName}}".to_string())))?{{/required}};
{{/bodyParams}}
{{#formParams}}
{{#-first}}
// Form parameters
{{/-first}}{{/formParams}}{{#vendorExtensions}}{{#hasFile}}
// Expecting a multipart form, extract and parse it now.
let mut entries = match Multipart::from_request(req) {
Ok(mut multipart) => {
match multipart.save().temp() {
SaveResult::Full(entries) => {
Ok(entries)
},
_ => {
Err(Response::with((status::InternalServerError, format!("Unable to process all message parts"))))
},
}
},
Err(e) => {
// Unable to parse as multipart
Err(Response::with((status::BadRequest, format!("Couldn't parse body as multipart"))))
}
}?;
{{/hasFile}}{{/vendorExtensions}}{{#allParams}}{{#isFormParam}}{{#isFile}}
let param_{{paramName}} = entries.fields.remove("{{paramName}}");
let param_{{paramName}} = match param_{{paramName}} {
Some(body) => {
Ok({let bytes = body.as_bytes();
{{^required}}Some({{/required}}
Box::new(stream::once(Ok(bytes.to_vec()))) as Box, Error=Error> + Send>
{{^required}}){{/required}}}
)
}
None => {Err(Response::with((status::BadRequest, format!("Body part not found!"))))}
}?;
{{/isFile}}
let param_{{paramName}} = {{#isFile}}{{^required}}Box::new(future::ok({{/required}}param_{{paramName}}{{^required}})){{/required}};{{/isFile}}{{^isFile}}{{^isContainer}}{{#vendorExtensions}}{{{example}}};{{/vendorExtensions}}{{/isContainer}}{{#isListContainer}}{{#required}}Vec::new();{{/required}}{{^required}}None;{{/required}}{{/isListContainer}}{{#isMapContainer}}None;{{/isMapContainer}}{{/isFile}}
{{/isFormParam}}
{{/allParams}}
match api.{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}param_{{paramName}}{{#isListContainer}}.as_ref(){{/isListContainer}}, {{/allParams}}context).wait() {
Ok(rsp) => match rsp {
{{#responses}}
{{operationId}}Response::{{#vendorExtensions}}{{x-responseId}}{{/vendorExtensions}}{{#dataType}}{{^headers}}(body){{/headers}}{{#headers}}{{#-first}}{ body{{/-first}}{{/headers}}{{/dataType}}{{#headers}}{{#-first}}{{^dataType}}{ {{/dataType}}{{#dataType}}, {{/dataType}}{{/-first}}{{^-first}}, {{/-first}}{{name}}{{#-last}} }{{/-last}}{{/headers}} => {
{{^isFile}}
{{#dataType}}{{#vendorExtensions}}{{#producesPlainText}} let body_string = body;
{{/producesPlainText}}{{#producesXml}}
{{^has_namespace}} let body_string = serde_xml_rs::to_string(&body).expect("impossible to fail to serialize");{{/has_namespace}}{{#has_namespace}}
let mut namespaces = BTreeMap::new();
// An empty string is used to indicate a global namespace in xmltree.
namespaces.insert("".to_string(), models::namespaces::{{uppercase_data_type}}.clone());
let body_string = serde_xml_rs::to_string_with_namespaces(&body, namespaces).expect("impossible to fail to serialize");{{/has_namespace}}{{/producesXml}}{{#producesJson}}
let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize");{{/producesJson}}{{/vendorExtensions}}{{/dataType}}
let mut response = Response::with((status::Status::from_u16({{code}}){{#dataType}}, body_string{{/dataType}}));{{/isFile}}{{#isFile}} body.fold(Vec::new(), |mut body, chunk| {
body.extend(chunk.iter());
future::ok::, Error>(body)
})
// Block whilst waiting for the stream to complete
.wait()
// It's possible that errors were received in the stream, if this is the case then we can't return a success response to the client and instead must return an internal error.
.map_err(|_| Response::with((status::InternalServerError, "An internal error occurred".to_string())))
// Assuming no errors then create an Iron response.
.map(|rsp| {
let mut response = Response::new();
response.status = Some(status::Status::from_u16({{code}}));
response.body = Some(Box::new(rsp));
{{/isFile}}
{{#headers}}{{#isFile}} {{/isFile}} header! { (Response{{nameInCamelCase}}, "{{baseName}}") => [{{{datatype}}}] }
{{#isFile}} {{/isFile}} response.headers.set(Response{{nameInCamelCase}}({{name}}));
{{/headers}}
{{#produces}}{{#-first}}
{{#dataType}}{{#isFile}} {{/isFile}} response.headers.set(ContentType(mimetypes::responses::{{#vendorExtensions}}{{uppercase_operation_id}}_{{x-uppercaseResponseId}}{{/vendorExtensions}}.clone()));{{/dataType}}
{{/-first}}{{/produces}}
{{#isFile}} {{/isFile}} context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
{{#bodyParams}}{{#vendorExtensions}}{{^consumesPlainText}} if !unused_elements.is_empty() {
response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements)));
}{{/consumesPlainText}}{{/vendorExtensions}}{{/bodyParams}}
{{^isFile}}Ok(response){{/isFile}}{{#isFile}} response
}){{/isFile}}
},
{{/responses}}
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
Err(Response::with((status::InternalServerError, "An internal error occurred".to_string())))
}
}
}
handle_request(req, &api_clone, &mut context).or_else(|mut response| {
context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
Ok(response)
})
},
"{{operationId}}");
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
}
/// Middleware to extract authentication data from request
pub struct ExtractAuthData;
impl BeforeMiddleware for ExtractAuthData {
fn before(&self, req: &mut Request) -> IronResult<()> {
{{#authMethods}}
{{#isBasic}}
{
use hyper::header::{Authorization, Basic, Bearer};
use std::ops::Deref;
if let Some(basic) = req.headers.get::>() {
req.extensions.insert::(AuthData::Basic(basic.deref().clone()));
return Ok(());
}
}
{{/isBasic}}
{{#isOAuth}}
{
use hyper::header::{Authorization, Basic, Bearer};
use std::ops::Deref;
if let Some(bearer) = req.headers.get::>() {
req.extensions.insert::(AuthData::Bearer(bearer.deref().clone()));
return Ok(());
}
}
{{/isOAuth}}
{{#isApiKey}}
{{#isKeyInHeader}}
{
header! { (ApiKey{{-index}}, "{{keyParamName}}") => [String] }
if let Some(header) = req.headers.get::() {
req.extensions.insert::(AuthData::ApiKey(header.0.clone()));
return Ok(());
}
}
{{/isKeyInHeader}}
{{#isKeyInQuery}}
{
let header = match req.get_ref::() {
Ok(query) => query.get("{{keyParamName}}").map(|v| v[0].clone()),
_ => None
};
if let Some(key) = header {
req.extensions.insert::(AuthData::ApiKey(key));
return Ok(());
}
}
{{/isKeyInQuery}}
{{/isApiKey}}
{{/authMethods}}
Ok(())
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy