#![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(()) } }