All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rust-server.server-mod.mustache Maven / Gradle / Ivy

There is a newer version: 7.7.0
Show newest version
#![allow(unused_extern_crates)]
extern crate serde_ignored;
extern crate tokio_core;
extern crate native_tls;
extern crate hyper_tls;
extern crate openssl;
extern crate mime;
{{^apiUsesUuid}}extern crate uuid;{{/apiUsesUuid}}
extern crate chrono;
extern crate percent_encoding;
extern crate url;

{{#apiUsesUuid}}use uuid;{{/apiUsesUuid}}
use std::sync::Arc;
use std::marker::PhantomData;
use futures::{Future, future, Stream, stream};
use hyper;
use hyper::{Request, Response, Error, StatusCode};
use hyper::header::{Headers, ContentType};
use self::url::form_urlencoded;
use mimetypes;

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;

#[allow(unused_imports)]
use std::collections::BTreeSet;

pub use swagger::auth::Authorization;
use swagger::{ApiError, XSpanId, XSpanIdString, Has, RequestParser};
use swagger::auth::Scopes;

use {Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
     {{{operationId}}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
     };
#[allow(unused_imports)]
use models;

pub mod context;

header! { (Warning, "Warning") => [String] }

mod paths {
    extern crate regex;

    lazy_static! {
        pub static ref GLOBAL_REGEX_SET: regex::RegexSet = regex::RegexSet::new(&[
{{#pathSet}}
            r"^{{{basePathWithoutHost}}}{{{pathRegEx}}}"{{^-last}},{{/-last}}
{{/pathSet}}
        ]).unwrap();
    }
{{#pathSet}}
    pub static ID_{{{PATH_ID}}}: usize = {{{index}}};
{{#hasPathParams}}
    lazy_static! {
        pub static ref REGEX_{{{PATH_ID}}}: regex::Regex = regex::Regex::new(r"^{{{basePathWithoutHost}}}{{{pathRegEx}}}").unwrap();
    }
{{/hasPathParams}}
{{/pathSet}}
}

pub struct NewService {
    api_impl: Arc,
    marker: PhantomData,
}

impl NewService
where
    T: Api + Clone + 'static,
    C: Has {{#hasAuthMethods}}+ Has>{{/hasAuthMethods}} + 'static
{
    pub fn new>>(api_impl: U) -> NewService {
        NewService{api_impl: api_impl.into(), marker: PhantomData}
    }
}

impl hyper::server::NewService for NewService
where
    T: Api + Clone + 'static,
    C: Has {{#hasAuthMethods}}+ Has>{{/hasAuthMethods}} + 'static
{
    type Request = (Request, C);
    type Response = Response;
    type Error = Error;
    type Instance = Service;

    fn new_service(&self) -> Result {
        Ok(Service::new(self.api_impl.clone()))
    }
}

pub struct Service {
    api_impl: Arc,
    marker: PhantomData,
}

impl Service
where
    T: Api + Clone + 'static,
    C: Has {{#hasAuthMethods}}+ Has>{{/hasAuthMethods}} + 'static {
    pub fn new>>(api_impl: U) -> Service {
        Service{api_impl: api_impl.into(), marker: PhantomData}
    }
}

impl hyper::server::Service for Service
where
    T: Api + Clone + 'static,
    C: Has {{#hasAuthMethods}}+ Has>{{/hasAuthMethods}} + 'static
{
    type Request = (Request, C);
    type Response = Response;
    type Error = Error;
    type Future = Box>;

    fn call(&self, (req, mut context): Self::Request) -> Self::Future {
        let api_impl = self.api_impl.clone();
        let (method, uri, _, headers, body) = req.deconstruct();
        let path = paths::GLOBAL_REGEX_SET.matches(uri.path());

        // This match statement is duplicated below in `parse_operation_id()`.
        // Please update both places if changing how this code is autogenerated.
        match &method {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
            // {{{operationId}}} - {{{httpMethod}}} {{{path}}}
            &hyper::Method::{{vendorExtensions.HttpMethod}} if path.matched(paths::ID_{{vendorExtensions.PATH_ID}}) => {
{{#hasAuthMethods}}
                {
                    let authorization = match (&context as &Has>).get() {
                        &Some(ref authorization) => authorization,
                        &None => return Box::new(future::ok(Response::new()
                                                .with_status(StatusCode::Forbidden)
                                                .with_body("Unauthenticated"))),
                    };

                    {{#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 Box::new(future::ok(Response::new()
                                .with_status(StatusCode::Forbidden)
                                .with_body(missing_scopes.fold(
                                    "Insufficient authorization, missing scopes".to_string(),
                                    |s, scope| format!("{} {}", s, scope)
                                ))
                            ));
                        }
                    }
                    {{/isOAuth}}
                    {{/authMethods}}
                }
{{/hasAuthMethods}}

{{#vendorExtensions}}{{#hasPathParams}}
                // Path parameters
                let path = uri.path().to_string();
                let path_params =
                    paths::REGEX_{{{PATH_ID}}}
                    .captures(&path)
                    .unwrap_or_else(||
                        panic!("Path {} matched RE {{{PATH_ID}}} in set but failed match against \"{}\"", path, paths::REGEX_{{{PATH_ID}}}.as_str())
                    );
{{/hasPathParams}}{{/vendorExtensions}}
{{#pathParams}}
                let param_{{{paramName}}} = match percent_encoding::percent_decode(path_params["{{{baseName}}}"].as_bytes()).decode_utf8() {
                    Ok(param_{{{paramName}}}) => match param_{{{paramName}}}.parse::<{{{dataType}}}>() {
                        Ok(param_{{{paramName}}}) => param_{{{paramName}}},
                        Err(e) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't parse path parameter {{{baseName}}}: {}", e)))),
                    },
                    Err(_) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't percent-decode path parameter as UTF-8: {}", &path_params["{{{baseName}}}"]))))
                };
{{/pathParams}}
{{#headerParams}}{{#-first}}
                // Header parameters
{{/-first}}
                header! { (Request{{vendorExtensions.typeName}}, "{{{baseName}}}") => {{#isListContainer}}({{{baseType}}})*{{/isListContainer}}{{^isListContainer}}[{{{dataType}}}]{{/isListContainer}} }
{{#required}}
                let param_{{{paramName}}} = match headers.get::() {
                    Some(param_{{{paramName}}}) => param_{{{paramName}}}.0.clone(),
                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body("Missing or invalid required header {{{baseName}}}"))),
                };
{{/required}}
{{^required}}
                let param_{{{paramName}}} = 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 = form_urlencoded::parse(uri.query().unwrap_or_default().as_bytes()).collect::>();
{{/-first}}
                let param_{{{paramName}}} = query_params.iter().filter(|e| e.0 == "{{{baseName}}}").map(|e| e.1.to_owned())
{{#isListContainer}}
                    .filter_map(|param_{{{paramName}}}| param_{{{paramName}}}.parse::<{{{baseType}}}>().ok())
                    .collect::>();
{{^required}}
                let param_{{{paramName}}} = if !param_{{{paramName}}}.is_empty() {
                    Some(param_{{{paramName}}})
                } else {
                    None
                };
{{/required}}
{{/isListContainer}}{{^isListContainer}}
                    .nth(0);
{{#required}}
                let param_{{{paramName}}} = match param_{{{paramName}}} {
                    Some(param_{{{paramName}}}) => match param_{{{paramName}}}.parse::<{{{dataType}}}>() {
                        Ok(param_{{{paramName}}}) => param_{{{paramName}}},
                        Err(e) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't parse query parameter {{{baseName}}} - doesn't match schema: {}", e)))),
                    },
                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body("Missing required query parameter {{{baseName}}}"))),
                };
{{/required}}{{^required}}
                let param_{{{paramName}}} = param_{{{paramName}}}.and_then(|param_{{{paramName}}}| param_{{{paramName}}}.parse::<{{{baseType}}}>().ok());
{{/required}}
{{/isListContainer}}
{{/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.
                Box::new(body.concat2()
                    .then(move |result| -> Box> {
                        match result {
                            Ok(body) => {
{{#vendorExtensions}}{{^consumesPlainText}}
                                let mut unused_elements = Vec::new();
{{/consumesPlainText}}
                                let param_{{{paramName}}}: Option<{{{dataType}}}> = if !body.is_empty() {
{{#consumesXml}}
                                    let deserializer = &mut serde_xml_rs::de::Deserializer::new_from_reader(&*body);
{{/consumesXml}}{{#consumesJson}}
                                    let deserializer = &mut serde_json::Deserializer::from_slice(&*body);
{{/consumesJson}}{{^consumesPlainText}}
                                    match serde_ignored::deserialize(deserializer, |path| {
                                            warn!("Ignoring unknown field in body: {}", path);
                                            unused_elements.push(path.to_string());
                                    }) {
                                        Ok(param_{{{paramName}}}) => param_{{{paramName}}},
{{#required}}
                                        Err(e) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't parse body parameter {{{baseName}}} - doesn't match schema: {}", e)))),
{{/required}}{{^required}}
                                        Err(_) => None,
{{/required}}
                                    }
{{/consumesPlainText}}{{#consumesPlainText}}
                                    match String::from_utf8(body.to_vec()) {
                                        Ok(param_{{{paramName}}}) => Some(param_{{{paramName}}}),
                                        Err(e) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't parse body parameter {{{baseName}}} - not valid UTF-8: {}", e)))),
                                    }
{{/consumesPlainText}}{{/vendorExtensions}}
                                } else {
                                    None
                                };
{{#required}}
                                let param_{{{paramName}}} = match param_{{{paramName}}} {
                                    Some(param_{{{paramName}}}) => param_{{{paramName}}},
                                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body("Missing required body parameter {{{baseName}}}"))),
                                };
{{/required}}
{{/-first}}{{/bodyParams}}
{{^bodyParams}}{{#vendorExtensions}}
                Box::new({
                        {{
{{#formParams}}{{#-first}}
                                // Form parameters
{{/-first}}
                                let param_{{{paramName}}} = {{^isContainer}}{{#vendorExtensions}}{{{example}}};{{/vendorExtensions}}{{/isContainer}}{{#isListContainer}}{{#required}}Vec::new();{{/required}}{{^required}}None;{{/required}}{{/isListContainer}}{{#isMapContainer}}None;{{/isMapContainer}}
{{/formParams}}
{{/vendorExtensions}}{{/bodyParams}}
                                Box::new(api_impl.{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}({{#allParams}}param_{{{paramName}}}{{#isListContainer}}.as_ref(){{/isListContainer}}, {{/allParams}}&context)
                                    .then(move |result| {
                                        let mut response = Response::new();
                                        response.headers_mut().set(XSpanId((&context as &Has).get().0.to_string()));
{{#bodyParams}}{{#vendorExtensions}}{{^consumesPlainText}}
                                        if !unused_elements.is_empty() {
                                            response.headers_mut().set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements)));
                                        }
{{/consumesPlainText}}{{/vendorExtensions}}{{/bodyParams}}
                                        match result {
                                            Ok(rsp) => match rsp {
{{#responses}}
                                                {{{operationId}}}Response::{{#vendorExtensions}}{{x-responseId}}{{/vendorExtensions}}
{{#dataType}}{{^headers}}
                                                    (body)
{{/headers}}{{#headers}}
{{#-first}}
                                                    {
                                                        body,
{{/-first}}
                                                        {{{name}}}{{^-last}}, {{/-last}}
{{#-last}}
                                                    }
{{/-last}}
{{/headers}}{{/dataType}}
{{^dataType}}{{#headers}}{{#-first}}
                                                    {
{{/-first}}
                                                        {{{name}}}{{^-last}}, {{/-last}}
{{#-last}}
                                                    }
{{/-last}}
{{/headers}}{{/dataType}}
                                                => {
                                                    response.set_status(StatusCode::try_from({{{code}}}).unwrap());
{{#headers}}
                                                    header! { (Response{{{nameInCamelCase}}}, "{{{baseName}}}") => [{{{dataType}}}] }
                                                    response.headers_mut().set(Response{{{nameInCamelCase}}}({{{name}}}));
{{/headers}}
{{#produces}}{{#-first}}{{#dataType}}
                                                    response.headers_mut().set(ContentType(mimetypes::responses::{{#vendorExtensions}}{{{uppercase_operation_id}}}_{{x-uppercaseResponseId}}{{/vendorExtensions}}.clone()));
{{/dataType}}{{/-first}}{{/produces}}
{{#dataType}}
{{#vendorExtensions}}{{#producesXml}}{{^has_namespace}}
                                                    let body = 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 = serde_xml_rs::to_string_with_namespaces(&body, namespaces).expect("impossible to fail to serialize");
{{/has_namespace}}{{/producesXml}}{{#producesJson}}
                                                    let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
{{/producesJson}}{{/vendorExtensions}}
                                                    response.set_body(body);
{{/dataType}}
                                                },
{{/responses}}
                                            },
                                            Err(_) => {
                                                // Application code returned an error. This should not happen, as the implementation should
                                                // return a valid response.
                                                response.set_status(StatusCode::InternalServerError);
                                                response.set_body("An internal error occurred");
                                            },
                                        }

                                        future::ok(response)
                                    }
                                ))
{{^bodyParams}}{{#vendorExtensions}}
                        }}
                }) as Box>
{{/vendorExtensions}}{{/bodyParams}}
{{#bodyParams}}{{#-first}}
                            },
                            Err(e) => Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't read body parameter {{{baseName}}}: {}", e)))),
                        }
                    })
                ) as Box>
{{/-first}}{{/bodyParams}}
            },

{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
            _ => Box::new(future::ok(Response::new().with_status(StatusCode::NotFound))) as Box>,
        }
    }
}

impl Clone for Service
{
    fn clone(&self) -> Self {
        Service {
            api_impl: self.api_impl.clone(),
            marker: self.marker.clone(),
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy