diff --git a/docs/generators/java-microprofile.md b/docs/generators/java-microprofile.md index 11a92e692023..517f907c5fd9 100644 --- a/docs/generators/java-microprofile.md +++ b/docs/generators/java-microprofile.md @@ -62,7 +62,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |implicitHeadersRegex|Skip header parameters that matches given regex in the generated API methods using @ApiImplicitParams annotation. Note: this parameter is ignored when implicitHeaders=true| |null| |invokerPackage|root package for generated code| |org.openapitools.client| |legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|
**true**
The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
**false**
The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.
|true| -|library|library template (sub-template) to use|
**jersey2**
HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.17.1
**jersey3**
HTTP client: Jersey client 3.1.1. JSON processing: Jackson 2.17.1
**feign**
HTTP client: OpenFeign 13.2.1. JSON processing: Jackson 2.17.1 or Gson 2.10.1
**okhttp-gson**
[DEFAULT] HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.
**retrofit2**
HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1 (Retrofit 2.5.0) or Jackson 2.17.1. Enable the RxJava adapter using '-DuseRxJava[2/3]=true'. (RxJava 1.x or 2.x or 3.x)
**resttemplate**
HTTP client: Spring RestTemplate 5.3.33 (6.1.5 if `useJakartaEe=true`). JSON processing: Jackson 2.17.1
**webclient**
HTTP client: Spring WebClient 5.1.18. JSON processing: Jackson 2.17.1
**restclient**
HTTP client: Spring RestClient 6.1.6. JSON processing: Jackson 2.17.1
**resteasy**
HTTP client: Resteasy client 4.7.6. JSON processing: Jackson 2.17.1
**vertx**
HTTP client: VertX client 3.5.2. JSON processing: Jackson 2.17.1
**google-api-client**
HTTP client: Google API client 2.2.0. JSON processing: Jackson 2.17.1
**rest-assured**
HTTP client: rest-assured 5.3.2. JSON processing: Gson 2.10.1 or Jackson 2.17.1. Only for Java 8
**native**
HTTP client: Java native HttpClient. JSON processing: Jackson 2.17.1. Only for Java11+
**microprofile**
HTTP client: Microprofile client 2.0 (default, set desired version via `microprofileRestClientVersion=x.x.x`). JSON processing: JSON-B 1.0.2 or Jackson 2.17.1
**apache-httpclient**
HTTP client: Apache httpclient 5.2.1. JSON processing: Jackson 2.17.1
|okhttp-gson| +|library|library template (sub-template) to use|
**jersey2**
HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.17.1
**jersey3**
HTTP client: Jersey client 3.1.1. JSON processing: Jackson 2.17.1
**feign**
HTTP client: OpenFeign 13.2.1. JSON processing: Jackson 2.17.1 or Gson 2.10.1
**okhttp-gson**
[DEFAULT] HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.
**retrofit2**
HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1 (Retrofit 2.5.0) or Jackson 2.17.1. Enable the RxJava adapter using '-DuseRxJava[2/3]=true'. (RxJava 1.x or 2.x or 3.x)
**resttemplate**
HTTP client: Spring RestTemplate 5.3.33 (6.1.5 if `useJakartaEe=true`). JSON processing: Jackson 2.17.1
**webclient**
HTTP client: Spring WebClient 5.1.18. JSON processing: Jackson 2.17.1
**restclient**
HTTP client: Spring RestClient 6.1.6. JSON processing: Jackson 2.17.1
**resteasy**
HTTP client: Resteasy client 4.7.6. JSON processing: Jackson 2.17.1
**vertx**
HTTP client: VertX client 3.5.2. JSON processing: Jackson 2.17.1
**google-api-client**
HTTP client: Google API client 2.2.0. JSON processing: Jackson 2.17.1
**rest-assured**
HTTP client: rest-assured 5.3.2. JSON processing: Gson 2.10.1 or Jackson 2.17.1. Only for Java 8
**native**
HTTP client: Java native HttpClient. JSON processing: Jackson 2.17.1. Only for Java11+
**microprofile**
HTTP client: Microprofile client 2.0 (default, set desired version via `microprofileRestClientVersion=x.x.x`). JSON processing: JSON-B 1.0.2 or Jackson 2.17.1
**apache-httpclient**
HTTP client: Apache httpclient 5.2.1. JSON processing: Jackson 2.17.1
**apache-asynchttpclient**
HTTP client: Apache async httpclient 5.2.1. JSON processing: Jackson 2.17.1
|okhttp-gson| |licenseName|The name of the license| |Unlicense| |licenseUrl|The URL of the license| |http://unlicense.org| |microprofileFramework|Framework for microprofile. Possible values "kumuluzee"| |null| @@ -86,7 +86,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true| |sourceFolder|source folder for generated code| |src/main/java| |supportStreaming|Support streaming endpoint (beta)| |false| -|supportUrlQuery|Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries.| |false| +|supportUrlQuery|Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` and `apache-asynchttpclient libraries.| |false| |testOutput|Set output folder for models and APIs tests| |${project.build.directory}/generated-test-sources/openapi| |useAbstractionForFiles|Use alternative types instead of java.io.File to allow passing bytes without a file on disk. Available on resttemplate, webclient, restclient, libraries| |false| |useBeanValidation|Use BeanValidation API annotations| |false| diff --git a/docs/generators/java.md b/docs/generators/java.md index 184a6d1ca5f8..f5c1d5f6b1d3 100644 --- a/docs/generators/java.md +++ b/docs/generators/java.md @@ -62,7 +62,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |implicitHeadersRegex|Skip header parameters that matches given regex in the generated API methods using @ApiImplicitParams annotation. Note: this parameter is ignored when implicitHeaders=true| |null| |invokerPackage|root package for generated code| |org.openapitools.client| |legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|
**true**
The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
**false**
The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.
|true| -|library|library template (sub-template) to use|
**jersey2**
HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.17.1
**jersey3**
HTTP client: Jersey client 3.1.1. JSON processing: Jackson 2.17.1
**feign**
HTTP client: OpenFeign 13.2.1. JSON processing: Jackson 2.17.1 or Gson 2.10.1
**okhttp-gson**
[DEFAULT] HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.
**retrofit2**
HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1 (Retrofit 2.5.0) or Jackson 2.17.1. Enable the RxJava adapter using '-DuseRxJava[2/3]=true'. (RxJava 1.x or 2.x or 3.x)
**resttemplate**
HTTP client: Spring RestTemplate 5.3.33 (6.1.5 if `useJakartaEe=true`). JSON processing: Jackson 2.17.1
**webclient**
HTTP client: Spring WebClient 5.1.18. JSON processing: Jackson 2.17.1
**restclient**
HTTP client: Spring RestClient 6.1.6. JSON processing: Jackson 2.17.1
**resteasy**
HTTP client: Resteasy client 4.7.6. JSON processing: Jackson 2.17.1
**vertx**
HTTP client: VertX client 3.5.2. JSON processing: Jackson 2.17.1
**google-api-client**
HTTP client: Google API client 2.2.0. JSON processing: Jackson 2.17.1
**rest-assured**
HTTP client: rest-assured 5.3.2. JSON processing: Gson 2.10.1 or Jackson 2.17.1. Only for Java 8
**native**
HTTP client: Java native HttpClient. JSON processing: Jackson 2.17.1. Only for Java11+
**microprofile**
HTTP client: Microprofile client 2.0 (default, set desired version via `microprofileRestClientVersion=x.x.x`). JSON processing: JSON-B 1.0.2 or Jackson 2.17.1
**apache-httpclient**
HTTP client: Apache httpclient 5.2.1. JSON processing: Jackson 2.17.1
|okhttp-gson| +|library|library template (sub-template) to use|
**jersey2**
HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.17.1
**jersey3**
HTTP client: Jersey client 3.1.1. JSON processing: Jackson 2.17.1
**feign**
HTTP client: OpenFeign 13.2.1. JSON processing: Jackson 2.17.1 or Gson 2.10.1
**okhttp-gson**
[DEFAULT] HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.
**retrofit2**
HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1 (Retrofit 2.5.0) or Jackson 2.17.1. Enable the RxJava adapter using '-DuseRxJava[2/3]=true'. (RxJava 1.x or 2.x or 3.x)
**resttemplate**
HTTP client: Spring RestTemplate 5.3.33 (6.1.5 if `useJakartaEe=true`). JSON processing: Jackson 2.17.1
**webclient**
HTTP client: Spring WebClient 5.1.18. JSON processing: Jackson 2.17.1
**restclient**
HTTP client: Spring RestClient 6.1.6. JSON processing: Jackson 2.17.1
**resteasy**
HTTP client: Resteasy client 4.7.6. JSON processing: Jackson 2.17.1
**vertx**
HTTP client: VertX client 3.5.2. JSON processing: Jackson 2.17.1
**google-api-client**
HTTP client: Google API client 2.2.0. JSON processing: Jackson 2.17.1
**rest-assured**
HTTP client: rest-assured 5.3.2. JSON processing: Gson 2.10.1 or Jackson 2.17.1. Only for Java 8
**native**
HTTP client: Java native HttpClient. JSON processing: Jackson 2.17.1. Only for Java11+
**microprofile**
HTTP client: Microprofile client 2.0 (default, set desired version via `microprofileRestClientVersion=x.x.x`). JSON processing: JSON-B 1.0.2 or Jackson 2.17.1
**apache-httpclient**
HTTP client: Apache httpclient 5.2.1. JSON processing: Jackson 2.17.1
**apache-asynchttpclient**
HTTP client: Apache async httpclient 5.2.1. JSON processing: Jackson 2.17.1
|okhttp-gson| |licenseName|The name of the license| |Unlicense| |licenseUrl|The URL of the license| |http://unlicense.org| |microprofileFramework|Framework for microprofile. Possible values "kumuluzee"| |null| @@ -86,7 +86,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true| |sourceFolder|source folder for generated code| |src/main/java| |supportStreaming|Support streaming endpoint (beta)| |false| -|supportUrlQuery|Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries.| |false| +|supportUrlQuery|Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` and `apache-asynchttpclient libraries.| |false| |testOutput|Set output folder for models and APIs tests| |${project.build.directory}/generated-test-sources/openapi| |useAbstractionForFiles|Use alternative types instead of java.io.File to allow passing bytes without a file on disk. Available on resttemplate, webclient, restclient, libraries| |false| |useBeanValidation|Use BeanValidation API annotations| |false| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java index 45b62581f15f..3c05be45e526 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java @@ -93,6 +93,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen public static final String VERTX = "vertx"; public static final String MICROPROFILE = "microprofile"; public static final String APACHE = "apache-httpclient"; + public static final String APACHE_ASYNC = "apache-asynchttpclient"; public static final String MICROPROFILE_REST_CLIENT_VERSION = "microprofileRestClientVersion"; public static final String MICROPROFILE_REST_CLIENT_DEFAULT_VERSION = "2.0"; public static final String MICROPROFILE_REST_CLIENT_DEFAULT_ROOT_PACKAGE = "javax"; @@ -240,7 +241,7 @@ public JavaClientCodegen() { cliOptions.add(CliOption.newBoolean(CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, "Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter. ONLY jersey2, jersey3, okhttp-gson, microprofile, Spring RestClient support this option.")); cliOptions.add(CliOption.newBoolean(WEBCLIENT_BLOCKING_OPERATIONS, "Making all WebClient operations blocking(sync). Note that if on operation 'x-webclient-blocking: false' then such operation won't be sync", this.webclientBlockingOperations)); cliOptions.add(CliOption.newBoolean(GENERATE_CLIENT_AS_BEAN, "For resttemplate, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).", this.generateClientAsBean)); - cliOptions.add(CliOption.newBoolean(SUPPORT_URL_QUERY, "Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries.")); + cliOptions.add(CliOption.newBoolean(SUPPORT_URL_QUERY, "Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` and `apache-asynchttpclient libraries.")); cliOptions.add(CliOption.newBoolean(USE_ENUM_CASE_INSENSITIVE, "Use `equalsIgnoreCase` when String for enum comparison", useEnumCaseInsensitive)); cliOptions.add(CliOption.newBoolean(FAIL_ON_UNKNOWN_PROPERTIES, "Fail Jackson de-serialization on unknown properties", this.failOnUnknownProperties)); @@ -259,6 +260,7 @@ public JavaClientCodegen() { supportedLibraries.put(NATIVE, "HTTP client: Java native HttpClient. JSON processing: Jackson 2.17.1. Only for Java11+"); supportedLibraries.put(MICROPROFILE, "HTTP client: Microprofile client " + MICROPROFILE_REST_CLIENT_DEFAULT_VERSION + " (default, set desired version via `" + MICROPROFILE_REST_CLIENT_VERSION + "=x.x.x`). JSON processing: JSON-B 1.0.2 or Jackson 2.17.1"); supportedLibraries.put(APACHE, "HTTP client: Apache httpclient 5.2.1. JSON processing: Jackson 2.17.1"); + supportedLibraries.put(APACHE_ASYNC, "HTTP client: Apache async httpclient 5.2.1. JSON processing: Jackson 2.17.1"); CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use"); libraryOption.setEnum(supportedLibraries); @@ -395,9 +397,9 @@ public void processOpts() { convertPropertyToBooleanAndWriteBack(WEBCLIENT_BLOCKING_OPERATIONS, op -> webclientBlockingOperations=op); convertPropertyToBooleanAndWriteBack(FAIL_ON_UNKNOWN_PROPERTIES, this::setFailOnUnknownProperties); - // add URL query deepObject support to native, apache-httpclient by default + // add URL query deepObject support to native, apache-httpclient, apache-asynchttpclient by default if (!additionalProperties.containsKey(SUPPORT_URL_QUERY)) { - if (isLibrary(NATIVE) || isLibrary(APACHE)) { + if (isLibrary(NATIVE) || isLibrary(APACHE) || isLibrary(APACHE_ASYNC)) { // default to true for native and apache-httpclient additionalProperties.put(SUPPORT_URL_QUERY, true); } @@ -436,7 +438,7 @@ public void processOpts() { } // helper for client library that allow to parse/format java.time.OffsetDateTime or org.threeten.bp.OffsetDateTime - if (additionalProperties.containsKey("jsr310") && (isLibrary(WEBCLIENT) || isLibrary(VERTX) || isLibrary(RESTTEMPLATE) || isLibrary(RESTEASY) || isLibrary(MICROPROFILE) || isLibrary(JERSEY2) || isLibrary(JERSEY3) || isLibrary(APACHE) || isLibrary(RESTCLIENT))) { + if (additionalProperties.containsKey("jsr310") && (isLibrary(WEBCLIENT) || isLibrary(VERTX) || isLibrary(RESTTEMPLATE) || isLibrary(RESTEASY) || isLibrary(MICROPROFILE) || isLibrary(JERSEY2) || isLibrary(JERSEY3) || isLibrary(APACHE) || isLibrary(APACHE_ASYNC) || isLibrary(RESTCLIENT))) { supportingFiles.add(new SupportingFile("JavaTimeFormatter.mustache", invokerFolder, "JavaTimeFormatter.java")); } @@ -492,7 +494,7 @@ public void processOpts() { supportingFiles.add(new SupportingFile("auth/Authentication.mustache", authFolder, "Authentication.java")); } - if (APACHE.equals(getLibrary()) || RESTTEMPLATE.equals(getLibrary())) { + if (APACHE.equals(getLibrary()) || APACHE_ASYNC.equals(getLibrary()) || RESTTEMPLATE.equals(getLibrary())) { supportingFiles.add(new SupportingFile("BaseApi.mustache", invokerFolder, "BaseApi.java")); } @@ -644,7 +646,7 @@ public void processOpts() { additionalProperties.put("jsonbPolymorphism", true); } } - } else if (APACHE.equals(getLibrary())) { + } else if (APACHE.equals(getLibrary()) || (APACHE_ASYNC.equals(getLibrary()))) { forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON); } else { LOGGER.error("Unknown library option (-l/--library): {}", getLibrary()); @@ -814,7 +816,7 @@ public int compare(CodegenParameter one, CodegenParameter another) { } } - if (NATIVE.equals(getLibrary()) || APACHE.equals(getLibrary())) { + if (NATIVE.equals(getLibrary()) || APACHE.equals(getLibrary()) || APACHE_ASYNC.equals(getLibrary())) { OperationMap operations = objs.getOperations(); List operationList = operations.getOperation(); Pattern methodPattern = Pattern.compile("^(.*):([^:]*)$"); diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/ApiClient.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/ApiClient.mustache new file mode 100644 index 000000000000..eed11a7b9490 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/ApiClient.mustache @@ -0,0 +1,1135 @@ +{{>licenseInfo}} +package {{invokerPackage}}; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.databind.*; +{{#joda}} +import com.fasterxml.jackson.datatype.joda.JodaModule; +{{/joda}} +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.time.OffsetDateTime; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +{{#openApiNullable}} +import org.openapitools.jackson.nullable.JsonNullableModule; +{{/openApiNullable}} + +import org.apache.hc.core5.concurrent.FutureCallback; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.client5.http.impl.async.HttpAsyncClients; +import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; +import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; +import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; + +import org.apache.hc.client5.http.cookie.BasicCookieStore; +import org.apache.hc.client5.http.cookie.Cookie; +import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; +import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.cookie.BasicClientCookie; +import org.apache.hc.client5.http.protocol.HttpClientContext; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.NameValuePair; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.ByteArrayEntity; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.entity.FileEntity; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.core5.http.message.BasicNameValuePair; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Date; +import java.util.function.Supplier; +import java.util.TimeZone; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Future; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import java.net.URLEncoder; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.nio.file.Paths; +import java.lang.reflect.Type; +import java.net.URI; + +import java.text.DateFormat; + +import {{invokerPackage}}.auth.Authentication; +{{#hasHttpBasicMethods}} +import {{invokerPackage}}.auth.HttpBasicAuth; +{{/hasHttpBasicMethods}} +{{#hasHttpBearerMethods}} +import {{invokerPackage}}.auth.HttpBearerAuth; +{{/hasHttpBearerMethods}} +{{#hasApiKeyMethods}} +import {{invokerPackage}}.auth.ApiKeyAuth; +{{/hasApiKeyMethods}} +{{#hasOAuthMethods}} +import {{invokerPackage}}.auth.OAuth; +{{/hasOAuthMethods}} + +{{>generatedAnnotation}} +public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} { + private Map defaultHeaderMap = new HashMap(); + private Map defaultCookieMap = new HashMap(); + private String basePath = "{{{basePath}}}"; + protected List servers = new ArrayList({{#servers}}{{#-first}}Arrays.asList( +{{/-first}} new ServerConfiguration( + "{{{url}}}", + "{{{description}}}{{^description}}No description provided{{/description}}", + new HashMap(){{#variables}}{{#-first}} {{ +{{/-first}} put("{{{name}}}", new ServerVariable( + "{{{description}}}{{^description}}No description provided{{/description}}", + "{{{defaultValue}}}", + new HashSet( + {{#enumValues}} + {{#-first}} + Arrays.asList( + {{/-first}} + "{{{.}}}"{{^-last}},{{/-last}} + {{#-last}} + ) + {{/-last}} + {{/enumValues}} + ) + )); + {{#-last}} + }}{{/-last}}{{/variables}} + ){{^-last}},{{/-last}} + {{#-last}} + ){{/-last}}{{/servers}}); + protected Integer serverIndex = 0; + protected Map serverVariables = null; + private boolean debugging = false; + private int connectionTimeout = 0; + + private CloseableHttpAsyncClient httpClient; + private ObjectMapper objectMapper; + protected String tempFolderPath = null; + + private Map authentications; + + private Map lastStatusCodeByThread = new ConcurrentHashMap<>(); + private Map>> lastResponseHeadersByThread = new ConcurrentHashMap<>(); + + private DateFormat dateFormat; + + // Methods that can have a request body + private static List bodyMethods = Arrays.asList("POST", "PUT", "DELETE", "PATCH"); + + public ApiClient(CloseableHttpAsyncClient httpClient) { + objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, {{failOnUnknownProperties}}); + objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + objectMapper.registerModule(new Jdk8Module()); + {{#joda}} + objectMapper.registerModule(new JodaModule()); + {{/joda}} + objectMapper.registerModule(new JavaTimeModule()); + {{#openApiNullable}} + objectMapper.registerModule(new JsonNullableModule()); + {{/openApiNullable}} + objectMapper.setDateFormat(ApiClient.buildDefaultDateFormat()); + + dateFormat = ApiClient.buildDefaultDateFormat(); + + // Set default User-Agent. + setUserAgent("{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{artifactVersion}}}/java{{/httpUserAgent}}"); + + // Setup authentications (key: authentication name, value: authentication). + authentications = new HashMap();{{#authMethods}}{{#isBasic}}{{#isBasicBasic}} + authentications.put("{{name}}", new HttpBasicAuth());{{/isBasicBasic}}{{#isBasicBearer}} + authentications.put("{{name}}", new HttpBearerAuth("{{scheme}}"));{{/isBasicBearer}}{{/isBasic}}{{#isApiKey}} + authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}} + authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}} + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + + this.httpClient = httpClient; + } + + public ApiClient() { + this(HttpAsyncClients.createDefault()); + } + + public static DateFormat buildDefaultDateFormat() { + return new RFC3339DateFormat(); + } + + /** + * Returns the current object mapper used for JSON serialization/deserialization. + *

+ * Note: If you make changes to the object mapper, remember to set it back via + * setObjectMapper in order to trigger HTTP client rebuilding. + *

+ * @return Object mapper + */ + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + /** + * Sets the object mapper. + * + * @param objectMapper object mapper + * @return API client + */ + public ApiClient setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + return this; + } + + public CloseableHttpAsyncClient getHttpClient() { + return httpClient; + } + + /** + * Sets the HTTP client. + * + * @param httpClient HTTP client + * @return API client + */ + public ApiClient setHttpClient(CloseableHttpAsyncClient httpClient) { + this.httpClient = httpClient; + return this; + } + + public String getBasePath() { + return basePath; + } + + /** + * Sets the base path. + * + * @param basePath base path + * @return API client + */ + public ApiClient setBasePath(String basePath) { + this.basePath = basePath; + this.serverIndex = null; + return this; + } + + public List getServers() { + return servers; + } + + /** + * Sets the server. + * + * @param servers a list of server configuration + * @return API client + */ + public ApiClient setServers(List servers) { + this.servers = servers; + return this; + } + + public Integer getServerIndex() { + return serverIndex; + } + + /** + * Sets the server index. + * + * @param serverIndex server index + * @return API client + */ + public ApiClient setServerIndex(Integer serverIndex) { + this.serverIndex = serverIndex; + return this; + } + + public Map getServerVariables() { + return serverVariables; + } + + /** + * Sets the server variables. + * + * @param serverVariables server variables + * @return API client + */ + public ApiClient setServerVariables(Map serverVariables) { + this.serverVariables = serverVariables; + return this; + } + + /** + * Gets the status code of the previous request + * + * @return Status code + */ + @Deprecated + public int getStatusCode() { + return lastStatusCodeByThread.get(Thread.currentThread().getId()); + } + + /** + * Gets the response headers of the previous request + * @return Response headers + */ + @Deprecated + public Map> getResponseHeaders() { + return lastResponseHeadersByThread.get(Thread.currentThread().getId()); + } + + /** + * Get authentications (key: authentication name, value: authentication). + * @return Map of authentication + */ + public Map getAuthentications() { + return authentications; + } + + /** + * Get authentication for the given name. + * + * @param authName The authentication name + * @return The authentication, null if not found + */ + public Authentication getAuthentication(String authName) { + return authentications.get(authName); + } + + /** + * The path of temporary folder used to store downloaded files from endpoints + * with file response. The default value is null, i.e. using + * the system's default temporary folder. + * + * @return Temp folder path + */ + public String getTempFolderPath() { + return tempFolderPath; + } + + {{#hasHttpBearerMethods}} + /** + * Helper method to set access token for the first Bearer authentication. + * @param bearerToken Bearer token + * @return API client + */ + public ApiClient setBearerToken(String bearerToken) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBearerAuth) { + ((HttpBearerAuth) auth).setBearerToken(bearerToken); + return this; + } + } + throw new RuntimeException("No Bearer authentication configured!"); + } + + /** + * Helper method to set the supplier of access tokens for Bearer authentication. + * + * @param tokenSupplier the token supplier function + */ + public void setBearerToken(Supplier tokenSupplier) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBearerAuth) { + ((HttpBearerAuth) auth).setBearerToken(tokenSupplier); + return; + } + } + throw new RuntimeException("No Bearer authentication configured!"); + } + {{/hasHttpBearerMethods}} + + {{#hasHttpBasicMethods}} + /** + * Helper method to set username for the first HTTP basic authentication. + * @param username Username + * @return API client + */ + public ApiClient setUsername(String username) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setUsername(username); + return this; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set password for the first HTTP basic authentication. + * @param password Password + * @return API client + */ + public ApiClient setPassword(String password) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setPassword(password); + return this; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + {{/hasHttpBasicMethods}} + + {{#hasApiKeyMethods}} + /** + * Helper method to set API key value for the first API key authentication. + * @param apiKey the API key + * @return API client + */ + public ApiClient setApiKey(String apiKey) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKey(apiKey); + return this; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set API key prefix for the first API key authentication. + * @param apiKeyPrefix API key prefix + * @return API client + */ + public ApiClient setApiKeyPrefix(String apiKeyPrefix) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); + return this; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + {{/hasApiKeyMethods}} + + {{#hasOAuthMethods}} + /** + * Helper method to set access token for the first OAuth2 authentication. + * @param accessToken Access token + * @return API client + */ + public ApiClient setAccessToken(String accessToken) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setAccessToken(accessToken); + return this; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + {{/hasOAuthMethods}} + + /** + * Set the User-Agent header's value (by adding to the default header map). + * @param userAgent User agent + * @return API client + */ + public ApiClient setUserAgent(String userAgent) { + addDefaultHeader("User-Agent", userAgent); + return this; + } + + /** + * Set temp folder path + * @param tempFolderPath Temp folder path + * @return API client + */ + public ApiClient setTempFolderPath(String tempFolderPath) { + this.tempFolderPath = tempFolderPath; + return this; + } + + /** + * Add a default header. + * + * @param key The header's key + * @param value The header's value + * @return API client + */ + public ApiClient addDefaultHeader(String key, String value) { + defaultHeaderMap.put(key, value); + return this; + } + + /** + * Add a default cookie. + * + * @param key The cookie's key + * @param value The cookie's value + * @return API client + */ + public ApiClient addDefaultCookie(String key, String value) { + defaultCookieMap.put(key, value); + return this; + } + + /** + * Check that whether debugging is enabled for this API client. + * @return True if debugging is on + */ + public boolean isDebugging() { + return debugging; + } + + /** + * Enable/disable debugging for this API client. + * + * @param debugging To enable (true) or disable (false) debugging + * @return API client + */ + public ApiClient setDebugging(boolean debugging) { + // TODO: implement debugging mode + this.debugging = debugging; + return this; + } + + /** + * Connect timeout (in milliseconds). + * @return Connection timeout + */ + public int getConnectTimeout() { + return connectionTimeout; + } + + /** + * Set the connect timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link Integer#MAX_VALUE}. + * @param connectionTimeout Connection timeout in milliseconds + * @return API client + */ + public ApiClient setConnectTimeout(int connectionTimeout) { + this.connectionTimeout = connectionTimeout; + return this; + } + + /** + * Get the date format used to parse/format date parameters. + * @return Date format + */ + public DateFormat getDateFormat() { + return dateFormat; + } + + /** + * Set the date format used to parse/format date parameters. + * @param dateFormat Date format + * @return API client + */ + public ApiClient setDateFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + // Also set the date format for model (de)serialization with Date properties. + this.objectMapper.setDateFormat((DateFormat) dateFormat.clone()); + return this; + } + + /** + * Parse the given string into Date object. + * @param str String + * @return Date + */ + public Date parseDate(String str) { + try { + return dateFormat.parse(str); + } catch (java.text.ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * Format the given Date object into string. + * @param date Date + * @return Date in string format + */ + public String formatDate(Date date) { + return dateFormat.format(date); + } + + /** + * Format the given parameter object into string. + * @param param Object + * @return Object in string format + */ + public String parameterToString(Object param) { + if (param == null) { + return ""; + } else if (param instanceof Date) { + return formatDate((Date) param); + } {{#jsr310}}else if (param instanceof OffsetDateTime) { + return formatOffsetDateTime((OffsetDateTime) param); + } {{/jsr310}}else if (param instanceof Collection) { + StringBuilder b = new StringBuilder(); + for(Object o : (Collection)param) { + if(b.length() > 0) { + b.append(','); + } + b.append(String.valueOf(o)); + } + return b.toString(); + } else { + return String.valueOf(param); + } + } + + /** + * Formats the specified query parameter to a list containing a single {@code Pair} object. + * + * Note that {@code value} must not be a collection. + * + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list containing a single {@code Pair} object. + */ + public List parameterToPair(String name, Object value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value instanceof Collection) { + return params; + } + + params.add(new Pair(name, escapeString(parameterToString(value)))); + return params; + } + + /** + * Formats the specified collection query parameters to a list of {@code Pair} objects. + * + * Note that the values of each of the returned Pair objects are percent-encoded. + * + * @param collectionFormat The collection format of the parameter. + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list of {@code Pair} objects. + */ + public List parameterToPairs(String collectionFormat, String name, Collection value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value.isEmpty()) { + return params; + } + + // create the params based on the collection format + if ("multi".equals(collectionFormat)) { + for (Object item : value) { + params.add(new Pair(name, escapeString(parameterToString(item)))); + } + return params; + } + + // collectionFormat is assumed to be "csv" by default + String delimiter = ","; + + // escape all delimiters except commas, which are URI reserved + // characters + if ("ssv".equals(collectionFormat)) { + delimiter = escapeString(" "); + } else if ("tsv".equals(collectionFormat)) { + delimiter = escapeString("\t"); + } else if ("pipes".equals(collectionFormat)) { + delimiter = escapeString("|"); + } + + StringBuilder sb = new StringBuilder() ; + for (Object item : value) { + sb.append(delimiter); + sb.append(escapeString(parameterToString(item))); + } + + params.add(new Pair(name, sb.substring(delimiter.length()))); + + return params; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime MIME + * @return True if MIME type is boolean + */ + public boolean isJsonMime(String mime) { + String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); + } + + /** + * Select the Accept header's value from the given accepts array: + * if JSON exists in the given array, use it; + * otherwise use all of them (joining into a string) + * + * @param accepts The accepts array to select from + * @return The Accept header to use. If the given array is empty, + * null will be returned (not to set the Accept header explicitly). + */ + public String selectHeaderAccept(String[] accepts) { + if (accepts.length == 0) { + return null; + } + for (String accept : accepts) { + if (isJsonMime(accept)) { + return accept; + } + } + return StringUtil.join(accepts, ","); + } + + /** + * Select the Content-Type header's value from the given array: + * if JSON exists in the given array, use it; + * otherwise use the first one of the array. + * + * @param contentTypes The Content-Type array to select from + * @return The Content-Type header to use. If the given array is empty, + * or matches "any", JSON will be used. + */ + public String selectHeaderContentType(String[] contentTypes) { + if (contentTypes.length == 0 || contentTypes[0].equals("*/*")) { + return "application/json"; + } + for (String contentType : contentTypes) { + if (isJsonMime(contentType)) { + return contentType; + } + } + return contentTypes[0]; + } + + /** + * Escape the given string to be used as URL query value. + * @param str String + * @return Escaped string + */ + public String escapeString(String str) { + try { + return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); + } catch (UnsupportedEncodingException e) { + return str; + } + } + + /** + * Transforms response headers into map. + * + * @param headers HTTP headers + * @return a map of string array + */ + protected Map> transformResponseHeaders(Header[] headers) { + Map> headersMap = new HashMap<>(); + for (Header header : headers) { + List valuesList = headersMap.get(header.getName()); + if (valuesList != null) { + valuesList.add(header.getValue()); + } else { + valuesList = new ArrayList<>(); + valuesList.add(header.getValue()); + headersMap.put(header.getName(), valuesList); + } + } + return headersMap; + } + + /** + * Parse content type object from header value + */ + private ContentType getContentType(String headerValue) throws ApiException { + try { + return ContentType.parse(headerValue); + } catch (UnsupportedCharsetException e) { + throw new ApiException("Could not parse content type " + headerValue); + } + } + + /** + * Get content type of a response or null if one was not provided + */ + private String getResponseMimeType(HttpResponse response) throws ApiException { + Header contentTypeHeader = response.getFirstHeader("Content-Type"); + if (contentTypeHeader != null) { + return getContentType(contentTypeHeader.getValue()).getMimeType(); + } + return null; + } + + /** + * Serialize the given Java object into string according the given + * Content-Type (only JSON is supported for now). + * @param obj Object + * @param contentType Content type + * @param formParams Form parameters + * @return Object + * @throws ApiException API exception + */ + public String serialize(Object obj, Map formParams, ContentType contentType) throws ApiException { + String mimeType = contentType.getMimeType(); + if (isJsonMime(mimeType)) { + try { + return objectMapper.writeValueAsString(obj); + } catch (JsonProcessingException e) { + throw new ApiException(e); + } + } else { + throw new ApiException("Serialization for content type '" + contentType + "' not supported"); + } + } + + /** + * Deserialize response body to Java object according to the Content-Type. + * + * @param Type + * @param response Response + * @param valueType Return type + * @return Deserialized object + * @throws ApiException API exception + * @throws IOException IO exception + */ + @SuppressWarnings("unchecked") + public T deserialize(SimpleHttpResponse response, TypeReference valueType) throws ApiException, IOException, ParseException { + if (valueType == null) { + return null; + } + Type valueRawType = valueType.getType(); + if (valueRawType.equals(byte[].class)) { + return (T) response.getBodyBytes(); + } else if (valueRawType.equals(File.class)) { + return (T) downloadFileFromResponse(response); + } + String mimeType = getResponseMimeType(response); + if (mimeType == null || isJsonMime(mimeType)) { + // Assume json if no mime type + // convert input stream to string + String content = response.getBodyText(); + + if ("".equals(content)) { // returns null for empty body + return null; + } + + return objectMapper.readValue(content, valueType); + } else if (mimeType.toLowerCase().startsWith("text/")) { + // convert input stream to string + return (T) response.getBodyText(); + } else { + Map> responseHeaders = transformResponseHeaders(response.getHeaders()); + throw new ApiException( + "Deserialization for content type '" + mimeType + "' not supported for type '" + valueType + "'", + response.getCode(), + responseHeaders, + response.getBodyText() + ); + } + } + + private File downloadFileFromResponse(SimpleHttpResponse response) throws IOException { + Header contentDispositionHeader = response.getFirstHeader("Content-Disposition"); + String contentDisposition = contentDispositionHeader == null ? null : contentDispositionHeader.getValue(); + File file = prepareDownloadFile(contentDisposition); + InputStream is = new ByteArrayInputStream(response.getBodyBytes()); + Files.copy(is, file.toPath(), StandardCopyOption.REPLACE_EXISTING); + return file; + } + + protected File prepareDownloadFile(String contentDisposition) throws IOException { + String filename = null; + if (contentDisposition != null && !"".equals(contentDisposition)) { + // Get filename from the Content-Disposition header. + Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?"); + Matcher matcher = pattern.matcher(contentDisposition); + if (matcher.find()) + filename = matcher.group(1); + } + + String prefix; + String suffix = null; + if (filename == null) { + prefix = "download-"; + suffix = ""; + } else { + int pos = filename.lastIndexOf('.'); + if (pos == -1) { + prefix = filename + "-"; + } else { + prefix = filename.substring(0, pos) + "-"; + suffix = filename.substring(pos); + } + // Files.createTempFile requires the prefix to be at least three characters long + if (prefix.length() < 3) + prefix = "download-"; + } + + if (tempFolderPath == null) + return Files.createTempFile(prefix, suffix).toFile(); + else + return Files.createTempFile(Paths.get(tempFolderPath), prefix, suffix).toFile(); + } + + /** + * Returns the URL of the client as defined by the server (if exists) or the base path. + * + * @return The URL for the client. + */ + public String getBaseURL() { + String baseURL; + if (serverIndex != null) { + if (serverIndex < 0 || serverIndex >= servers.size()) { + throw new ArrayIndexOutOfBoundsException(String.format( + "Invalid index %d when selecting the host settings. Must be less than %d", serverIndex, servers.size() + )); + } + baseURL = servers.get(serverIndex).URL(serverVariables); + } else { + baseURL = basePath; + } + return baseURL; + } + + /** + * Build full URL by concatenating base URL, the given sub path and query parameters. + * + * @param path The sub path + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param urlQueryDeepObject URL query string of the deep object parameters + * @return The full URL + */ + private String buildUrl(String path, List queryParams, List collectionQueryParams, String urlQueryDeepObject) { + String baseURL = getBaseURL(); + + final StringBuilder url = new StringBuilder(); + url.append(baseURL).append(path); + + if (queryParams != null && !queryParams.isEmpty()) { + // support (constant) query string in `path`, e.g. "/posts?draft=1" + String prefix = path.contains("?") ? "&" : "?"; + for (Pair param : queryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + // query parameter value already escaped as part of parameterToPair + url.append(escapeString(param.getName())).append("=").append(value); + } + } + } + + if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) { + String prefix = url.toString().contains("?") ? "&" : "?"; + for (Pair param : collectionQueryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + // collection query parameter value already escaped as part of parameterToPairs + url.append(escapeString(param.getName())).append("=").append(value); + } + } + } + + if (urlQueryDeepObject != null && urlQueryDeepObject.length() > 0) { + url.append(url.toString().contains("?") ? "&" : "?"); + url.append(urlQueryDeepObject); + } + + return url.toString(); + } + + protected boolean isSuccessfulStatus(int statusCode) { + return statusCode >= 200 && statusCode < 300; + } + + protected boolean isBodyAllowed(String method) { + return bodyMethods.contains(method); + } + + protected Cookie buildCookie(String key, String value, URI uri) { + BasicClientCookie cookie = new BasicClientCookie(key, value); + cookie.setDomain(uri.getHost()); + cookie.setPath("/"); + return cookie; + } + + protected T processResponse(SimpleHttpResponse response, TypeReference returnType) throws ApiException, IOException, ParseException { + int statusCode = response.getCode(); + lastStatusCodeByThread.put(Thread.currentThread().getId(), statusCode); + if (statusCode == HttpStatus.SC_NO_CONTENT) { + return null; + } + + Map> responseHeaders = transformResponseHeaders(response.getHeaders()); + lastResponseHeadersByThread.put(Thread.currentThread().getId(), responseHeaders); + + if (isSuccessfulStatus(statusCode)) { + return this.deserialize(response, returnType); + } else { + String message = response.getBody().getBodyText(); + throw new ApiException(message, statusCode, responseHeaders, message); + } + } + + /** + * Invoke API by sending HTTP request with the given options. + * + * @param Type + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "POST", "PUT", and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param urlQueryDeepObject A URL query string for deep object parameters + * @param body The request body object - if it is not binary, otherwise null + * @param headerParams The header parameters + * @param cookieParams The cookie parameters + * @param formParams The form parameters + * @param accept The request's Accept header + * @param contentType The request's Content-Type header + * @param authNames The authentications to apply + * @param returnType Return type + * @return The response body in type of string + * @throws ApiException API exception + */ + public CompletableFuture invokeAPI( + String path, + String method, + List queryParams, + List collectionQueryParams, + String urlQueryDeepObject, + Object body, + Map headerParams, + Map cookieParams, + Map formParams, + String accept, + String contentType, + String[] authNames, + TypeReference returnType + ) throws ApiException { + if (body != null && !formParams.isEmpty()) { + throw new ApiException("Cannot have body and form params"); + } + + updateParamsForAuth(authNames, queryParams, headerParams, cookieParams); + final String url = buildUrl(path, queryParams, collectionQueryParams, urlQueryDeepObject); + + SimpleRequestBuilder builder = SimpleRequestBuilder.create(method); + builder.setUri(url); + + if (accept != null) { + builder.addHeader("Accept", accept); + } + for (Entry keyValue : headerParams.entrySet()) { + builder.addHeader(keyValue.getKey(), keyValue.getValue()); + } + for (Map.Entry keyValue : defaultHeaderMap.entrySet()) { + if (!headerParams.containsKey(keyValue.getKey())) { + builder.addHeader(keyValue.getKey(), keyValue.getValue()); + } + } + + BasicCookieStore store = new BasicCookieStore(); + for (Entry keyValue : cookieParams.entrySet()) { + store.addCookie(buildCookie(keyValue.getKey(), keyValue.getValue(), builder.getUri())); + } + for (Entry keyValue : defaultCookieMap.entrySet()) { + if (!cookieParams.containsKey(keyValue.getKey())) { + store.addCookie(buildCookie(keyValue.getKey(), keyValue.getValue(), builder.getUri())); + } + } + + HttpClientContext context = HttpClientContext.create(); + context.setCookieStore(store); + + ContentType contentTypeObj = getContentType(contentType); + if (body != null || !formParams.isEmpty()) { + if (isBodyAllowed(method)) { + // Add entity if we have content and a valid method + builder.setBody(serialize(body, formParams, contentTypeObj), contentTypeObj); + } else { + throw new ApiException("method " + method + " does not support a request body"); + } + } else { + // for empty body + builder.setBody("", contentTypeObj); + } + + CompletableFuture future = new CompletableFuture<>(); + httpClient.execute(builder.build(), context, new FutureCallback() { + @Override + public void completed(SimpleHttpResponse result) { + try { + T data = processResponse(result, returnType); + future.complete(data); + } catch (Exception e) { + future.completeExceptionally(e); + } + } + + @Override + public void failed(Exception ex) { + future.completeExceptionally(ex); + } + + @Override + public void cancelled() { + future.cancel(true); + } + }); + + return future; + } + + /** + * Update query and header parameters based on authentication settings. + * + * @param authNames The authentications to apply + * @param queryParams Query parameters + * @param headerParams Header parameters + * @param cookieParams Cookie parameters + */ + private void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams, Map cookieParams) { + for (String authName : authNames) { + Authentication auth = authentications.get(authName); + if (auth == null) throw new RuntimeException("Authentication undefined: " + authName); + auth.applyToParams(queryParams, headerParams, cookieParams); + } + } + +} diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/BaseApi.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/BaseApi.mustache new file mode 100644 index 000000000000..0ba08c103017 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/BaseApi.mustache @@ -0,0 +1,111 @@ +{{>licenseInfo}} +package {{invokerPackage}}; + +import com.fasterxml.jackson.core.type.TypeReference; + +import java.util.concurrent.CompletableFuture; +import java.util.Collections; +import java.util.Map; + +{{>generatedAnnotation}} +public abstract class BaseApi { + + protected ApiClient apiClient; + + public BaseApi() { + this(Configuration.getDefaultApiClient()); + } + + public BaseApi(ApiClient apiClient) { + this.apiClient = apiClient; + } + + public ApiClient getApiClient() { + return apiClient; + } + + public void setApiClient(ApiClient apiClient) { + this.apiClient = apiClient; + } + + /** + * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests. + * @param url The URL for the request, either full URL or only the path. + * @param method The HTTP method for the request. + * @throws ApiException if fails to make API call. + */ + public CompletableFuture invokeAPI(String url, String method) throws ApiException { + return invokeAPI(url, method, null, null, Collections.emptyMap()); + } + + /** + * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests. + * @param url The URL for the request, either full URL or only the path. + * @param method The HTTP method for the request. + * @param additionalHeaders Additional headers for the request. + * @throws ApiException if fails to make API call. + */ + public CompletableFuture invokeAPI(String url, String method, Map additionalHeaders) throws ApiException { + return invokeAPI(url, method, null, null, additionalHeaders); + } + + /** + * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests. + * @param url The URL for the request, either full URL or only the path. + * @param method The HTTP method for the request. + * @param request The request object. + * @throws ApiException if fails to make API call. + */ + public CompletableFuture invokeAPI(String url, String method, Object request) throws ApiException { + return invokeAPI(url, method, request, null, Collections.emptyMap()); + } + + /** + * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests. + * @param url The URL for the request, either full URL or only the path. + * @param method The HTTP method for the request. + * @param request The request object. + * @param additionalHeaders Additional headers for the request. + * @throws ApiException if fails to make API call. + */ + public CompletableFuture invokeAPI(String url, String method, Object request, Map additionalHeaders) throws ApiException { + return invokeAPI(url, method, request, null, additionalHeaders); + } + + /** + * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests. + * @param url The URL for the request, either full URL or only the path. + * @param method The HTTP method for the request. + * @param returnType The return type. + * @return The API response in the specified type. + * @throws ApiException if fails to make API call. + */ + public CompletableFuture invokeAPI(String url, String method, TypeReference returnType) throws ApiException { + return invokeAPI(url, method, null, returnType, Collections.emptyMap()); + } + + /** + * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests. + * @param url The URL for the request, either full URL or only the path. + * @param method The HTTP method for the request. + * @param request The request object. + * @param returnType The return type. + * @return The API response in the specified type. + * @throws ApiException if fails to make API call. + */ + public CompletableFuture invokeAPI(String url, String method, Object request, TypeReference returnType) throws ApiException { + return invokeAPI(url, method, request, returnType, Collections.emptyMap()); + } + + /** + * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests. + * @param url The URL for the request, either full URL or only the path. + * @param method The HTTP method for the request. + * @param request The request object. + * @param returnType The return type. + * @param additionalHeaders Additional headers for the request. + * @return The API response in the specified type. + * @throws ApiException if fails to make API call. + */ + public abstract CompletableFuture invokeAPI(String url, String method, Object request, TypeReference returnType, Map additionalHeaders) throws ApiException; +} diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/README.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/README.mustache new file mode 100644 index 000000000000..b50e7db085c2 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/README.mustache @@ -0,0 +1,209 @@ +# {{artifactId}} + +{{appName}} + +- API version: {{appVersion}} +{{^hideGenerationTimestamp}} + +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} + +- Generator version: {{generatorVersion}} + +{{#appDescriptionWithNewLines}}{{{appDescriptionWithNewLines}}}{{/appDescriptionWithNewLines}} + +{{#infoUrl}} + For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)* + +## Requirements + +Building the API client library requires: + +1. Java 1.8+ +2. Maven/Gradle + +## Installation + +To install the API client library to your local Maven repository, simply execute: + +```shell +mvn clean install +``` + +To deploy it to a remote Maven repository instead, configure the settings of the repository and execute: + +```shell +mvn clean deploy +``` + +Refer to the [OSSRH Guide](http://central.sonatype.org/pages/ossrh-guide.html) for more information. + +### Maven users + +Add this dependency to your project's POM: + +```xml + + {{{groupId}}} + {{{artifactId}}} + {{{artifactVersion}}} + compile + +``` + +### Gradle users + +Add this dependency to your project's build file: + +```groovy +compile "{{{groupId}}}:{{{artifactId}}}:{{{artifactVersion}}}" +``` + +### Others + +At first generate the JAR by executing: + +```shell +mvn clean package +``` + +Then manually install the following JARs: + +- `target/{{{artifactId}}}-{{{artifactVersion}}}.jar` +- `target/lib/*.jar` + +## Getting Started + +Please follow the [installation](#installation) instruction and execute the following Java code: + +```java +{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}} +import {{{invokerPackage}}}.*; +import {{{invokerPackage}}}.auth.*; +import {{{modelPackage}}}.*; +import {{{package}}}.{{{classname}}}; + +public class {{{classname}}}Example { + + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("{{{basePath}}}"); + {{#hasAuthMethods}}{{#authMethods}}{{#isBasic}}{{#isBasicBasic}} + // Configure HTTP basic authorization: {{{name}}} + HttpBasicAuth {{{name}}} = (HttpBasicAuth) defaultClient.getAuthentication("{{{name}}}"); + {{{name}}}.setUsername("YOUR USERNAME"); + {{{name}}}.setPassword("YOUR PASSWORD");{{/isBasicBasic}}{{#isBasicBearer}} + // Configure HTTP bearer authorization: {{{name}}} + HttpBearerAuth {{{name}}} = (HttpBearerAuth) defaultClient.getAuthentication("{{{name}}}"); + {{{name}}}.setBearerToken("BEARER TOKEN");{{/isBasicBearer}}{{/isBasic}}{{#isApiKey}} + // Configure API key authorization: {{{name}}} + ApiKeyAuth {{{name}}} = (ApiKeyAuth) defaultClient.getAuthentication("{{{name}}}"); + {{{name}}}.setApiKey("YOUR API KEY"); + // Uncomment the following line to set a prefix for the API key, e.g. "Token" (defaults to null) + //{{{name}}}.setApiKeyPrefix("Token");{{/isApiKey}}{{#isOAuth}} + // Configure OAuth2 access token for authorization: {{{name}}} + OAuth {{{name}}} = (OAuth) defaultClient.getAuthentication("{{{name}}}"); + {{{name}}}.setAccessToken("YOUR ACCESS TOKEN");{{/isOAuth}}{{#isHttpSignature}} + // Configure HTTP signature authorization: {{{name}}} + HttpSignatureAuth {{{name}}} = (HttpSignatureAuth) defaultClient.getAuthentication("{{{name}}}"); + // All the HTTP signature parameters below should be customized to your environment. + // Configure the keyId + {{{name}}}.setKeyId("YOUR KEY ID"); + // Configure the signature algorithm + {{{name}}}.setSigningAlgorithm(SigningAlgorithm.HS2019); + // Configure the specific cryptographic algorithm + {{{name}}}.setAlgorithm(Algorithm.ECDSA_SHA256); + // Configure the cryptographic algorithm parameters, if applicable + {{{name}}}.setAlgorithmParameterSpec(null); + // Set the cryptographic digest algorithm. + {{{name}}}.setDigestAlgorithm("SHA-256"); + // Set the HTTP headers that should be included in the HTTP signature. + {{{name}}}.setHeaders(Arrays.asList("date", "host")); + // Set the private key used to sign the HTTP messages + {{{name}}}.setPrivateKey();{{/isHttpSignature}} + {{/authMethods}} + {{/hasAuthMethods}} + + {{{classname}}} apiInstance = new {{{classname}}}(defaultClient); + {{#allParams}} + {{{dataType}}} {{{paramName}}} = {{{example}}}; // {{{dataType}}} | {{{description}}} + {{/allParams}} + try { + {{#returnType}}{{{returnType}}} result = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}});{{#returnType}} + System.out.println(result);{{/returnType}} + } catch (ApiException e) { + System.err.println("Exception when calling {{{classname}}}#{{{operationId}}}"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} +``` + +## Documentation for API Endpoints + +All URIs are relative to *{{basePath}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{commonPath}}{{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +## Documentation for Models + +{{#models}}{{#model}} - [{{classname}}]({{modelDocPath}}{{classname}}.md) +{{/model}}{{/models}} + + +## Documentation for Authorization + +{{^authMethods}}Endpoints do not require authorization.{{/authMethods}} +{{#hasAuthMethods}}Authentication schemes defined for the API:{{/hasAuthMethods}} +{{#authMethods}} + +### {{name}} + +{{#isApiKey}} + +- **Type**: API key +- **API key parameter name**: {{keyParamName}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} +{{/isApiKey}} +{{#isBasicBasic}} + +- **Type**: HTTP basic authentication +{{/isBasicBasic}} +{{#isBasicBearer}} + +- **Type**: HTTP Bearer Token authentication{{#bearerFormat}} ({{{.}}}){{/bearerFormat}} +{{/isBasicBearer}} +{{#isHttpSignature}} + +- **Type**: HTTP signature authentication +{{/isHttpSignature}} +{{#isOAuth}} + +- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - {{scope}}: {{description}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}} + +## Recommendation + +It's recommended to create an instance of `ApiClient` per thread in a multithreaded environment to avoid any potential issues. + +## Author + +{{#apiInfo}}{{#apis}}{{#-last}}{{infoEmail}} +{{/-last}}{{/apis}}{{/apiInfo}} diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api.mustache new file mode 100644 index 000000000000..9b2ff5c86e7e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api.mustache @@ -0,0 +1,240 @@ +{{>licenseInfo}} +package {{package}}; + +import com.fasterxml.jackson.core.type.TypeReference; +import {{invokerPackage}}.ApiException; +import {{invokerPackage}}.ApiClient; +import {{invokerPackage}}.BaseApi; +import {{invokerPackage}}.Configuration; +{{#models.0}} +import {{modelPackage}}.*; +{{/models.0}} +import {{invokerPackage}}.Pair; + +{{#imports}}import {{import}}; +{{/imports}} + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; +import java.util.concurrent.CompletableFuture; + +{{#useBeanValidation}} +import {{javaxPackage}}.validation.constraints.*; +import {{javaxPackage}}.validation.Valid; + +{{/useBeanValidation}} +{{>generatedAnnotation}} +{{#operations}} +public class {{classname}} extends BaseApi { + + public {{classname}}() { + super(Configuration.getDefaultApiClient()); + } + + public {{classname}}(ApiClient apiClient) { + super(apiClient); + } + + {{#operation}} + /** + * {{summary}} + * {{notes}} + {{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}} + {{/allParams}} + {{#returnType}} + * @return CompletableFuture<{{returnType}}> + {{/returnType}} + * @throws ApiException if fails to make API call + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} + public CompletableFuture<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException { + return this.{{operationId}}({{#allParams}}{{paramName}}, {{/allParams}}Collections.emptyMap()); + } + + + /** + * {{summary}} + * {{notes}} + {{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}} + {{/allParams}} + * @param additionalHeaders additionalHeaders for this call + {{#returnType}} + * @return CompletableFuture<{{returnType}}> + {{/returnType}} + * @throws ApiException if fails to make API call + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} + public CompletableFuture<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}Map additionalHeaders) throws ApiException { + Object localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; + {{#allParams}}{{#required}} + // verify the required parameter '{{paramName}}' is set + if ({{paramName}} == null) { + throw new ApiException(400, "Missing the required parameter '{{paramName}}' when calling {{operationId}}"); + } + {{/required}}{{/allParams}} + // create path and map variables + String localVarPath = "{{{path}}}"{{#pathParams}} + .replaceAll("\\{" + "{{baseName}}" + "\\}", apiClient.escapeString(apiClient.parameterToString({{{paramName}}}))){{/pathParams}}; + + StringJoiner localVarQueryStringJoiner = new StringJoiner("&"); + String localVarQueryParameterBaseName; + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + {{#queryParams}} + {{#isDeepObject}} + localVarQueryParameterBaseName = "{{{baseName}}}"; + {{#isArray}} + for (int i=0; i < {{paramName}}.size(); i++) { + localVarQueryStringJoiner.add({{paramName}}.get(i).toUrlQueryString(String.format("{{baseName}}[%d]", i))); + } + {{/isArray}} + {{^isArray}} + localVarQueryStringJoiner.add({{paramName}}.toUrlQueryString("{{baseName}}")); + {{/isArray}} + {{/isDeepObject}} + {{^isDeepObject}} + {{#isExplode}} + {{#hasVars}} + {{#vars}} + {{#isArray}} + localVarQueryParams.addAll(apiClient.parameterToPairs("multi", "{{baseName}}", {{paramName}}.{{getter}}())); + {{/isArray}} + {{^isArray}} + localVarQueryParams.addAll(apiClient.parameterToPair("{{baseName}}", {{paramName}}.{{getter}}())); + {{/isArray}} + {{/vars}} + {{/hasVars}} + {{^hasVars}} + {{#isModel}} + localVarQueryStringJoiner.add({{paramName}}.toUrlQueryString()); + {{/isModel}} + {{^isModel}} + {{#collectionFormat}}localVarCollectionQueryParams.addAll(apiClient.parameterToPairs("{{{collectionFormat}}}", {{/collectionFormat}}{{^collectionFormat}}localVarQueryParams.addAll(apiClient.parameterToPair({{/collectionFormat}}"{{baseName}}", {{paramName}})); + {{/isModel}} + {{/hasVars}} + {{/isExplode}} + {{^isExplode}} + {{#collectionFormat}}localVarCollectionQueryParams.addAll(apiClient.parameterToPairs("{{{collectionFormat}}}", {{/collectionFormat}}{{^collectionFormat}}localVarQueryParams.addAll(apiClient.parameterToPair({{/collectionFormat}}"{{baseName}}", {{paramName}})); + {{/isExplode}} + {{/isDeepObject}} + {{/queryParams}} + {{#headerParams}}if ({{paramName}} != null) + localVarHeaderParams.put("{{baseName}}", apiClient.parameterToString({{paramName}})); + {{/headerParams}} + + localVarHeaderParams.putAll(additionalHeaders); + + {{#cookieParams}}if ({{paramName}} != null) + localVarCookieParams.put("{{baseName}}", apiClient.parameterToString({{paramName}})); + {{/cookieParams}} + + {{#formParams}}if ({{paramName}} != null) + localVarFormParams.put("{{baseName}}", {{paramName}}); + {{/formParams}} + + final String[] localVarAccepts = { + {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}} + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + + final String[] localVarContentTypes = { + {{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}} + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + + String[] localVarAuthNames = new String[] { {{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{/authMethods}} }; + + {{#returnType}} + TypeReference<{{{returnType}}}> localVarReturnType = new TypeReference<{{{returnType}}}>() {}; + {{/returnType}} + return apiClient.invokeAPI( + localVarPath, + "{{httpMethod}}", + localVarQueryParams, + localVarCollectionQueryParams, + localVarQueryStringJoiner.toString(), + localVarPostBody, + localVarHeaderParams, + localVarCookieParams, + localVarFormParams, + localVarAccept, + localVarContentType, + localVarAuthNames, + {{#returnType}}localVarReturnType{{/returnType}}{{^returnType}}null{{/returnType}} + ); + } + + {{#-last}} + @Override + public CompletableFuture invokeAPI(String url, String method, Object request, TypeReference returnType, Map additionalHeaders) throws ApiException { + String localVarPath = url.replace(apiClient.getBaseURL(), ""); + StringJoiner localVarQueryStringJoiner = new StringJoiner("&"); + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + localVarHeaderParams.putAll(additionalHeaders); + + final String[] localVarAccepts = { + {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}} + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + + final String[] localVarContentTypes = { + {{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}} + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + + String[] localVarAuthNames = new String[] { {{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{/authMethods}} }; + + return apiClient.invokeAPI( + localVarPath, + method, + localVarQueryParams, + localVarCollectionQueryParams, + localVarQueryStringJoiner.toString(), + request, + localVarHeaderParams, + localVarCookieParams, + localVarFormParams, + localVarAccept, + localVarContentType, + localVarAuthNames, + returnType + ); + } + {{/-last}} + {{/operation}} +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api_test.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api_test.mustache new file mode 100644 index 000000000000..e2b46150259a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api_test.mustache @@ -0,0 +1,58 @@ +{{>licenseInfo}} + +package {{package}}; + +import {{invokerPackage}}.ApiException; +{{#imports}}import {{import}}; +{{/imports}} +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.util.concurrent.CompletableFuture; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +{{#useBeanValidation}} +import {{javaxPackage}}.validation.constraints.*; +import {{javaxPackage}}.validation.Valid; + +{{/useBeanValidation}} +/** + * API tests for {{classname}} + */ +@Disabled +public class {{classname}}Test { + + private final {{classname}} api = new {{classname}}(); + + {{#operations}} + {{#operation}} + /** + {{#summary}} + * {{summary}} + * + {{/summary}} + {{#notes}} + * {{notes}} + * + {{/notes}} + * @throws ApiException + * if the Api call fails + */ + @Test + public void {{operationId}}Test() throws ApiException { + {{#allParams}} + {{{dataType}}} {{paramName}} = null; + {{/allParams}} + {{#returnType}}CompletableFuture<{{{returnType}}}> response = {{/returnType}}api.{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}); + + // TODO: test validations + } + {{/operation}} + {{/operations}} +} diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/build.gradle.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/build.gradle.mustache new file mode 100644 index 000000000000..f18581f08ba1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/build.gradle.mustache @@ -0,0 +1,145 @@ +apply plugin: 'idea' +apply plugin: 'eclipse' + +group = '{{groupId}}' +version = '{{artifactVersion}}' + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.+' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + } +} + +repositories { + mavenCentral() +} + +if(hasProperty('target') && target == 'android') { + + apply plugin: 'com.android.library' + apply plugin: 'com.github.dcendents.android-maven' + + android { + compileSdkVersion 25 + buildToolsVersion '25.0.2' + defaultConfig { + minSdkVersion 14 + targetSdkVersion 25 + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + // Rename the aar correctly + libraryVariants.all { variant -> + variant.outputs.each { output -> + def outputFile = output.outputFile + if (outputFile != null && outputFile.name.endsWith('.aar')) { + def fileName = "${project.name}-${variant.baseName}-${version}.aar" + output.outputFile = new File(outputFile.parent, fileName) + } + } + } + + dependencies { + provided "jakarta.annotation:jakarta.annotation-api:$jakarta_annotation_version" + } + } + + afterEvaluate { + android.libraryVariants.all { variant -> + def task = project.tasks.create "jar${variant.name.capitalize()}", Jar + task.description = "Create jar artifact for ${variant.name}" + task.dependsOn variant.javaCompile + task.from variant.javaCompile.destinationDirectory + task.destinationDirectory = project.file("${project.buildDir}/outputs/jar") + task.archiveFileName = "${project.name}-${variant.baseName}-${version}.jar" + artifacts.add('archives', task); + } + } + + task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' + } + + artifacts { + archives sourcesJar + } + +} else { + + apply plugin: 'java' + apply plugin: 'maven-publish' + + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + + publishing { + publications { + maven(MavenPublication) { + artifactId = '{{artifactId}}' + + from components.java + } + } + } + + task execute(type:JavaExec) { + main = System.getProperty('mainClass') + classpath = sourceSets.main.runtimeClasspath + } + + task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource + } + + task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir + } + + artifacts { + archives sourcesJar + archives javadocJar + } +} + +ext { + swagger_annotations_version = "1.6.6" + jackson_version = "2.17.1" + jackson_databind_version = "2.17.1" + {{#openApiNullable}} + jackson_databind_nullable_version = "0.2.6" + {{/openApiNullable}} + jakarta_annotation_version = "1.3.5" + httpclient_version = "5.1.3" + jodatime_version = "2.9.9" + junit_version = "4.13.2" +} + +dependencies { + implementation "io.swagger:swagger-annotations:$swagger_annotations_version" + implementation "com.google.code.findbugs:jsr305:3.0.2" + implementation "org.apache.httpcomponents.client5:httpclient5:$httpclient_version" + implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version" + implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version" + implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_databind_version" + implementation "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version" + {{#openApiNullable}} + implementation "org.openapitools:jackson-databind-nullable:$jackson_databind_nullable_version" + {{/openApiNullable}} + {{#joda}} + implementation "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_version" + {{/joda}} + implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version" + implementation "jakarta.annotation:jakarta.annotation-api:$jakarta_annotation_version" + testImplementation "junit:junit:$junit_version" +} diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/pom.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/pom.mustache new file mode 100644 index 000000000000..3a506ca3f828 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/pom.mustache @@ -0,0 +1,369 @@ + + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{artifactUrl}} + {{artifactDescription}} + + {{scmConnection}} + {{scmDeveloperConnection}} + {{scmUrl}} + +{{#parentOverridden}} + + {{{parentGroupId}}} + {{{parentArtifactId}}} + {{{parentVersion}}} + +{{/parentOverridden}} + + + + {{licenseName}} + {{licenseUrl}} + repo + + + + + + {{developerName}} + {{developerEmail}} + {{developerOrganization}} + {{developerOrganizationUrl}} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + 1.8 + 1.8 + true + 128m + 512m + + -Xlint:all + -J-Xss4m + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.1.0 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + 10 + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + test-jar + + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.3.0 + + + add_sources + generate-sources + + add-source + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.4.1 + + none + 1.8 + + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar-no-fork + + + + + + + + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.0.1 + + + sign-artifacts + verify + + sign + + + + + + + + + + + {{#swagger1AnnotationLibrary}} + + io.swagger + swagger-annotations + ${swagger-annotations-version} + + {{/swagger1AnnotationLibrary}} + {{#swagger2AnnotationLibrary}} + + io.swagger.core.v3 + swagger-annotations + ${swagger-annotations-version} + + {{/swagger2AnnotationLibrary}} + + + + com.google.code.findbugs + jsr305 + 3.0.2 + + + + + + org.apache.httpcomponents.client5 + httpclient5 + ${httpclient-version} + + + + + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-databind-version} + + {{^useJakartaEe}} + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + ${jackson-version} + + {{/useJakartaEe}} + {{#useJakartaEe}} + + com.fasterxml.jackson.jakarta.rs + jackson-jakarta-rs-json-provider + ${jackson-version} + + {{/useJakartaEe}} + {{#withXml}} + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + ${jackson-version} + + + {{/withXml}} + {{#joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + {{/joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-version} + + {{#useBeanValidation}} + + + jakarta.validation + jakarta.validation-api + ${beanvalidation-version} + provided + + {{/useBeanValidation}} + {{#performBeanValidation}} + + + org.hibernate + hibernate-validator + 5.4.3.Final + + {{/performBeanValidation}} + {{#parcelableModel}} + + + com.google.android + android + 4.1.1.4 + provided + + {{/parcelableModel}} + {{#openApiNullable}} + + org.openapitools + jackson-databind-nullable + ${jackson-databind-nullable-version} + + {{/openApiNullable}} + + jakarta.annotation + jakarta.annotation-api + ${jakarta-annotation-version} + provided + + + + org.junit.jupiter + junit-jupiter-api + ${junit-version} + test + + + + UTF-8 + {{#swagger1AnnotationLibrary}} + 1.6.6 + {{/swagger1AnnotationLibrary}} + {{#swagger2AnnotationLibrary}} + 2.2.15 + {{/swagger2AnnotationLibrary}} + 5.2.1 + 2.17.1 + 2.17.1 + {{#openApiNullable}} + 0.2.6 + {{/openApiNullable}} + {{#useJakartaEe}} + 2.1.1 + 3.0.2 + {{/useJakartaEe}} + {{^useJakartaEe}} + 1.3.5 + 2.0.2 + {{/useJakartaEe}} + 5.10.2 + + diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java index 71191d9f7194..a63f4d4b0b35 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java @@ -72,6 +72,7 @@ public class JavaClientCodegenTest { @Getter enum Library { APACHE_HTTPCLIENT("apache-httpclient", Serializer.JACKSON), + APACHE_ASYNCHTTPCLIENT("apache-asynchttpclient", Serializer.JACKSON), FEIGN("feign", Serializer.JACKSON, Set.of(Serializer.GSON)), GOOGLE_API_CLIENT("google-api-client", Serializer.JACKSON), JERSEY_2("jersey2", Serializer.JACKSON), @@ -3234,4 +3235,24 @@ public void testGenerateParameterId() { " getCall(Integer queryParameter, final ApiCallback _callback)" ); } + + @Test + public void testApacheAsyncHasCompletableFuture() { + final Path output = newTempFolder(); + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("java") + .setLibrary("apache-asynchttpclient") + .setInputSpec("src/test/resources/3_0/echo_api.yaml") + .setOutputDir(output.toString().replace("\\", "/")); + + new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + + Path apiFile = output.resolve("src/main/java/org/openapitools/client/api/QueryApi.java"); + + assertThat(apiFile).content() + .contains("import java.util.concurrent.CompletableFuture;") + .contains("CompletableFuture invokeAPI") + .contains("CompletableFuture testQueryStyleFormExplodeTrueObjectAllOf("); + } + } \ No newline at end of file