Merge 'master' branch of 'swagger-codegen' into 'openapi-generator' (#327)

This commit is contained in:
Jérémie Bresson
2018-05-05 16:40:52 +02:00
committed by GitHub
parent c791146b62
commit a2b618a452
16 changed files with 458 additions and 102 deletions

View File

@@ -472,6 +472,9 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements Co
String modifiedName = name.replaceAll("\\.", "");
modifiedName = sanitizeKotlinSpecificNames(modifiedName);
// Camelize name of nested properties
modifiedName = camelize(modifiedName);
if (reservedWords.contains(modifiedName)) {
modifiedName = escapeReservedWord(modifiedName);
}
@@ -479,6 +482,12 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements Co
return titleCase(modifiedName);
}
@Override
public String toModelFilename(String name) {
// Should be the same as the model name
return toModelName(name);
}
/**
* Provides a strongly typed declaration for simple arrays of some type and arrays of arrays of some type.
*

View File

@@ -169,6 +169,13 @@
(map (fn [[k v]] [k (normalize-param v)]))
(into {})))
(defn default-to-json-mime
"Default to JSON MIME if given */* MIME"
[mime]
(if (= mime "*/*")
"application/json"
mime))
(defn json-mime?
"Check if the given MIME is a standard JSON MIME or :json."
[mime]
@@ -182,7 +189,7 @@
[mimes]
(-> (filter json-mime? mimes)
first
(or (first mimes))))
(or (default-to-json-mime (first mimes)))))
(defn serialize
"Serialize the given data according to content-type.

View File

@@ -3,11 +3,11 @@ package {{packageName}}
{{#operations}}
import (
"context"
"io/ioutil"
"net/http"
"net/url"
"strings"
"context"
{{#imports}} "{{import}}"
{{/imports}}
)

View File

@@ -3,6 +3,7 @@ package {{packageName}}
import (
"bytes"
"context"
"encoding/json"
"encoding/xml"
"errors"
@@ -20,7 +21,6 @@ import (
"time"
"unicode/utf8"
"context"
"golang.org/x/oauth2"
)

View File

@@ -9,6 +9,6 @@ import org.threeten.bp.LocalDateTime
{{#models}}
{{#model}}
{{#isEnum}}{{>enum_class}}{{/isEnum}}{{^isEnum}}{{>data_class}}{{/isEnum}}
{{#isEnum}}{{>enum_class}}{{/isEnum}}{{^isEnum}}{{#isAlias}}typealias {{classname}} = {{dataType}}{{/isAlias}}{{^isAlias}}{{>data_class}}{{/isAlias}}{{/isEnum}}
{{/model}}
{{/models}}

View File

@@ -60,17 +60,21 @@ from {{{packageName}}}.rest import ApiException
from pprint import pprint
{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}{{#hasAuthMethods}}{{#authMethods}}{{#isBasic}}
# Configure HTTP basic authorization: {{{name}}}
{{{packageName}}}.configuration.username = 'YOUR_USERNAME'
{{{packageName}}}.configuration.password = 'YOUR_PASSWORD'{{/isBasic}}{{#isApiKey}}
configuration = {{{packageName}}}.Configuration()
configuration.username = 'YOUR_USERNAME'
configuration.password = 'YOUR_PASSWORD'{{/isBasic}}{{#isApiKey}}
# Configure API key authorization: {{{name}}}
{{{packageName}}}.configuration.api_key['{{{keyParamName}}}'] = 'YOUR_API_KEY'
configuration = {{{packageName}}}.Configuration()
configuration.api_key['{{{keyParamName}}}'] = 'YOUR_API_KEY'
# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed
# {{{packageName}}}.configuration.api_key_prefix['{{{keyParamName}}}'] = 'Bearer'{{/isApiKey}}{{#isOAuth}}
# configuration.api_key_prefix['{{{keyParamName}}}'] = 'Bearer'{{/isApiKey}}{{#isOAuth}}
# Configure OAuth2 access token for authorization: {{{name}}}
{{{packageName}}}.configuration.access_token = 'YOUR_ACCESS_TOKEN'{{/isOAuth}}{{/authMethods}}
configuration = {{{packageName}}}.Configuration()
configuration.access_token = 'YOUR_ACCESS_TOKEN'{{/isOAuth}}{{/authMethods}}
{{/hasAuthMethods}}
# create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}()
api_instance = {{{packageName}}}.{{{classname}}}({{{packageName}}}.ApiClient(configuration))
{{#allParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}
{{/allParams}}

View File

@@ -19,7 +19,7 @@ chrono = { version = "0.4", features = ["serde"] }
futures = "0.1"
hyper = {version = "0.11", optional = true}
hyper-tls = {version = "0.1.2", optional = true}
swagger = "0.10.0"
swagger = "0.12.1"
# Not required by example server.
#

View File

@@ -38,7 +38,7 @@ use std::collections::{HashMap, BTreeMap};
#[allow(unused_imports)]
use swagger;
use swagger::{Context, ApiError, XSpanId};
use swagger::{ApiError, XSpanId, XSpanIdString, Has, AuthData};
use {Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
{{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
@@ -67,8 +67,7 @@ fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<S
/// A client that implements the API by making HTTP calls out to a server.
#[derive(Clone)]
pub struct Client {
hyper_client: Arc<Fn(&Handle) -> Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>> + Sync + Send>,
handle: Arc<Handle>,
hyper_client: Arc<Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>>>,
base_path: String,
}
@@ -173,25 +172,13 @@ impl Client {
where
C: hyper::client::Connect + hyper::client::Service,
{
let hyper_client = {
move |handle: &Handle| -> Box<
hyper::client::Service<
Request = hyper::Request<hyper::Body>,
Response = hyper::Response,
Error = hyper::Error,
Future = hyper::client::FutureResponse,
>,
> {
let connector = connector_fn(handle);
Box::new(hyper::Client::configure().connector(connector).build(
handle,
))
}
};
let connector = connector_fn(&handle);
let hyper_client = Box::new(hyper::Client::configure().connector(connector).build(
&handle,
));
Ok(Client {
hyper_client: Arc::new(hyper_client),
handle: Arc::new(handle),
base_path: into_base_path(base_path, protocol)?,
})
}
@@ -205,22 +192,21 @@ impl Client {
/// The reason for this function's existence is to support legacy test code, which did mocking at the hyper layer.
/// This is not a recommended way to write new tests. If other reasons are found for using this function, they
/// should be mentioned here.
pub fn try_new_with_hyper_client(hyper_client: Arc<Fn(&Handle) -> Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>> + Sync + Send>,
pub fn try_new_with_hyper_client(hyper_client: Arc<Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>>>,
handle: Handle,
base_path: &str)
-> Result<Client, ClientInitError>
{
Ok(Client {
hyper_client: hyper_client,
handle: Arc::new(handle),
base_path: into_base_path(base_path, None)?,
})
}
}
impl Api for Client {
impl<C> Api<C> for Client where C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<AuthData>>{{/hasAuthMethods}}{
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
{{#queryParams}}{{#-first}}
// Query parameters
{{/-first}}{{#required}} let query_{{paramName}} = format!("{{baseName}}={{=<% %>=}}{<% paramName %>}<%={{ }}=%>&", {{paramName}}=param_{{paramName}}{{#isListContainer}}.join(","){{/isListContainer}}{{^isListContainer}}.to_string(){{/isListContainer}});
@@ -306,9 +292,9 @@ impl Api for Client {
request.headers_mut().set(ContentType(mimetypes::requests::{{#vendorExtensions}}{{uppercase_operation_id}}{{/vendorExtensions}}.clone()));
{{/bodyParam}}
context.x_span_id.as_ref().map(|header| request.headers_mut().set(XSpanId(header.clone())));
{{#authMethods}}{{#isBasic}} context.auth_data.as_ref().map(|auth_data| {
if let &swagger::AuthData::Basic(ref basic_header) = auth_data {
request.headers_mut().set(XSpanId((context as &Has<XSpanIdString>).get().0.clone()));
{{#authMethods}}{{#isBasic}} (context as &Has<Option<AuthData>>).get().as_ref().map(|auth_data| {
if let &AuthData::Basic(ref basic_header) = auth_data {
request.headers_mut().set(hyper::header::Authorization(
basic_header.clone(),
))
@@ -326,8 +312,7 @@ impl Api for Client {
request.set_body(body_string.into_bytes());
{{/hasFile}}{{/vendorExtensions}}
let hyper_client = (self.hyper_client)(&*self.handle);
Box::new(hyper_client.call(request)
Box::new(self.hyper_client.call(request)
.map_err(|e| ApiError(format!("No response received: {}", e)))
.and_then(|mut response| {
match response.status().as_u16() {

View File

@@ -4,12 +4,15 @@ extern crate {{externCrateName}};
#[allow(unused_extern_crates)]
extern crate futures;
#[allow(unused_extern_crates)]
#[macro_use]
extern crate swagger;
#[allow(unused_extern_crates)]
extern crate uuid;
extern crate clap;
extern crate tokio_core;
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData};
#[allow(unused_imports)]
use futures::{Future, future, Stream, stream};
use tokio_core::reactor;
@@ -60,15 +63,16 @@ fn main() {
.expect("Failed to create HTTP client")
};
// Using a non-default `Context` is not required; this is just an example!
let client = client.with_context({{externCrateName}}::Context::new_with_span_id(self::uuid::Uuid::new_v4().to_string()));
let context: make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString) =
make_context!(ContextBuilder, EmptyContext, None, XSpanIdString(self::uuid::Uuid::new_v4().to_string()));
let client = client.with_context(context);
match matches.value_of("operation") {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
{{#vendorExtensions}}{{#noClientExample}}// Disabled because there's no example.
// {{/noClientExample}}Some("{{operationId}}") => {
{{#noClientExample}}// {{/noClientExample}} let result = core.run(client.{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{^-first}}, {{/-first}}{{#vendorExtensions}}{{{example}}}{{/vendorExtensions}}{{/allParams}}));
{{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>")));
{{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &Has<XSpanIdString>).get().clone());
{{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} },
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
_ => {

View File

@@ -29,6 +29,7 @@ use hyper::server::Http;
use tokio_proto::TcpServer;
use clap::{App, Arg};
use swagger::auth::AllowAllAuthenticator;
use swagger::EmptyContext;
mod server_lib;
@@ -54,9 +55,9 @@ fn main() {
.get_matches();
let service_fn =
{{externCrateName}}::server::auth::NewService::new(
{{externCrateName}}::server::auth::NewService::<_, EmptyContext>::new(
AllowAllAuthenticator::new(
server_lib::NewService,
server_lib::NewService::new(),
"cosmo"
)
);

View File

@@ -8,19 +8,31 @@ mod errors {
pub use self::errors::*;
use std::io;
use std::clone::Clone;
use std::marker::PhantomData;
use hyper;
use {{externCrateName}};
use swagger::{Has, XSpanIdString};
use swagger::auth::Authorization;
pub struct NewService;
pub struct NewService<C>{
marker: PhantomData<C>
}
impl hyper::server::NewService for NewService {
type Request = (hyper::Request, {{externCrateName}}::Context);
impl<C> NewService<C>{
pub fn new() -> Self {
NewService{marker:PhantomData}
}
}
impl<C> hyper::server::NewService for NewService<C> where C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Clone + 'static {
type Request = (hyper::Request, C);
type Response = hyper::Response;
type Error = hyper::Error;
type Instance = {{externCrateName}}::server::Service<server::Server>;
type Instance = {{externCrateName}}::server::Service<server::Server<C>, C>;
/// Instantiate a new server.
fn new_service(&self) -> io::Result<Self::Instance> {
Ok({{externCrateName}}::server::Service::new(server::Server))
Ok({{externCrateName}}::server::Service::new(server::Server::new()))
}
}

View File

@@ -7,23 +7,33 @@ use chrono;
{{#apiHasFile}}use futures::Stream;{{/apiHasFile}}
use std::collections::HashMap;
{{#apiHasFile}}use std::io::Error;{{/apiHasFile}}
use std::marker::PhantomData;
{{#apiUsesUuid}}use uuid;{{/apiUsesUuid}}
use swagger;
use swagger::{Has, XSpanIdString};
use {{externCrateName}}::{Api, ApiError, Context{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
use {{externCrateName}}::{Api, ApiError{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
{{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
};
use {{externCrateName}}::models;
#[derive(Copy, Clone)]
pub struct Server;
pub struct Server<C> {
marker: PhantomData<C>,
}
impl Api for Server {
impl<C> Server<C> {
pub fn new() -> Self {
Server{marker: PhantomData}
}
}
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
{{#summary}} /// {{{summary}}}{{/summary}}
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
let context = context.clone();
println!("{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{^isFile}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/isFile}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}{{^isFile}}, {{paramName}}{{/isFile}}{{/allParams}}, context.x_span_id.unwrap_or(String::from("<none>")).clone());{{#allParams}}{{#isFile}}
println!("{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{^isFile}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/isFile}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}{{^isFile}}, {{paramName}}{{/isFile}}{{/allParams}}, context.get().0.clone());{{#allParams}}{{#isFile}}
let _ = {{paramName}}; //Suppresses unused param warning{{/isFile}}{{/allParams}}
Box::new(futures::failed("Generic failure".into()))
}

View File

@@ -30,7 +30,7 @@ pub use futures::Future;
#[cfg(any(feature = "client", feature = "server"))]
mod mimetypes;
pub use swagger::{ApiError, Context, ContextWrapper};
pub use swagger::{ApiError, ContextWrapper};
pub const BASE_PATH: &'static str = "{{basePathWithoutHost}}";
pub const API_VERSION: &'static str = "{{appVersion}}";
@@ -48,10 +48,10 @@ pub enum {{operationId}}Response {
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
/// API
pub trait Api {
pub trait Api<C> {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
{{#summary}} /// {{{summary}}}{{/summary}}
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box<Future<Item={{operationId}}Response, Error=ApiError>>;
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>>;
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
}
@@ -64,18 +64,18 @@ pub trait ApiNoContext {
}
/// Trait to extend an API to make it easy to bind it to a context.
pub trait ContextWrapperExt<'a> where Self: Sized {
pub trait ContextWrapperExt<'a, C> where Self: Sized {
/// Binds this API to a context.
fn with_context(self: &'a Self, context: Context) -> ContextWrapper<'a, Self>;
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C>;
}
impl<'a, T: Api + Sized> ContextWrapperExt<'a> for T {
fn with_context(self: &'a T, context: Context) -> ContextWrapper<'a, T> {
ContextWrapper::<T>::new(self, context)
impl<'a, T: Api<C> + Sized, C> ContextWrapperExt<'a, C> for T {
fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> {
ContextWrapper::<T, C>::new(self, context)
}
}
impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> {
impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
{{#summary}} /// {{{summary}}}{{/summary}}
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {

View File

@@ -1,25 +1,47 @@
use std::io;
use std::marker::PhantomData;
use std::default::Default;
use hyper;
use hyper::{Request, Response, Error, StatusCode};
use server::url::form_urlencoded;
use swagger::auth::{Authorization, AuthData, Scopes};
use swagger::{Has, Pop, Push, XSpanIdString};
use Api;
pub struct NewService<T> where T: hyper::server::NewService<Request=(Request,Option<AuthData>), Response=Response, Error=Error> {
pub struct NewService<T, C>
where
C: Default + Push<XSpanIdString>,
C::Result: Push<Option<AuthData>>,
T: hyper::server::NewService<Request = (Request, <C::Result as Push<Option<AuthData>>>::Result), Response = Response, Error = Error>,
{
inner: T,
marker: PhantomData<C>,
}
impl<T> NewService<T> where T: hyper::server::NewService<Request=(Request,Option<AuthData>), Response=Response, Error=Error> + 'static {
pub fn new(inner: T) -> NewService<T> {
NewService{inner}
impl<T, C> NewService<T, C>
where
C: Default + Push<XSpanIdString>,
C::Result: Push<Option<AuthData>>,
T: hyper::server::NewService<Request = (Request, <C::Result as Push<Option<AuthData>>>::Result), Response = Response, Error = Error> + 'static,
{
pub fn new(inner: T) -> NewService<T, C> {
NewService {
inner,
marker: PhantomData,
}
}
}
impl<T> hyper::server::NewService for NewService<T> where T: hyper::server::NewService<Request=(Request,Option<AuthData>), Response=Response, Error=Error> + 'static {
impl<T, C> hyper::server::NewService for NewService<T, C>
where
C: Default + Push<XSpanIdString>,
C::Result: Push<Option<AuthData>>,
T: hyper::server::NewService<Request = (Request, <C::Result as Push<Option<AuthData>>>::Result), Response = Response, Error = Error> + 'static,
{
type Request = Request;
type Response = Response;
type Error = Error;
type Instance = Service<T::Instance>;
type Instance = Service<T::Instance, C>;
fn new_service(&self) -> Result<Self::Instance, io::Error> {
self.inner.new_service().map(|s| Service::new(s))
@@ -27,23 +49,44 @@ impl<T> hyper::server::NewService for NewService<T> where T: hyper::server::NewS
}
/// Middleware to extract authentication data from request
pub struct Service<T> where T: hyper::server::Service<Request=(Request,Option<AuthData>), Response=Response, Error=Error> {
pub struct Service<T, C>
where
C: Default + Push<XSpanIdString>,
C::Result: Push<Option<AuthData>>,
T: hyper::server::Service<Request = (Request, <C::Result as Push<Option<AuthData>>>::Result), Response = Response, Error = Error>,
{
inner: T,
marker: PhantomData<C>,
}
impl<T> Service<T> where T: hyper::server::Service<Request=(Request,Option<AuthData>), Response=Response, Error=Error> {
pub fn new(inner: T) -> Service<T> {
Service{inner}
impl<T, C> Service<T, C>
where
C: Default + Push<XSpanIdString>,
C::Result: Push<Option<AuthData>>,
T: hyper::server::Service<Request = (Request, <C::Result as Push<Option<AuthData>>>::Result), Response = Response, Error = Error>,
{
pub fn new(inner: T) -> Service<T, C> {
Service {
inner,
marker: PhantomData,
}
}
}
impl<T> hyper::server::Service for Service<T> where T: hyper::server::Service<Request=(Request,Option<AuthData>), Response=Response, Error=Error> {
impl<T, C> hyper::server::Service for Service<T, C>
where
C: Default + Push<XSpanIdString>,
C::Result: Push<Option<AuthData>>,
T: hyper::server::Service<Request = (Request, <C::Result as Push<Option<AuthData>>>::Result), Response = Response, Error = Error>,
{
type Request = Request;
type Response = Response;
type Error = Error;
type Future = T::Future;
fn call(&self, req: Self::Request) -> Self::Future {
let context = C::default().push(XSpanIdString::get_or_generate(&req));
{{#authMethods}}
{{#isBasic}}
{
@@ -51,7 +94,8 @@ impl<T> hyper::server::Service for Service<T> where T: hyper::server::Service<Re
use std::ops::Deref;
if let Some(basic) = req.headers().get::<Authorization<Basic>>().cloned() {
let auth_data = AuthData::Basic(basic.deref().clone());
return self.inner.call((req, Some(auth_data)));
let context = context.push(Some(auth_data));
return self.inner.call((req, context));
}
}
{{/isBasic}}
@@ -61,7 +105,8 @@ impl<T> hyper::server::Service for Service<T> where T: hyper::server::Service<Re
use std::ops::Deref;
if let Some(bearer) = req.headers().get::<Authorization<Bearer>>().cloned() {
let auth_data = AuthData::Bearer(bearer.deref().clone());
return self.inner.call((req, Some(auth_data)));
let context = context.push(Some(auth_data));
return self.inner.call((req, context));
}
}
{{/isOAuth}}
@@ -71,7 +116,8 @@ impl<T> hyper::server::Service for Service<T> where T: hyper::server::Service<Re
header! { (ApiKey{{-index}}, "{{keyParamName}}") => [String] }
if let Some(header) = req.headers().get::<ApiKey{{-index}}>().cloned() {
let auth_data = AuthData::ApiKey(header.0);
return self.inner.call((req, Some(auth_data)));
let context = context.push(Some(auth_data));
return self.inner.call((req, context));
}
}
{{/isKeyInHeader}}
@@ -83,13 +129,15 @@ impl<T> hyper::server::Service for Service<T> where T: hyper::server::Service<Re
.nth(0);
if let Some(key) = key {
let auth_data = AuthData::ApiKey(key);
return self.inner.call((req, Some(auth_data)));
let context = context.push(Some(auth_data));
return self.inner.call((req, context));
}
}
{{/isKeyInQuery}}
{{/isApiKey}}
{{/authMethods}}
return self.inner.call((req, None));
let context = context.push(None);
return self.inner.call((req, context));
}
}

View File

@@ -13,6 +13,7 @@ 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};
@@ -35,7 +36,7 @@ use std::io;
use std::collections::BTreeSet;
pub use swagger::auth::Authorization;
use swagger::{ApiError, Context, XSpanId};
use swagger::{ApiError, XSpanId, XSpanIdString, Has};
use swagger::auth::Scopes;
use {Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
@@ -68,39 +69,56 @@ mod paths {
{{/pathSet}}
}
pub struct NewService<T> {
pub struct NewService<T, C> {
api_impl: Arc<T>,
marker: PhantomData<C>,
}
impl<T> NewService<T> where T: Api + Clone + 'static {
pub fn new<U: Into<Arc<T>>>(api_impl: U) -> NewService<T> {
NewService{api_impl: api_impl.into()}
impl<T, C> NewService<T, C>
where
T: Api<C> + Clone + 'static,
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static
{
pub fn new<U: Into<Arc<T>>>(api_impl: U) -> NewService<T, C> {
NewService{api_impl: api_impl.into(), marker: PhantomData}
}
}
impl<T> hyper::server::NewService for NewService<T> where T: Api + Clone + 'static {
type Request = (Request, Context);
impl<T, C> hyper::server::NewService for NewService<T, C>
where
T: Api<C> + Clone + 'static,
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static
{
type Request = (Request, C);
type Response = Response;
type Error = Error;
type Instance = Service<T>;
type Instance = Service<T, C>;
fn new_service(&self) -> Result<Self::Instance, io::Error> {
Ok(Service::new(self.api_impl.clone()))
}
}
pub struct Service<T> {
pub struct Service<T, C> {
api_impl: Arc<T>,
marker: PhantomData<C>,
}
impl<T> Service<T> where T: Api + Clone + 'static {
pub fn new<U: Into<Arc<T>>>(api_impl: U) -> Service<T> {
Service{api_impl: api_impl.into()}
impl<T, C> Service<T, C>
where
T: Api<C> + Clone + 'static,
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static {
pub fn new<U: Into<Arc<T>>>(api_impl: U) -> Service<T, C> {
Service{api_impl: api_impl.into(), marker: PhantomData}
}
}
impl<T> hyper::server::Service for Service<T> where T: Api + Clone + 'static {
type Request = (Request, Context);
impl<T, C> hyper::server::Service for Service<T, C>
where
T: Api<C> + Clone + 'static,
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static
{
type Request = (Request, C);
type Response = Response;
type Error = Error;
type Future = Box<Future<Item=Response, Error=Error>>;
@@ -113,14 +131,11 @@ impl<T> hyper::server::Service for Service<T> where T: Api + Clone + 'static {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
// {{operationId}} - {{httpMethod}} {{path}}
&hyper::Method::{{vendorExtensions.HttpMethod}} if path.matched(paths::ID_{{vendorExtensions.PATH_ID}}) => {
if context.x_span_id.is_none() {
context.x_span_id = Some(headers.get::<XSpanId>().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()));
}
{{#hasAuthMethods}}
{
let authorization = match context.authorization.as_ref() {
Some(authorization) => authorization,
None => return Box::new(future::ok(Response::new()
let authorization = match (&context as &Has<Option<Authorization>>).get() {
&Some(ref authorization) => authorization,
&None => return Box::new(future::ok(Response::new()
.with_status(StatusCode::Forbidden)
.with_body("Unauthenticated"))),
};
@@ -304,7 +319,7 @@ impl<T> hyper::server::Service for Service<T> where T: Api + Clone + 'static {
{{^required}}{{#isFile}} let param_{{paramName}} = Box::new(future::ok(param_{{paramName}}));{{/isFile}}{{/required}}
{{/formParams}}
{{/hasFile}}{{^hasFile}}
Box::new(({
Box::new({
{{
{{#formParams}}{{#-first}}
// Form parameters
@@ -315,7 +330,7 @@ impl<T> hyper::server::Service for Service<T> where T: Api + Clone + 'static {
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();
context.x_span_id.as_ref().map(|header| response.headers_mut().set(XSpanId(header.clone())));
response.headers_mut().set(XSpanId((&context as &Has<XSpanIdString>).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)));
@@ -410,7 +425,7 @@ impl<T> hyper::server::Service for Service<T> where T: Api + Clone + 'static {
))
{{^bodyParams}}{{#vendorExtensions}}{{^hasFile}}
}}
})) as Box<Future<Item=Response, Error=Error>>
}) as Box<Future<Item=Response, Error=Error>>
{{/hasFile}}{{#hasFile}}
as Box<Future<Item=Response, Error=Error>>
},

View File

@@ -0,0 +1,261 @@
package org.openapitools.codegen.kotlin;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.DateTimeSchema;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.MapSchema;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.DefaultCodegen;
import org.openapitools.codegen.languages.KotlinClientCodegen;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@SuppressWarnings("static-method")
public class KotlinClientCodegenModelTest {
private Schema getArrayTestSchema() {
return new ObjectSchema()
.description("a sample model")
.addProperties("id", new IntegerSchema().format("int64"))
.addProperties("examples", new ArraySchema().items(new StringSchema()))
.addRequiredItem("id");
}
private Schema getSimpleSchema() {
return new ObjectSchema()
.description("a sample model")
.addProperties("id", new IntegerSchema().format("int64"))
.addProperties("name", new StringSchema())
.addProperties("createdAt", new DateTimeSchema())
.addRequiredItem("id")
.addRequiredItem("name");
}
private Schema getMapSchema() {
return new ObjectSchema()
.description("a sample model")
.addProperties("mapping", new MapSchema()
.additionalProperties(new StringSchema()));
}
private Schema getComplexSchema() {
return new ObjectSchema()
.description("a sample model")
.addProperties("child", new ObjectSchema().$ref("#/components/schemas/Child"));
}
@Test(description = "convert a simple model")
public void simpleModelTest() {
final Schema schema = getSimpleSchema();
final DefaultCodegen codegen = new KotlinClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", schema);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 3);
final CodegenProperty property1 = cm.vars.get(0);
Assert.assertEquals(property1.baseName, "id");
Assert.assertEquals(property1.datatype, "kotlin.Long");
Assert.assertEquals(property1.name, "id");
Assert.assertEquals(property1.defaultValue, "null");
Assert.assertEquals(property1.baseType, "kotlin.Long");
Assert.assertTrue(property1.hasMore);
Assert.assertTrue(property1.required);
Assert.assertTrue(property1.isPrimitiveType);
Assert.assertTrue(property1.isNotContainer);
final CodegenProperty property2 = cm.vars.get(1);
Assert.assertEquals(property2.baseName, "name");
Assert.assertEquals(property2.datatype, "kotlin.String");
Assert.assertEquals(property2.name, "name");
Assert.assertEquals(property2.defaultValue, "null");
Assert.assertEquals(property2.baseType, "kotlin.String");
Assert.assertTrue(property2.hasMore);
Assert.assertTrue(property2.required);
Assert.assertTrue(property2.isPrimitiveType);
Assert.assertTrue(property2.isNotContainer);
final CodegenProperty property3 = cm.vars.get(2);
Assert.assertEquals(property3.baseName, "createdAt");
Assert.assertEquals(property3.datatype, "java.time.LocalDateTime");
Assert.assertEquals(property3.name, "createdAt");
Assert.assertEquals(property3.defaultValue, "null");
Assert.assertEquals(property3.baseType, "java.time.LocalDateTime");
Assert.assertFalse(property3.hasMore);
Assert.assertFalse(property3.required);
Assert.assertTrue(property3.isNotContainer);
}
@Test(description = "convert a simple model: threetenbp")
public void selectDateLibraryAsThreetenbp() {
final Schema schema = getSimpleSchema();
final KotlinClientCodegen codegen = new KotlinClientCodegen();
codegen.setDateLibrary(KotlinClientCodegen.DateLibrary.THREETENBP.value);
codegen.processOpts();
final CodegenModel cm = codegen.fromModel("sample", schema);
final CodegenProperty property3 = cm.vars.get(2);
Assert.assertEquals(property3.baseName, "createdAt");
Assert.assertEquals(property3.datatype, "org.threeten.bp.LocalDateTime");
Assert.assertEquals(property3.name, "createdAt");
Assert.assertEquals(property3.defaultValue, "null");
Assert.assertEquals(property3.baseType, "org.threeten.bp.LocalDateTime");
Assert.assertFalse(property3.hasMore);
Assert.assertFalse(property3.required);
Assert.assertTrue(property3.isNotContainer);
}
@Test(description = "convert a simple model: date string")
public void selectDateLibraryAsString() {
final Schema schema = getSimpleSchema();
final KotlinClientCodegen codegen = new KotlinClientCodegen();
codegen.setDateLibrary(KotlinClientCodegen.DateLibrary.STRING.value);
codegen.processOpts();
final CodegenModel cm = codegen.fromModel("sample", schema);
final CodegenProperty property3 = cm.vars.get(2);
Assert.assertEquals(property3.baseName, "createdAt");
Assert.assertEquals(property3.datatype, "kotlin.String");
Assert.assertEquals(property3.name, "createdAt");
Assert.assertEquals(property3.defaultValue, "null");
Assert.assertEquals(property3.baseType, "kotlin.String");
Assert.assertFalse(property3.hasMore);
Assert.assertFalse(property3.required);
Assert.assertTrue(property3.isNotContainer);
}
@Test(description = "convert a simple model: date java8")
public void selectDateLibraryAsJava8() {
final Schema schema = getSimpleSchema();
final KotlinClientCodegen codegen = new KotlinClientCodegen();
codegen.setDateLibrary(KotlinClientCodegen.DateLibrary.JAVA8.value);
codegen.processOpts();
final CodegenModel cm = codegen.fromModel("sample", schema);
final CodegenProperty property3 = cm.vars.get(2);
Assert.assertEquals(property3.baseName, "createdAt");
Assert.assertEquals(property3.datatype, "java.time.LocalDateTime");
Assert.assertEquals(property3.name, "createdAt");
Assert.assertEquals(property3.defaultValue, "null");
Assert.assertEquals(property3.baseType, "java.time.LocalDateTime");
Assert.assertFalse(property3.hasMore);
Assert.assertFalse(property3.required);
Assert.assertTrue(property3.isNotContainer);
}
@Test(description = "convert a model with array property to default kotlin.Array")
public void arrayPropertyTest() {
final Schema model = getArrayTestSchema();
final DefaultCodegen codegen = new KotlinClientCodegen();
final CodegenModel generated = codegen.fromModel("sample", model);
Assert.assertEquals(generated.name, "sample");
Assert.assertEquals(generated.classname, "Sample");
Assert.assertEquals(generated.description, "a sample model");
Assert.assertEquals(generated.vars.size(), 2);
final CodegenProperty property = generated.vars.get(1);
Assert.assertEquals(property.baseName, "examples");
Assert.assertEquals(property.getter, "getExamples");
Assert.assertEquals(property.setter, "setExamples");
Assert.assertEquals(property.datatype, "kotlin.Array<kotlin.String>");
Assert.assertEquals(property.name, "examples");
Assert.assertEquals(property.defaultValue, "null");
Assert.assertEquals(property.baseType, "kotlin.Array");
Assert.assertEquals(property.containerType, "array");
Assert.assertFalse(property.required);
Assert.assertTrue(property.isContainer);
}
@Test(description = "convert a model with a map property")
public void mapPropertyTest() {
final Schema schema = getMapSchema();
final DefaultCodegen codegen = new KotlinClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", schema);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty property1 = cm.vars.get(0);
Assert.assertEquals(property1.baseName, "mapping");
Assert.assertEquals(property1.datatype, "kotlin.collections.Map<kotlin.String, kotlin.String>");
Assert.assertEquals(property1.name, "mapping");
Assert.assertEquals(property1.baseType, "kotlin.collections.Map");
Assert.assertEquals(property1.containerType, "map");
Assert.assertFalse(property1.required);
Assert.assertTrue(property1.isContainer);
Assert.assertTrue(property1.isPrimitiveType);
}
@Test(description = "convert a model with complex property")
public void complexPropertyTest() {
final Schema schema = getComplexSchema();
final DefaultCodegen codegen = new KotlinClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", schema);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty property1 = cm.vars.get(0);
Assert.assertEquals(property1.baseName, "child");
Assert.assertEquals(property1.datatype, "Child");
Assert.assertEquals(property1.name, "child");
Assert.assertEquals(property1.baseType, "Child");
Assert.assertFalse(property1.required);
Assert.assertTrue(property1.isNotContainer);
}
@DataProvider(name = "modelNames")
public static Object[][] modelNames() {
return new Object[][]{
{"TestNs.TestClass", new ModelNameTest("TestNs.TestClass", "TestNsTestClass")},
{"$", new ModelNameTest("$", "Dollar")},
{"for", new ModelNameTest("`for`", "For")},
{"One<Two", new ModelNameTest("One<Two", "OneLessThanTwo")},
{"this is a test", new ModelNameTest("this is a test", "ThisIsATest")}
};
}
@Test(dataProvider = "modelNames", description = "sanitize model names")
public void sanitizeModelNames(final String name, final ModelNameTest testCase) {
final Schema schema = getComplexSchema();
final DefaultCodegen codegen = new KotlinClientCodegen();
final CodegenModel cm = codegen.fromModel(name, schema);
Assert.assertEquals(cm.name, testCase.expectedName);
Assert.assertEquals(cm.classname, testCase.expectedClassName);
}
private static class ModelNameTest {
private String expectedName;
private String expectedClassName;
private ModelNameTest(String nameAndClass) {
this.expectedName = nameAndClass;
this.expectedClassName = nameAndClass;
}
private ModelNameTest(String expectedName, String expectedClassName) {
this.expectedName = expectedName;
this.expectedClassName = expectedClassName;
}
}
}