diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java index e643328a53fa..4b42ecdeaa74 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java @@ -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. * diff --git a/modules/openapi-generator/src/main/resources/clojure/core.mustache b/modules/openapi-generator/src/main/resources/clojure/core.mustache index 0da2a4514954..d55442603ae0 100644 --- a/modules/openapi-generator/src/main/resources/clojure/core.mustache +++ b/modules/openapi-generator/src/main/resources/clojure/core.mustache @@ -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. diff --git a/modules/openapi-generator/src/main/resources/go/api.mustache b/modules/openapi-generator/src/main/resources/go/api.mustache index bd4904daf569..d4a4ce7740e3 100644 --- a/modules/openapi-generator/src/main/resources/go/api.mustache +++ b/modules/openapi-generator/src/main/resources/go/api.mustache @@ -3,11 +3,11 @@ package {{packageName}} {{#operations}} import ( + "context" "io/ioutil" "net/http" "net/url" "strings" - "context" {{#imports}} "{{import}}" {{/imports}} ) diff --git a/modules/openapi-generator/src/main/resources/go/client.mustache b/modules/openapi-generator/src/main/resources/go/client.mustache index c2b2d9ea5e6f..d243a8595714 100644 --- a/modules/openapi-generator/src/main/resources/go/client.mustache +++ b/modules/openapi-generator/src/main/resources/go/client.mustache @@ -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" ) diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/model.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/model.mustache index bb27cf99c597..f0c5a9bb4a4b 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-client/model.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-client/model.mustache @@ -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}} diff --git a/modules/openapi-generator/src/main/resources/python/README.mustache b/modules/openapi-generator/src/main/resources/python/README.mustache index 184dbdc61d3e..cf92dc583db1 100644 --- a/modules/openapi-generator/src/main/resources/python/README.mustache +++ b/modules/openapi-generator/src/main/resources/python/README.mustache @@ -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}} diff --git a/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache b/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache index 787d5c72ce8c..c52c1e889771 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache @@ -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. # diff --git a/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache b/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache index 9ed53c0b8074..12768e25f303 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache @@ -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 Box, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>> + Sync + Send>, - handle: Arc, + hyper_client: Arc, 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, - 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 Box, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>> + Sync + Send>, + pub fn try_new_with_hyper_client(hyper_client: Arc, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>>>, handle: Handle, base_path: &str) -> Result { Ok(Client { hyper_client: hyper_client, - handle: Arc::new(handle), base_path: into_base_path(base_path, None)?, }) } } -impl Api for Client { +impl Api for Client where C: Has {{#hasAuthMethods}}+ Has>{{/hasAuthMethods}}{ {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} - fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box> { + fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box> { {{#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).get().0.clone())); +{{#authMethods}}{{#isBasic}} (context as &Has>).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() { diff --git a/modules/openapi-generator/src/main/resources/rust-server/example-client.mustache b/modules/openapi-generator/src/main/resources/rust-server/example-client.mustache index 2245ae475628..0620589c60ea 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/example-client.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/example-client.mustache @@ -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, 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(""))); + {{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &Has).get().clone()); {{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} }, {{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} _ => { diff --git a/modules/openapi-generator/src/main/resources/rust-server/example-server.mustache b/modules/openapi-generator/src/main/resources/rust-server/example-server.mustache index ee99d434bb36..0dbc3cacca06 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/example-server.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/example-server.mustache @@ -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" ) ); diff --git a/modules/openapi-generator/src/main/resources/rust-server/example-server_lib.mustache b/modules/openapi-generator/src/main/resources/rust-server/example-server_lib.mustache index d02d0525b47c..761d3e6b07f5 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/example-server_lib.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/example-server_lib.mustache @@ -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{ + marker: PhantomData +} -impl hyper::server::NewService for NewService { - type Request = (hyper::Request, {{externCrateName}}::Context); +impl NewService{ + pub fn new() -> Self { + NewService{marker:PhantomData} + } +} + +impl hyper::server::NewService for NewService where C: Has {{#hasAuthMethods}}+ Has>{{/hasAuthMethods}} + Clone + 'static { + type Request = (hyper::Request, C); type Response = hyper::Response; type Error = hyper::Error; - type Instance = {{externCrateName}}::server::Service; + type Instance = {{externCrateName}}::server::Service, C>; /// Instantiate a new server. fn new_service(&self) -> io::Result { - Ok({{externCrateName}}::server::Service::new(server::Server)) + Ok({{externCrateName}}::server::Service::new(server::Server::new())) } } diff --git a/modules/openapi-generator/src/main/resources/rust-server/example-server_server.mustache b/modules/openapi-generator/src/main/resources/rust-server/example-server_server.mustache index fdb7ceaf541d..c5122fcefac0 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/example-server_server.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/example-server_server.mustache @@ -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 { + marker: PhantomData, +} -impl Api for Server { +impl Server { + pub fn new() -> Self { + Server{marker: PhantomData} + } +} + +impl Api for Server where C: Has{ {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} {{#summary}} /// {{{summary}}}{{/summary}} - fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box> { + fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box> { 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("")).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())) } diff --git a/modules/openapi-generator/src/main/resources/rust-server/lib.mustache b/modules/openapi-generator/src/main/resources/rust-server/lib.mustache index 6c54cdda649f..10cd24f4b3e3 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/lib.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/lib.mustache @@ -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 { {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} {{#summary}} /// {{{summary}}}{{/summary}} - fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box>; + fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box>; {{/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::::new(self, context) +impl<'a, T: Api + Sized, C> ContextWrapperExt<'a, C> for T { + fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> { + ContextWrapper::::new(self, context) } } -impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { +impl<'a, T: Api, 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{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}) -> Box> { diff --git a/modules/openapi-generator/src/main/resources/rust-server/server-auth.mustache b/modules/openapi-generator/src/main/resources/rust-server/server-auth.mustache index 5f2884cf8aad..24e08ff44d98 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/server-auth.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/server-auth.mustache @@ -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 where T: hyper::server::NewService), Response=Response, Error=Error> { +pub struct NewService + where + C: Default + Push, + C::Result: Push>, + T: hyper::server::NewService>>::Result), Response = Response, Error = Error>, +{ inner: T, + marker: PhantomData, } -impl NewService where T: hyper::server::NewService), Response=Response, Error=Error> + 'static { - pub fn new(inner: T) -> NewService { - NewService{inner} +impl NewService + where + C: Default + Push, + C::Result: Push>, + T: hyper::server::NewService>>::Result), Response = Response, Error = Error> + 'static, +{ + pub fn new(inner: T) -> NewService { + NewService { + inner, + marker: PhantomData, + } } } -impl hyper::server::NewService for NewService where T: hyper::server::NewService), Response=Response, Error=Error> + 'static { +impl hyper::server::NewService for NewService + where + C: Default + Push, + C::Result: Push>, + T: hyper::server::NewService>>::Result), Response = Response, Error = Error> + 'static, +{ type Request = Request; type Response = Response; type Error = Error; - type Instance = Service; + type Instance = Service; fn new_service(&self) -> Result { self.inner.new_service().map(|s| Service::new(s)) @@ -27,23 +49,44 @@ impl hyper::server::NewService for NewService where T: hyper::server::NewS } /// Middleware to extract authentication data from request -pub struct Service where T: hyper::server::Service), Response=Response, Error=Error> { +pub struct Service + where + C: Default + Push, + C::Result: Push>, + T: hyper::server::Service>>::Result), Response = Response, Error = Error>, +{ inner: T, + marker: PhantomData, } -impl Service where T: hyper::server::Service), Response=Response, Error=Error> { - pub fn new(inner: T) -> Service { - Service{inner} +impl Service + where + C: Default + Push, + C::Result: Push>, + T: hyper::server::Service>>::Result), Response = Response, Error = Error>, +{ + pub fn new(inner: T) -> Service { + Service { + inner, + marker: PhantomData, + } } } -impl hyper::server::Service for Service where T: hyper::server::Service), Response=Response, Error=Error> { +impl hyper::server::Service for Service + where + C: Default + Push, + C::Result: Push>, + T: hyper::server::Service>>::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 hyper::server::Service for Service where T: hyper::server::Service>().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 hyper::server::Service for Service where T: hyper::server::Service>().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 hyper::server::Service for Service where T: hyper::server::Service [String] } if let Some(header) = req.headers().get::().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 hyper::server::Service for Service where T: hyper::server::Service { +pub struct NewService { api_impl: Arc, + marker: PhantomData, } -impl NewService where T: Api + Clone + 'static { - pub fn new>>(api_impl: U) -> NewService { - NewService{api_impl: api_impl.into()} +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 { - type Request = (Request, Context); +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; + type Instance = Service; fn new_service(&self) -> Result { Ok(Service::new(self.api_impl.clone())) } } -pub struct Service { +pub struct Service { api_impl: Arc, + marker: PhantomData, } -impl Service where T: Api + Clone + 'static { - pub fn new>>(api_impl: U) -> Service { - Service{api_impl: api_impl.into()} +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 { - type Request = (Request, Context); +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>; @@ -113,14 +131,11 @@ impl hyper::server::Service for Service 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::().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>).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 hyper::server::Service for Service 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 hyper::server::Service for Service 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).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 hyper::server::Service for Service where T: Api + Clone + 'static { )) {{^bodyParams}}{{#vendorExtensions}}{{^hasFile}} }} - })) as Box> + }) as Box> {{/hasFile}}{{#hasFile}} as Box> }, diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java new file mode 100644 index 000000000000..ef049d522b43 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java @@ -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"); + 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"); + 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