From 7db8316c0985bd7e0d941e739314a11989e61b39 Mon Sep 17 00:00:00 2001 From: Anders Sjoblom Date: Fri, 30 Jan 2026 16:39:16 -0500 Subject: [PATCH 1/2] Updated ApiClient.mustache to handle large file uploads --- README.md | 12 +- build.gradle | 2 +- .../resources/vertex-java/ApiClient.mustache | 1971 +++++++++++++++++ 3 files changed, 1978 insertions(+), 7 deletions(-) create mode 100644 buildSrc/src/main/resources/vertex-java/ApiClient.mustache diff --git a/README.md b/README.md index c37f729..623a736 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The client can be used with Java 1.8+ and pulled into Maven or Gradle projects. com.vertexvis api-client-java - 0.16.0 + 0.17.0 compile ``` @@ -25,13 +25,13 @@ The client can be used with Java 1.8+ and pulled into Maven or Gradle projects. ### Gradle ```groovy -compile "com.vertexvis:api-client-java:0.16.0" +compile "com.vertexvis:api-client-java:0.17.0" ``` ### Sbt ```sbt -libraryDependencies += "com.vertexvis" % "api-client-java" % "0.16.0" +libraryDependencies += "com.vertexvis" % "api-client-java" % "0.17.0" ``` ### Others @@ -44,7 +44,7 @@ mvn clean package Then manually install the following JARs. -- `target/api-client-java-0.16.0.jar` +- `target/api-client-java-0.17.0.jar` - `target/lib/*.jar` ## Usage @@ -104,7 +104,7 @@ To consume published snapshot versions in other projects, add the snapshot repos com.vertexvis api-client-java - 0.16.0-SNAPSHOT + 0.17.0-SNAPSHOT ``` @@ -119,7 +119,7 @@ repositories { } dependencies { - implementation 'com.vertexvis:api-client-java:0.16.0-SNAPSHOT' + implementation 'com.vertexvis:api-client-java:0.17.0-SNAPSHOT' } ``` diff --git a/build.gradle b/build.gradle index 7cc1604..51dc9d3 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id "io.github.gradle-nexus.publish-plugin" } -def projectVersion = '0.16.0' +def projectVersion = '0.17.0' def isSnapshot = project.hasProperty('isSnapshot') && project.isSnapshot.toBoolean() version = isSnapshot ? "${projectVersion}-SNAPSHOT" : projectVersion diff --git a/buildSrc/src/main/resources/vertex-java/ApiClient.mustache b/buildSrc/src/main/resources/vertex-java/ApiClient.mustache new file mode 100644 index 0000000..697776d --- /dev/null +++ b/buildSrc/src/main/resources/vertex-java/ApiClient.mustache @@ -0,0 +1,1971 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +{{#dynamicOperations}} +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; +import io.swagger.v3.parser.OpenAPIV3Parser; +{{/dynamicOperations}} +import okhttp3.*; +import okhttp3.internal.http.HttpMethod; +import okhttp3.internal.tls.OkHostnameVerifier; +import okhttp3.logging.HttpLoggingInterceptor; +import okhttp3.logging.HttpLoggingInterceptor.Level; +import okio.Buffer; +import okio.BufferedSink; +import okio.Okio; +{{#joda}} +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +{{/joda}} +{{#hasOAuthMethods}} +import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; +import org.apache.oltu.oauth2.common.message.types.GrantType; +{{/hasOAuthMethods}} + +import javax.net.ssl.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Type; +import java.net.URI; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.text.DateFormat; +{{#jsr310}} +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +{{/jsr310}} +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import {{invokerPackage}}.auth.Authentication; +import {{invokerPackage}}.auth.HttpBasicAuth; +import {{invokerPackage}}.auth.HttpBearerAuth; +import {{invokerPackage}}.auth.ApiKeyAuth; +{{#hasOAuthMethods}} +import {{invokerPackage}}.auth.OAuth; +import {{invokerPackage}}.auth.RetryingOAuth; +import {{invokerPackage}}.auth.OAuthFlow; +{{/hasOAuthMethods}} +{{#withAWSV4Signature}} +import {{invokerPackage}}.auth.AWS4Auth; +{{/withAWSV4Signature}} + +/** + *

ApiClient class.

+ */ +public class ApiClient { + + protected 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; + protected boolean debugging = false; + protected Map defaultHeaderMap = new HashMap(); + protected Map defaultCookieMap = new HashMap(); + protected String tempFolderPath = null; + + protected Map authentications; + + protected DateFormat dateFormat; + protected DateFormat datetimeFormat; + protected boolean lenientDatetimeFormat; + protected int dateLength; + + protected InputStream sslCaCert; + protected boolean verifyingSsl; + protected KeyManager[] keyManagers; + + protected OkHttpClient httpClient; + protected JSON json; + + protected HttpLoggingInterceptor loggingInterceptor; + + {{#dynamicOperations}} + protected Map operationLookupMap = new HashMap<>(); + + {{/dynamicOperations}} + /** + * Basic constructor for ApiClient + */ + public ApiClient() { + init(); + initHttpClient(); + + // Setup authentications (key: authentication name, value: authentication).{{#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}}{{#isKeyInQuery}}"query"{{/isKeyInQuery}}{{#isKeyInCookie}}"cookie"{{/isKeyInCookie}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}} + authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}}{{#withAWSV4Signature}} + authentications.put("AWS4Auth", new AWS4Auth());{{/withAWSV4Signature}} + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + /** + * Basic constructor with custom OkHttpClient + * + * @param client a {@link okhttp3.OkHttpClient} object + */ + public ApiClient(OkHttpClient client) { + init(); + + httpClient = client; + + // Setup authentications (key: authentication name, value: authentication).{{#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}}{{#isKeyInQuery}}"query"{{/isKeyInQuery}}{{#isKeyInCookie}}"cookie"{{/isKeyInCookie}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}} + authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}}{{#withAWSV4Signature}} + authentications.put("AWS4Auth", new AWS4Auth());{{/withAWSV4Signature}} + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + {{#hasOAuthMethods}} + {{#oauthMethods}} + {{#-first}} + /** + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID + * + * @param clientId client ID + */ + public ApiClient(String clientId) { + this(clientId, null, null); + } + + /** + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID and additional parameters + * + * @param clientId client ID + * @param parameters a {@link java.util.Map} of parameters + */ + public ApiClient(String clientId, Map parameters) { + this(clientId, null, parameters); + } + + /** + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID, secret, and additional parameters + * + * @param clientId client ID + * @param clientSecret client secret + * @param parameters a {@link java.util.Map} of parameters + */ + public ApiClient(String clientId, String clientSecret, Map parameters) { + this(null, clientId, clientSecret, parameters); + } + + /** + * Constructor for ApiClient to support access token retry on 401/403 configured with base path, client ID, secret, and additional parameters + * + * @param basePath base path + * @param clientId client ID + * @param clientSecret client secret + * @param parameters a {@link java.util.Map} of parameters + */ + public ApiClient(String basePath, String clientId, String clientSecret, Map parameters) { + init(); + if (basePath != null) { + this.basePath = basePath; + } + + String tokenUrl = "{{{tokenUrl}}}"; + if (!"".equals(tokenUrl) && !URI.create(tokenUrl).isAbsolute()) { + URI uri = URI.create(getBasePath()); + tokenUrl = uri.getScheme() + ":" + + (uri.getAuthority() != null ? "//" + uri.getAuthority() : "") + + tokenUrl; + if (!URI.create(tokenUrl).isAbsolute()) { + throw new IllegalArgumentException("OAuth2 token URL must be an absolute URL"); + } + } + RetryingOAuth retryingOAuth = new RetryingOAuth(tokenUrl, clientId, OAuthFlow.{{#lambda.uppercase}}{{#lambda.snakecase}}{{flow}}{{/lambda.snakecase}}{{/lambda.uppercase}}, clientSecret, parameters); + authentications.put( + "{{name}}", + retryingOAuth + ); + initHttpClient(Collections.singletonList(retryingOAuth)); + // Setup authentications (key: authentication name, value: authentication).{{#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}}{{#isKeyInQuery}}"query"{{/isKeyInQuery}}{{#isKeyInCookie}}"cookie"{{/isKeyInCookie}}, "{{keyParamName}}"));{{/isApiKey}}{{/authMethods}}{{#withAWSV4Signature}} + authentications.put("AWS4Auth", new AWS4Auth());{{/withAWSV4Signature}} + + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + {{/-first}} + {{/oauthMethods}} + {{/hasOAuthMethods}} + protected void initHttpClient() { + initHttpClient(Collections.emptyList()); + } + + protected void initHttpClient(List interceptors) { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.addNetworkInterceptor(getProgressInterceptor()); + for (Interceptor interceptor: interceptors) { + builder.addInterceptor(interceptor); + } + {{#useGzipFeature}} + // Enable gzip request compression + builder.addInterceptor(new GzipRequestInterceptor()); + {{/useGzipFeature}} + + httpClient = builder.build(); + } + + protected void init() { + verifyingSsl = true; + + json = new JSON(); + + // Set default User-Agent. + setUserAgent("{{{httpUserAgent}}}{{^httpUserAgent}}OpenAPI-Generator/{{{artifactVersion}}}/java{{/httpUserAgent}}"); + + authentications = new HashMap(); + {{#dynamicOperations}} + + OpenAPI openAPI = new OpenAPIV3Parser().read("openapi/openapi.yaml"); + createOperationLookupMap(openAPI); + {{/dynamicOperations}} + } + + /** + * Get base path + * + * @return Base path + */ + public String getBasePath() { + return basePath; + } + + /** + * Set base path + * + * @param basePath Base path of the URL (e.g {{{basePath}}} + * @return An instance of OkHttpClient + */ + public ApiClient setBasePath(String basePath) { + this.basePath = basePath; + this.serverIndex = null; + return this; + } + + public List getServers() { + return servers; + } + + public ApiClient setServers(List servers) { + this.servers = servers; + return this; + } + + public Integer getServerIndex() { + return serverIndex; + } + + public ApiClient setServerIndex(Integer serverIndex) { + this.serverIndex = serverIndex; + return this; + } + + public Map getServerVariables() { + return serverVariables; + } + + public ApiClient setServerVariables(Map serverVariables) { + this.serverVariables = serverVariables; + return this; + } + + /** + * Get HTTP client + * + * @return An instance of OkHttpClient + */ + public OkHttpClient getHttpClient() { + return httpClient; + } + + /** + * Set HTTP client, which must never be null. + * + * @param newHttpClient An instance of OkHttpClient + * @return Api Client + * @throws java.lang.NullPointerException when newHttpClient is null + */ + public ApiClient setHttpClient(OkHttpClient newHttpClient) { + this.httpClient = Objects.requireNonNull(newHttpClient, "HttpClient must not be null!"); + return this; + } + + /** + * Get JSON + * + * @return JSON object + */ + public JSON getJSON() { + return json; + } + + /** + * Set JSON + * + * @param json JSON object + * @return Api client + */ + public ApiClient setJSON(JSON json) { + this.json = json; + return this; + } + + /** + * True if isVerifyingSsl flag is on + * + * @return True if isVerifySsl flag is on + */ + public boolean isVerifyingSsl() { + return verifyingSsl; + } + + /** + * Configure whether to verify certificate and hostname when making https requests. + * Default to true. + * NOTE: Do NOT set to false in production code, otherwise you would face multiple types of cryptographic attacks. + * + * @param verifyingSsl True to verify TLS/SSL connection + * @return ApiClient + */ + public ApiClient setVerifyingSsl(boolean verifyingSsl) { + this.verifyingSsl = verifyingSsl; + applySslSettings(); + return this; + } + + /** + * Get SSL CA cert. + * + * @return Input stream to the SSL CA cert + */ + public InputStream getSslCaCert() { + return sslCaCert; + } + + /** + * Configure the CA certificate to be trusted when making https requests. + * Use null to reset to default. + * + * @param sslCaCert input stream for SSL CA cert + * @return ApiClient + */ + public ApiClient setSslCaCert(InputStream sslCaCert) { + this.sslCaCert = sslCaCert; + applySslSettings(); + return this; + } + + /** + *

Getter for the field keyManagers.

+ * + * @return an array of {@link javax.net.ssl.KeyManager} objects + */ + public KeyManager[] getKeyManagers() { + return keyManagers; + } + + /** + * Configure client keys to use for authorization in an SSL session. + * Use null to reset to default. + * + * @param managers The KeyManagers to use + * @return ApiClient + */ + public ApiClient setKeyManagers(KeyManager[] managers) { + this.keyManagers = managers; + applySslSettings(); + return this; + } + + /** + *

Getter for the field dateFormat.

+ * + * @return a {@link java.text.DateFormat} object + */ + public DateFormat getDateFormat() { + return dateFormat; + } + + /** + *

Setter for the field dateFormat.

+ * + * @param dateFormat a {@link java.text.DateFormat} object + * @return a {@link {{invokerPackage}}.ApiClient} object + */ + public ApiClient setDateFormat(DateFormat dateFormat) { + JSON.setDateFormat(dateFormat); + return this; + } + + /** + *

Set SqlDateFormat.

+ * + * @param dateFormat a {@link java.text.DateFormat} object + * @return a {@link {{invokerPackage}}.ApiClient} object + */ + public ApiClient setSqlDateFormat(DateFormat dateFormat) { + JSON.setSqlDateFormat(dateFormat); + return this; + } + + {{#joda}} + public ApiClient setDateTimeFormat(DateTimeFormatter dateFormat) { + JSON.setDateTimeFormat(dateFormat); + return this; + } + + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + JSON.setLocalDateFormat(dateFormat); + return this; + } + + {{/joda}} + {{#jsr310}} + /** + *

Set OffsetDateTimeFormat.

+ * + * @param dateFormat a {@link java.time.format.DateTimeFormatter} object + * @return a {@link {{invokerPackage}}.ApiClient} object + */ + public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + JSON.setOffsetDateTimeFormat(dateFormat); + return this; + } + + /** + *

Set LocalDateFormat.

+ * + * @param dateFormat a {@link java.time.format.DateTimeFormatter} object + * @return a {@link {{invokerPackage}}.ApiClient} object + */ + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + JSON.setLocalDateFormat(dateFormat); + return this; + } + + {{/jsr310}} + /** + *

Set LenientOnJson.

+ * + * @param lenientOnJson a boolean + * @return a {@link {{invokerPackage}}.ApiClient} object + */ + public ApiClient setLenientOnJson(boolean lenientOnJson) { + JSON.setLenientOnJson(lenientOnJson); + return this; + } + + /** + * Get authentications (key: authentication name, value: authentication). + * + * @return Map of authentication objects + */ + 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); + } + + {{#hasHttpBearerMethods}} + /** + * Helper method to set access token for the first Bearer authentication. + * @param bearerToken Bearer token + */ + public void setBearerToken(String bearerToken) { + setBearerToken(() -> bearerToken); + } + + /** + * Helper method to set the supplier of access tokens for Bearer authentication. + * + * @param tokenSupplier The supplier of bearer tokens + */ + 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}} + + /** + * Helper method to set username for the first HTTP basic authentication. + * + * @param username Username + */ + public void setUsername(String username) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setUsername(username); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set password for the first HTTP basic authentication. + * + * @param password Password + */ + public void setPassword(String password) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setPassword(password); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set API key value for the first API key authentication. + * + * @param apiKey API key + */ + public void setApiKey(String apiKey) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKey(apiKey); + return; + } + } + 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 + */ + public void setApiKeyPrefix(String apiKeyPrefix) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set access token for the first OAuth2 authentication. + * + * @param accessToken Access token + */ + public void setAccessToken(String accessToken) { + {{#hasOAuthMethods}} + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setAccessToken(accessToken); + return; + } + } + {{/hasOAuthMethods}} + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Helper method to set credentials for AWSV4 Signature + * + * @param accessKey Access Key + * @param secretKey Secret Key + * @param region Region + * @param service Service to access to + */ + public void setAWS4Configuration(String accessKey, String secretKey, String region, String service) { + {{#withAWSV4Signature}} + for (Authentication auth : authentications.values()) { + if (auth instanceof AWS4Auth) { + ((AWS4Auth) auth).setCredentials(accessKey, secretKey); + ((AWS4Auth) auth).setRegion(region); + ((AWS4Auth) auth).setService(service); + return; + } + } + {{/withAWSV4Signature}} + throw new RuntimeException("No AWS4 authentication configured!"); + } + + /** + * Helper method to set credentials for AWSV4 Signature + * + * @param accessKey Access Key + * @param secretKey Secret Key + * @param sessionToken Session Token + * @param region Region + * @param service Service to access to + */ + public void setAWS4Configuration(String accessKey, String secretKey, String sessionToken, String region, String service) { + {{#withAWSV4Signature}} + for (Authentication auth : authentications.values()) { + if (auth instanceof AWS4Auth) { + ((AWS4Auth) auth).setCredentials(accessKey, secretKey, sessionToken); + ((AWS4Auth) auth).setRegion(region); + ((AWS4Auth) auth).setService(service); + return; + } + } + {{/withAWSV4Signature}} + throw new RuntimeException("No AWS4 authentication configured!"); + } + + /** + * Set the User-Agent header's value (by adding to the default header map). + * + * @param userAgent HTTP request's user agent + * @return ApiClient + */ + public ApiClient setUserAgent(String userAgent) { + addDefaultHeader("User-Agent", userAgent); + return this; + } + + /** + * Add a default header. + * + * @param key The header's key + * @param value The header's value + * @return ApiClient + */ + 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 ApiClient + */ + 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 enabled, false otherwise. + */ + public boolean isDebugging() { + return debugging; + } + + /** + * Enable/disable debugging for this API client. + * + * @param debugging To enable (true) or disable (false) debugging + * @return ApiClient + */ + public ApiClient setDebugging(boolean debugging) { + if (debugging != this.debugging) { + if (debugging) { + loggingInterceptor = new HttpLoggingInterceptor(); + loggingInterceptor.setLevel(Level.BODY); + httpClient = httpClient.newBuilder().addInterceptor(loggingInterceptor).build(); + } else { + final OkHttpClient.Builder builder = httpClient.newBuilder(); + builder.interceptors().remove(loggingInterceptor); + httpClient = builder.build(); + loggingInterceptor = null; + } + } + this.debugging = debugging; + return this; + } + + /** + * 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. + * + * @see createTempFile + * @return Temporary folder path + */ + public String getTempFolderPath() { + return tempFolderPath; + } + + /** + * Set the temporary folder path (for downloading files) + * + * @param tempFolderPath Temporary folder path + * @return ApiClient + */ + public ApiClient setTempFolderPath(String tempFolderPath) { + this.tempFolderPath = tempFolderPath; + return this; + } + + /** + * Get connection timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getConnectTimeout() { + return httpClient.connectTimeoutMillis(); + } + + /** + * Sets the connect timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link java.lang.Integer#MAX_VALUE}. + * + * @param connectionTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setConnectTimeout(int connectionTimeout) { + httpClient = httpClient.newBuilder().connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + /** + * Get read timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getReadTimeout() { + return httpClient.readTimeoutMillis(); + } + + /** + * Sets the read timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link java.lang.Integer#MAX_VALUE}. + * + * @param readTimeout read timeout in milliseconds + * @return Api client + */ + public ApiClient setReadTimeout(int readTimeout) { + httpClient = httpClient.newBuilder().readTimeout(readTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + /** + * Get write timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getWriteTimeout() { + return httpClient.writeTimeoutMillis(); + } + + /** + * Sets the write timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link java.lang.Integer#MAX_VALUE}. + * + * @param writeTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setWriteTimeout(int writeTimeout) { + httpClient = httpClient.newBuilder().writeTimeout(writeTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + {{#hasOAuthMethods}} + /** + * Helper method to configure the token endpoint of the first oauth found in the apiAuthorizations (there should be only one) + * + * @return Token request builder + */ + public TokenRequestBuilder getTokenEndPoint() { + for (Authentication apiAuth : authentications.values()) { + if (apiAuth instanceof RetryingOAuth) { + RetryingOAuth retryingOAuth = (RetryingOAuth) apiAuth; + return retryingOAuth.getTokenRequestBuilder(); + } + } + return null; + } + {{/hasOAuthMethods}} + + /** + * Format the given parameter object into string. + * + * @param param Parameter + * @return String representation of the parameter + */ + public String parameterToString(Object param) { + if (param == null) { + return ""; + } else if (param instanceof Date {{#joda}}|| param instanceof DateTime || param instanceof LocalDate{{/joda}}{{#jsr310}}|| param instanceof OffsetDateTime || param instanceof LocalDate{{/jsr310}}) { + //Serialize to json string and remove the " enclosing characters + String jsonStr = JSON.serialize(param); + return jsonStr.substring(1, jsonStr.length() - 1); + } else if (param instanceof Collection) { + StringBuilder b = new StringBuilder(); + for (Object o : (Collection) param) { + if (b.length() > 0) { + b.append(","); + } + b.append(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, parameterToString(value))); + return params; + } + + {{^dynamicOperations}} + /** + * 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; + } + {{/dynamicOperations}} + {{#dynamicOperations}} + public List parameterToPairs(Parameter param, Collection value) { + List params = new ArrayList(); + + // preconditions + if (param == null || param.getName() == null || param.getName().isEmpty() || value == null || value.isEmpty()) { + return params; + } + + // create the params based on the collection format + if (StyleEnum.FORM.equals(param.getStyle()) && Boolean.TRUE.equals(param.getExplode())) { + for (Object item : value) { + params.add(new Pair(param.getName(), 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 (StyleEnum.SPACEDELIMITED.equals(param.getStyle())) { + delimiter = escapeString(" "); + } else if (StyleEnum.PIPEDELIMITED.equals(param.getStyle())) { + delimiter = escapeString("|"); + } + + StringBuilder sb = new StringBuilder(); + for (Object item : value) { + sb.append(delimiter); + sb.append(escapeString(parameterToString(item))); + } + + params.add(new Pair(param.getName(), sb.substring(delimiter.length()))); + + return params; + } + {{/dynamicOperations}} + + /** + * Formats the specified free-form query parameters to a list of {@code Pair} objects. + * + * @param value The free-form query parameters. + * @return A list of {@code Pair} objects. + */ + public List freeFormParameterToPairs(Object value) { + List params = new ArrayList<>(); + + // preconditions + if (value == null || !(value instanceof Map )) { + return params; + } + + @SuppressWarnings("unchecked") + final Map valuesMap = (Map) value; + + for (Map.Entry entry : valuesMap.entrySet()) { + params.add(new Pair(entry.getKey(), parameterToString(entry.getValue()))); + } + + return params; + } + + + /** + * Formats the specified collection path parameter to a string value. + * + * @param collectionFormat The collection format of the parameter. + * @param value The value of the parameter. + * @return String representation of the parameter + */ + public String collectionPathParameterToString(String collectionFormat, Collection value) { + // create the value based on the collection format + if ("multi".equals(collectionFormat)) { + // not valid for path params + return parameterToString(value); + } + + // collectionFormat is assumed to be "csv" by default + String delimiter = ","; + + if ("ssv".equals(collectionFormat)) { + delimiter = " "; + } else if ("tsv".equals(collectionFormat)) { + delimiter = "\t"; + } else if ("pipes".equals(collectionFormat)) { + delimiter = "|"; + } + + StringBuilder sb = new StringBuilder() ; + for (Object item : value) { + sb.append(delimiter); + sb.append(parameterToString(item)); + } + + return sb.substring(delimiter.length()); + } + + /** + * Sanitize filename by removing path. + * e.g. ../../sun.gif becomes sun.gif + * + * @param filename The filename to be sanitized + * @return The sanitized filename + */ + public String sanitizeFilename(String filename) { + return filename.replaceFirst("^.*[/\\\\]", ""); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * "* / *" is also default to JSON + * @param mime MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + 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, + * returns null. If it matches "any", JSON will be used. + */ + public String selectHeaderContentType(String[] contentTypes) { + if (contentTypes.length == 0) { + return null; + } + + if (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 to be escaped + * @return Escaped string + */ + public String escapeString(String str) { + try { + return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); + } catch (UnsupportedEncodingException e) { + return str; + } + } + + /** + * Deserialize response body to Java object, according to the return type and + * the Content-Type response header. + * + * @param Type + * @param response HTTP response + * @param returnType The type of the Java object + * @return The deserialized Java object + * @throws {{invokerPackage}}.ApiException If fail to deserialize response body, i.e. cannot read response body + * or the Content-Type of the response is not supported. + */ + @SuppressWarnings("unchecked") + public T deserialize(Response response, Type returnType) throws ApiException { + if (response == null || returnType == null) { + return null; + } + + if ("byte[]".equals(returnType.toString())) { + // Handle binary response (byte array). + try { + return (T) response.body().bytes(); + } catch (IOException e) { + throw new ApiException(e); + } + } else if (returnType.equals(File.class)) { + // Handle file downloading. + return (T) downloadFileFromResponse(response); + } + + ResponseBody respBody = response.body(); + if (respBody == null) { + return null; + } + + String contentType = response.headers().get("Content-Type"); + if (contentType == null) { + // ensuring a default content type + contentType = "application/json"; + } + try { + if (isJsonMime(contentType)) { + return JSON.deserialize(respBody.byteStream(), returnType); + } else if (returnType.equals(String.class)) { + String respBodyString = respBody.string(); + if (respBodyString.isEmpty()) { + return null; + } + // Expecting string, return the raw response body. + return (T) respBodyString; + } else { + throw new ApiException( + "Content type \"" + contentType + "\" is not supported for type: " + returnType, + response.code(), + response.headers().toMultimap(), + response.body().string()); + } + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * Serialize the given Java object into request body according to the object's + * class and the request Content-Type. + * + * @param obj The Java object + * @param contentType The request Content-Type + * @return The serialized request body + * @throws {{invokerPackage}}.ApiException If fail to serialize the given object + */ + public RequestBody serialize(Object obj, String contentType) throws ApiException { + if (obj instanceof byte[]) { + // Binary (byte array) body parameter support. + return RequestBody.create((byte[]) obj, MediaType.parse(contentType)); + } else if (obj instanceof File) { + // File body parameter support. + return RequestBody.create((File) obj, MediaType.parse(contentType)); + } else if ("text/plain".equals(contentType) && obj instanceof String) { + return RequestBody.create((String) obj, MediaType.parse(contentType)); + } else if (isJsonMime(contentType)) { + String content; + if (obj != null) { + content = JSON.serialize(obj); + } else { + content = null; + } + return RequestBody.create(content, MediaType.parse(contentType)); + } else if (obj instanceof String) { + return RequestBody.create((String) obj, MediaType.parse(contentType)); + } else { + throw new ApiException("Content type \"" + contentType + "\" is not supported"); + } + } + + /** + * Download file from the given response. + * + * @param response An instance of the Response object + * @throws {{invokerPackage}}.ApiException If fail to read file content from response and write to disk + * @return Downloaded file + */ + public File downloadFileFromResponse(Response response) throws ApiException { + try { + File file = prepareDownloadFile(response); + BufferedSink sink = Okio.buffer(Okio.sink(file)); + sink.writeAll(response.body().source()); + sink.close(); + return file; + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * Prepare file for download + * + * @param response An instance of the Response object + * @return Prepared file for the download + * @throws java.io.IOException If fail to prepare file for download + */ + public File prepareDownloadFile(Response response) throws IOException { + String filename = null; + String contentDisposition = response.header("Content-Disposition"); + 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 = sanitizeFilename(matcher.group(1)); + } + } + + String prefix = null; + 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(); + } + + /** + * {@link #execute(Call, Type)} + * + * @param Type + * @param call An instance of the Call object + * @return ApiResponse<T> + * @throws {{invokerPackage}}.ApiException If fail to execute the call + */ + public ApiResponse execute(Call call) throws ApiException { + return execute(call, null); + } + + /** + * Execute HTTP call and deserialize the HTTP response body into the given return type. + * + * @param returnType The return type used to deserialize HTTP response body + * @param The return type corresponding to (same with) returnType + * @param call Call + * @return ApiResponse object containing response status, headers and + * data, which is a Java object deserialized from response body and would be null + * when returnType is null. + * @throws {{invokerPackage}}.ApiException If fail to execute the call + */ + public ApiResponse execute(Call call, Type returnType) throws ApiException { + try { + Response response = call.execute(); + T data = handleResponse(response, returnType); + return new ApiResponse(response.code(), response.headers().toMultimap(), data); + } catch (IOException e) { + throw new ApiException(e); + } + } + + {{#supportStreaming}} + /** + *

Execute stream.

+ * + * @param call a {@link okhttp3.Call} object + * @param returnType a {@link java.lang.reflect.Type} object + * @return a {@link java.io.InputStream} object + * @throws {{invokerPackage}}.ApiException if any. + */ + public InputStream executeStream(Call call, Type returnType) throws ApiException { + try { + Response response = call.execute(); + if (!response.isSuccessful()) { + throw new ApiException(response.code(), response.message(), response.headers().toMultimap(), null); + } + if (response.body() == null) { + return null; + } + return response.body().byteStream(); + } catch (IOException e) { + throw new ApiException(e); + } + } + + {{/supportStreaming}} + /** + * {@link #executeAsync(Call, Type, ApiCallback)} + * + * @param Type + * @param call An instance of the Call object + * @param callback ApiCallback<T> + */ + public void executeAsync(Call call, ApiCallback callback) { + executeAsync(call, null, callback); + } + + /** + * Execute HTTP call asynchronously. + * + * @param Type + * @param call The callback to be executed when the API call finishes + * @param returnType Return type + * @param callback ApiCallback + * @see #execute(Call, Type) + */ + @SuppressWarnings("unchecked") + public void executeAsync(Call call, final Type returnType, final ApiCallback callback) { + call.enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + callback.onFailure(new ApiException(e), 0, null); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + T result; + try { + result = (T) handleResponse(response, returnType); + } catch (ApiException e) { + callback.onFailure(e, response.code(), response.headers().toMultimap()); + return; + } catch (Exception e) { + callback.onFailure(new ApiException(e), response.code(), response.headers().toMultimap()); + return; + } + callback.onSuccess(result, response.code(), response.headers().toMultimap()); + } + }); + } + + /** + * Handle the given response, return the deserialized object when the response is successful. + * + * @param Type + * @param response Response + * @param returnType Return type + * @return Type + * @throws {{invokerPackage}}.ApiException If the response has an unsuccessful status code or + * fail to deserialize the response body + */ + public T handleResponse(Response response, Type returnType) throws ApiException { + if (response.isSuccessful()) { + if (returnType == null || response.code() == 204) { + // returning null if the returnType is not defined, + // or the status code is 204 (No Content) + if (response.body() != null) { + try { + response.body().close(); + } catch (Exception e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + return null; + } else { + return deserialize(response, returnType); + } + } else { + String respBody = null; + if (response.body() != null) { + try { + respBody = response.body().string(); + } catch (IOException e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), respBody); + } + } + + /** + * Build HTTP call with the given options. + * + * @param baseUrl The base URL + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param cookieParams The cookie parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param callback Callback for upload/download progress + * @return The HTTP call + * @throws {{invokerPackage}}.ApiException If fail to serialize the request body object + */ + public Call buildCall(String baseUrl, String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) throws ApiException { + Request request = buildRequest(baseUrl, path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, callback); + + return httpClient.newCall(request); + } + + /** + * Build an HTTP request with the given options. + * + * @param baseUrl The base URL + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param cookieParams The cookie parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param callback Callback for upload/download progress + * @return The HTTP request + * @throws {{invokerPackage}}.ApiException If fail to serialize the request body object + */ + public Request buildRequest(String baseUrl, String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) throws ApiException { + final String url = buildUrl(baseUrl, path, queryParams, collectionQueryParams); + + // prepare HTTP request body + RequestBody reqBody; + String contentType = headerParams.get("Content-Type"); + String contentTypePure = contentType; + if (contentTypePure != null && contentTypePure.contains(";")) { + contentTypePure = contentType.substring(0, contentType.indexOf(";")); + } + if (!HttpMethod.permitsRequestBody(method)) { + reqBody = null; + } else if ("application/x-www-form-urlencoded".equals(contentTypePure)) { + reqBody = buildRequestBodyFormEncoding(formParams); + } else if ("multipart/form-data".equals(contentTypePure)) { + reqBody = buildRequestBodyMultipart(formParams); + } else if (body == null) { + if ("DELETE".equals(method)) { + // allow calling DELETE without sending a request body + reqBody = null; + } else { + // use an empty request body (for POST, PUT and PATCH) + reqBody = RequestBody.create("", contentType == null ? null : MediaType.parse(contentType)); + } + } else { + reqBody = serialize(body, contentType); + } + + List updatedQueryParams = new ArrayList<>(queryParams); + + // update parameters with authentication settings + updateParamsForAuth(authNames, updatedQueryParams, headerParams, cookieParams, requestBodyToString(reqBody), method, URI.create(url)); + + final Request.Builder reqBuilder = new Request.Builder().url(buildUrl(baseUrl, path, updatedQueryParams, collectionQueryParams)); + processHeaderParams(headerParams, reqBuilder); + processCookieParams(cookieParams, reqBuilder); + + // Associate callback with request (if not null) so interceptor can + // access it when creating ProgressResponseBody + reqBuilder.tag(callback); + + Request request = null; + + if (callback != null && reqBody != null) { + ProgressRequestBody progressRequestBody = new ProgressRequestBody(reqBody, callback); + request = reqBuilder.method(method, progressRequestBody).build(); + } else { + request = reqBuilder.method(method, reqBody).build(); + } + + return request; + } + + /** + * Build full URL by concatenating base path, the given sub path and query parameters. + * + * @param baseUrl The base URL + * @param path The sub path + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @return The full URL + */ + public String buildUrl(String baseUrl, String path, List queryParams, List collectionQueryParams) { + final StringBuilder url = new StringBuilder(); + if (baseUrl != null) { + url.append(baseUrl).append(path); + } else { + 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; + } + 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()); + url.append(escapeString(param.getName())).append("=").append(escapeString(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); + } + } + } + + return url.toString(); + } + + /** + * Set header parameters to the request builder, including default headers. + * + * @param headerParams Header parameters in the form of Map + * @param reqBuilder Request.Builder + */ + public void processHeaderParams(Map headerParams, Request.Builder reqBuilder) { + for (Entry param : headerParams.entrySet()) { + reqBuilder.header(param.getKey(), parameterToString(param.getValue())); + } + for (Entry header : defaultHeaderMap.entrySet()) { + if (!headerParams.containsKey(header.getKey())) { + reqBuilder.header(header.getKey(), parameterToString(header.getValue())); + } + } + } + + /** + * Set cookie parameters to the request builder, including default cookies. + * + * @param cookieParams Cookie parameters in the form of Map + * @param reqBuilder Request.Builder + */ + public void processCookieParams(Map cookieParams, Request.Builder reqBuilder) { + for (Entry param : cookieParams.entrySet()) { + reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); + } + for (Entry param : defaultCookieMap.entrySet()) { + if (!cookieParams.containsKey(param.getKey())) { + reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); + } + } + } + + /** + * Update query and header parameters based on authentication settings. + * + * @param authNames The authentications to apply + * @param queryParams List of query parameters + * @param headerParams Map of header parameters + * @param cookieParams Map of cookie parameters + * @param payload HTTP request body + * @param method HTTP method + * @param uri URI + * @throws {{invokerPackage}}.ApiException If fails to update the parameters + */ + public void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams, + Map cookieParams, String payload, String method, URI uri) throws ApiException { + for (String authName : authNames) { + Authentication auth = authentications.get(authName); + if (auth == null) { + throw new RuntimeException("Authentication undefined: " + authName); + } + auth.applyToParams(queryParams, headerParams, cookieParams, payload, method, uri); + } + } + + /** + * Build a form-encoding request body with the given form parameters. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyFormEncoding(Map formParams) { + okhttp3.FormBody.Builder formBuilder = new okhttp3.FormBody.Builder(); + for (Entry param : formParams.entrySet()) { + formBuilder.add(param.getKey(), parameterToString(param.getValue())); + } + return formBuilder.build(); + } + + /** + * Build a multipart (file uploading) request body with the given form parameters, + * which could contain text fields and file fields. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyMultipart(Map formParams) { + MultipartBody.Builder mpBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); + for (Entry param : formParams.entrySet()) { + if (param.getValue() instanceof File) { + File file = (File) param.getValue(); + addPartToMultiPartBuilder(mpBuilder, param.getKey(), file); + } else if (param.getValue() instanceof List) { + List list = (List) param.getValue(); + for (Object item: list) { + if (item instanceof File) { + addPartToMultiPartBuilder(mpBuilder, param.getKey(), (File) item); + } else { + addPartToMultiPartBuilder(mpBuilder, param.getKey(), param.getValue()); + } + } + } else { + addPartToMultiPartBuilder(mpBuilder, param.getKey(), param.getValue()); + } + } + return mpBuilder.build(); + } + + /** + * Guess Content-Type header from the given file (defaults to "application/octet-stream"). + * + * @param file The given file + * @return The guessed Content-Type + */ + public String guessContentTypeFromFile(File file) { + String contentType = URLConnection.guessContentTypeFromName(file.getName()); + if (contentType == null) { + return "application/octet-stream"; + } else { + return contentType; + } + } + + /** + * Add a Content-Disposition Header for the given key and file to the MultipartBody Builder. + * + * @param mpBuilder MultipartBody.Builder + * @param key The key of the Header element + * @param file The file to add to the Header + */ + protected void addPartToMultiPartBuilder(MultipartBody.Builder mpBuilder, String key, File file) { + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + key + "\"; filename=\"" + file.getName() + "\""); + MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file)); + mpBuilder.addPart(partHeaders, RequestBody.create(file, mediaType)); + } + + /** + * Add a Content-Disposition Header for the given key and complex object to the MultipartBody Builder. + * + * @param mpBuilder MultipartBody.Builder + * @param key The key of the Header element + * @param obj The complex object to add to the Header + */ + protected void addPartToMultiPartBuilder(MultipartBody.Builder mpBuilder, String key, Object obj) { + RequestBody requestBody; + if (obj instanceof String) { + requestBody = RequestBody.create((String) obj, MediaType.parse("text/plain")); + } else { + String content; + if (obj != null) { + content = JSON.serialize(obj); + } else { + content = null; + } + requestBody = RequestBody.create(content, MediaType.parse("application/json")); + } + + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + key + "\""); + mpBuilder.addPart(partHeaders, requestBody); + } + + /** + * Get network interceptor to add it to the httpClient to track download progress for + * async requests. + */ + protected Interceptor getProgressInterceptor() { + return new Interceptor() { + @Override + public Response intercept(Interceptor.Chain chain) throws IOException { + final Request request = chain.request(); + final Response originalResponse = chain.proceed(request); + if (request.tag() instanceof ApiCallback) { + final ApiCallback callback = (ApiCallback) request.tag(); + return originalResponse.newBuilder() + .body(new ProgressResponseBody(originalResponse.body(), callback)) + .build(); + } + return originalResponse; + } + }; + } + + /** + * Apply SSL related settings to httpClient according to the current values of + * verifyingSsl and sslCaCert. + */ + protected void applySslSettings() { + try { + TrustManager[] trustManagers; + HostnameVerifier hostnameVerifier; + if (!verifyingSsl) { + trustManagers = new TrustManager[]{ + new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + } + }; + hostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + } else { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + + if (sslCaCert == null) { + trustManagerFactory.init((KeyStore) null); + } else { + char[] password = null; // Any password will work. + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + Collection certificates = certificateFactory.generateCertificates(sslCaCert); + if (certificates.isEmpty()) { + throw new IllegalArgumentException("expected non-empty set of trusted certificates"); + } + KeyStore caKeyStore = newEmptyKeyStore(password); + int index = 0; + for (Certificate certificate : certificates) { + String certificateAlias = "ca" + (index++); + caKeyStore.setCertificateEntry(certificateAlias, certificate); + } + trustManagerFactory.init(caKeyStore); + } + trustManagers = trustManagerFactory.getTrustManagers(); + hostnameVerifier = OkHostnameVerifier.INSTANCE; + } + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, new SecureRandom()); + httpClient = httpClient.newBuilder() + .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0]) + .hostnameVerifier(hostnameVerifier) + .build(); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + protected KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { + try { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, password); + return keyStore; + } catch (IOException e) { + throw new AssertionError(e); + } + } + {{#dynamicOperations}} + + public ApiClient createOperationLookupMap(OpenAPI openAPI) { + operationLookupMap = new HashMap<>(); + for (Map.Entry pathItemEntry : openAPI.getPaths().entrySet()) { + String path = pathItemEntry.getKey(); + PathItem pathItem = pathItemEntry.getValue(); + addOperationLookupEntry(path, "GET", pathItem.getGet()); + addOperationLookupEntry(path, "PUT", pathItem.getPut()); + addOperationLookupEntry(path, "POST", pathItem.getPost()); + addOperationLookupEntry(path, "DELETE", pathItem.getDelete()); + addOperationLookupEntry(path, "OPTIONS", pathItem.getOptions()); + addOperationLookupEntry(path, "HEAD", pathItem.getHead()); + addOperationLookupEntry(path, "PATCH", pathItem.getPatch()); + addOperationLookupEntry(path, "TRACE", pathItem.getTrace()); + } + return this; + } + + protected void addOperationLookupEntry(String path, String method, Operation operation) { + if ( operation != null && operation.getOperationId() != null) { + operationLookupMap.put( + operation.getOperationId(), + new ApiOperation(path, method, operation)); + } + } + + public Map getOperationLookupMap() { + return operationLookupMap; + } + + public String fillParametersFromOperation( + Operation operation, + Map paramMap, + String path, + List queryParams, + List collectionQueryParams, + Map headerParams, + Map cookieParams + ) { + for (Map.Entry entry : paramMap.entrySet()) { + Object value = entry.getValue(); + for (Parameter param : operation.getParameters()) { + if (entry.getKey().equals(param.getName())) { + switch (param.getIn()) { + case "path": + path = path.replace("{" + param.getName() + "}", escapeString(value.toString())); + break; + case "query": + if (value instanceof Collection) { + collectionQueryParams.addAll(parameterToPairs(param, (Collection) value)); + } else { + queryParams.addAll(parameterToPair(param.getName(), value)); + } + break; + case "header": + headerParams.put(param.getName(), parameterToString(value)); + break; + case "cookie": + cookieParams.put(param.getName(), parameterToString(value)); + break; + default: + throw new IllegalStateException("Unexpected param in: " + param.getIn()); + } + + } + } + } + return path; + } + {{/dynamicOperations}} + + /** + * Convert the HTTP request body to a string. + * + * @param requestBody The HTTP request object + * @return The string representation of the HTTP request body + * @throws {{invokerPackage}}.ApiException If fail to serialize the request body object into a string + */ + protected String requestBodyToString(RequestBody requestBody) throws ApiException { + // Payload is only used by some auth schemes; for large/binary uploads we must not buffer. + if (requestBody == null) { + return ""; + } + + // If you *know* none of your auth implementations use payload, you can simply: + // return ""; + + // Otherwise, keep a safe, capped implementation: + try { + final MediaType contentType = requestBody.contentType(); + final long maxBytes = 64L * 1024L; // 64KiB cap + + // Do not attempt to stringify binary bodies. + if (contentType != null) { + final String type = contentType.type(); + final String subtype = contentType.subtype(); + final boolean textLike = + "text".equalsIgnoreCase(type) || + (subtype != null && ( + subtype.toLowerCase().contains("json") || + subtype.toLowerCase().contains("xml") || + subtype.toLowerCase().contains("x-www-form-urlencoded") + )); + if (!textLike) return ""; + } + + final long declaredLength = requestBody.contentLength(); + if (declaredLength > maxBytes) { + return ""; + } + + final Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + + final long toRead = Math.min(buffer.size(), maxBytes); + final Charset charset = + (contentType != null && contentType.charset() != null) ? contentType.charset() : StandardCharsets.UTF_8; + + return buffer.readString(toRead, charset); + } catch (final IOException e) { + throw new ApiException(e); + } + } +} From c0af28a4ea516fa0721baac932a3ac10070e1117 Mon Sep 17 00:00:00 2001 From: Anders Sjoblom Date: Thu, 5 Feb 2026 10:57:32 -0500 Subject: [PATCH 2/2] Added patch support instead of carry modifiled source files --- buildSrc/build.gradle | 11 +- .../groovy/vertex.openapi-generator.gradle | 45 +- .../resources/vertex-java/ApiClient.mustache | 1971 ----------------- .../vertex-java/oneof_model.mustache | 537 ----- .../main/resources/vertex-java/pojo.mustache | 602 ----- gradle.properties | 1 + .../patches/ApiClient.mustache.patch | 70 + .../patches/oneof_model.mustache.patch | 41 + .../resources/patches/pojo.mustache.patch | 89 + .../main/resources/vertex-java/pojo.mustache | 601 ----- 10 files changed, 254 insertions(+), 3714 deletions(-) delete mode 100644 buildSrc/src/main/resources/vertex-java/ApiClient.mustache delete mode 100644 buildSrc/src/main/resources/vertex-java/oneof_model.mustache delete mode 100644 buildSrc/src/main/resources/vertex-java/pojo.mustache create mode 100644 gradle.properties create mode 100644 openapi-generator-plugin/src/main/resources/patches/ApiClient.mustache.patch create mode 100644 openapi-generator-plugin/src/main/resources/patches/oneof_model.mustache.patch create mode 100644 openapi-generator-plugin/src/main/resources/patches/pojo.mustache.patch delete mode 100644 openapi-generator-plugin/src/main/resources/vertex-java/pojo.mustache diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index d3ce4e0..98b4e95 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -7,9 +7,16 @@ repositories { mavenCentral() } +def rootProperties = new Properties() +def rootPropertiesFile = file("../gradle.properties") +if (rootPropertiesFile.exists()) { + rootPropertiesFile.withInputStream { rootProperties.load(it) } +} +def openapiGeneratorVersion = rootProperties.getProperty("openapiGeneratorVersion", "7.14.0") + dependencies { - implementation 'org.openapitools:openapi-generator-gradle-plugin:7.14.0' - implementation 'org.openapitools:openapi-generator:7.14.0' + implementation "org.openapitools:openapi-generator-gradle-plugin:${openapiGeneratorVersion}" + implementation "org.openapitools:openapi-generator:${openapiGeneratorVersion}" implementation 'io.swagger.core.v3:swagger-models:2.2.31' implementation 'io.github.gradle-nexus:publish-plugin:2.0.0' } diff --git a/buildSrc/src/main/groovy/vertex.openapi-generator.gradle b/buildSrc/src/main/groovy/vertex.openapi-generator.gradle index 9668f66..750602d 100644 --- a/buildSrc/src/main/groovy/vertex.openapi-generator.gradle +++ b/buildSrc/src/main/groovy/vertex.openapi-generator.gradle @@ -7,6 +7,48 @@ repositories { mavenCentral() } +def vertexOpenapiGeneratorVersion = project.findProperty("openapiGeneratorVersion") ?: "7.14.0" +def templateRootDir = "${buildDir}/openapi-templates" +def templateDirPath = "${templateRootDir}/Java/libraries/okhttp-gson" +def patchDirPath = "${project.rootDir}/openapi-generator-plugin/src/main/resources/patches" + +configurations { + vertexOpenapiGeneratorTemplates +} + +dependencies { + vertexOpenapiGeneratorTemplates("org.openapitools:openapi-generator:${vertexOpenapiGeneratorVersion}") { + transitive = false + } +} + +tasks.register("prepareOpenApiTemplates") { + outputs.dir(templateRootDir) + doLast { + def generatorJar = configurations.vertexOpenapiGeneratorTemplates.singleFile + copy { + from(zipTree(generatorJar)) + include "Java/libraries/okhttp-gson/**" + into templateRootDir + } + def patchDir = file(patchDirPath) + if (!patchDir.exists()) { + throw new GradleException("Patch directory not found at ${patchDir}") + } + + def patches = fileTree(patchDir).matching { include "*.patch" }.files.sort { it.name } + patches.each { patchFile -> + exec { + workingDir = file(templateDirPath) + commandLine "patch", "--batch", "--forward", "--reject-file=-", "-p0", "-i", patchFile.absolutePath + } + } + + def relPath = project.rootDir.toPath().relativize(file(templateDirPath).toPath()).toString() + println "OpenAPI templates prepared at: ${relPath}" + } +} + openApiGenerate { verbose = false generatorName = 'vertex-java' // Use our custom generator @@ -18,7 +60,7 @@ openApiGenerate { invokerPackage = 'com.vertexvis' modelPackage = 'com.vertexvis.model' apiPackage = 'com.vertexvis.api' - templateDir = "${project.rootDir}/buildSrc/src/main/resources/vertex-java" + templateDir = templateDirPath configOptions = [ openApiNullable: "false", dateLibrary: "java8", @@ -46,3 +88,4 @@ sourceSets { // Make sure compilation depends on code generation compileJava.dependsOn tasks.openApiGenerate +tasks.openApiGenerate.dependsOn tasks.prepareOpenApiTemplates diff --git a/buildSrc/src/main/resources/vertex-java/ApiClient.mustache b/buildSrc/src/main/resources/vertex-java/ApiClient.mustache deleted file mode 100644 index 697776d..0000000 --- a/buildSrc/src/main/resources/vertex-java/ApiClient.mustache +++ /dev/null @@ -1,1971 +0,0 @@ -{{>licenseInfo}} - -package {{invokerPackage}}; - -{{#dynamicOperations}} -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.PathItem; -import io.swagger.v3.oas.models.parameters.Parameter; -import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; -import io.swagger.v3.parser.OpenAPIV3Parser; -{{/dynamicOperations}} -import okhttp3.*; -import okhttp3.internal.http.HttpMethod; -import okhttp3.internal.tls.OkHostnameVerifier; -import okhttp3.logging.HttpLoggingInterceptor; -import okhttp3.logging.HttpLoggingInterceptor.Level; -import okio.Buffer; -import okio.BufferedSink; -import okio.Okio; -{{#joda}} -import org.joda.time.DateTime; -import org.joda.time.LocalDate; -import org.joda.time.format.DateTimeFormatter; -{{/joda}} -{{#hasOAuthMethods}} -import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; -import org.apache.oltu.oauth2.common.message.types.GrantType; -{{/hasOAuthMethods}} - -import javax.net.ssl.*; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Type; -import java.net.URI; -import java.net.URLConnection; -import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.SecureRandom; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.text.DateFormat; -{{#jsr310}} -import java.time.LocalDate; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; -{{/jsr310}} -import java.util.*; -import java.util.Map.Entry; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import {{invokerPackage}}.auth.Authentication; -import {{invokerPackage}}.auth.HttpBasicAuth; -import {{invokerPackage}}.auth.HttpBearerAuth; -import {{invokerPackage}}.auth.ApiKeyAuth; -{{#hasOAuthMethods}} -import {{invokerPackage}}.auth.OAuth; -import {{invokerPackage}}.auth.RetryingOAuth; -import {{invokerPackage}}.auth.OAuthFlow; -{{/hasOAuthMethods}} -{{#withAWSV4Signature}} -import {{invokerPackage}}.auth.AWS4Auth; -{{/withAWSV4Signature}} - -/** - *

ApiClient class.

- */ -public class ApiClient { - - protected 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; - protected boolean debugging = false; - protected Map defaultHeaderMap = new HashMap(); - protected Map defaultCookieMap = new HashMap(); - protected String tempFolderPath = null; - - protected Map authentications; - - protected DateFormat dateFormat; - protected DateFormat datetimeFormat; - protected boolean lenientDatetimeFormat; - protected int dateLength; - - protected InputStream sslCaCert; - protected boolean verifyingSsl; - protected KeyManager[] keyManagers; - - protected OkHttpClient httpClient; - protected JSON json; - - protected HttpLoggingInterceptor loggingInterceptor; - - {{#dynamicOperations}} - protected Map operationLookupMap = new HashMap<>(); - - {{/dynamicOperations}} - /** - * Basic constructor for ApiClient - */ - public ApiClient() { - init(); - initHttpClient(); - - // Setup authentications (key: authentication name, value: authentication).{{#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}}{{#isKeyInQuery}}"query"{{/isKeyInQuery}}{{#isKeyInCookie}}"cookie"{{/isKeyInCookie}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}} - authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}}{{#withAWSV4Signature}} - authentications.put("AWS4Auth", new AWS4Auth());{{/withAWSV4Signature}} - // Prevent the authentications from being modified. - authentications = Collections.unmodifiableMap(authentications); - } - - /** - * Basic constructor with custom OkHttpClient - * - * @param client a {@link okhttp3.OkHttpClient} object - */ - public ApiClient(OkHttpClient client) { - init(); - - httpClient = client; - - // Setup authentications (key: authentication name, value: authentication).{{#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}}{{#isKeyInQuery}}"query"{{/isKeyInQuery}}{{#isKeyInCookie}}"cookie"{{/isKeyInCookie}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}} - authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}}{{#withAWSV4Signature}} - authentications.put("AWS4Auth", new AWS4Auth());{{/withAWSV4Signature}} - // Prevent the authentications from being modified. - authentications = Collections.unmodifiableMap(authentications); - } - - {{#hasOAuthMethods}} - {{#oauthMethods}} - {{#-first}} - /** - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID - * - * @param clientId client ID - */ - public ApiClient(String clientId) { - this(clientId, null, null); - } - - /** - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID and additional parameters - * - * @param clientId client ID - * @param parameters a {@link java.util.Map} of parameters - */ - public ApiClient(String clientId, Map parameters) { - this(clientId, null, parameters); - } - - /** - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID, secret, and additional parameters - * - * @param clientId client ID - * @param clientSecret client secret - * @param parameters a {@link java.util.Map} of parameters - */ - public ApiClient(String clientId, String clientSecret, Map parameters) { - this(null, clientId, clientSecret, parameters); - } - - /** - * Constructor for ApiClient to support access token retry on 401/403 configured with base path, client ID, secret, and additional parameters - * - * @param basePath base path - * @param clientId client ID - * @param clientSecret client secret - * @param parameters a {@link java.util.Map} of parameters - */ - public ApiClient(String basePath, String clientId, String clientSecret, Map parameters) { - init(); - if (basePath != null) { - this.basePath = basePath; - } - - String tokenUrl = "{{{tokenUrl}}}"; - if (!"".equals(tokenUrl) && !URI.create(tokenUrl).isAbsolute()) { - URI uri = URI.create(getBasePath()); - tokenUrl = uri.getScheme() + ":" + - (uri.getAuthority() != null ? "//" + uri.getAuthority() : "") + - tokenUrl; - if (!URI.create(tokenUrl).isAbsolute()) { - throw new IllegalArgumentException("OAuth2 token URL must be an absolute URL"); - } - } - RetryingOAuth retryingOAuth = new RetryingOAuth(tokenUrl, clientId, OAuthFlow.{{#lambda.uppercase}}{{#lambda.snakecase}}{{flow}}{{/lambda.snakecase}}{{/lambda.uppercase}}, clientSecret, parameters); - authentications.put( - "{{name}}", - retryingOAuth - ); - initHttpClient(Collections.singletonList(retryingOAuth)); - // Setup authentications (key: authentication name, value: authentication).{{#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}}{{#isKeyInQuery}}"query"{{/isKeyInQuery}}{{#isKeyInCookie}}"cookie"{{/isKeyInCookie}}, "{{keyParamName}}"));{{/isApiKey}}{{/authMethods}}{{#withAWSV4Signature}} - authentications.put("AWS4Auth", new AWS4Auth());{{/withAWSV4Signature}} - - // Prevent the authentications from being modified. - authentications = Collections.unmodifiableMap(authentications); - } - - {{/-first}} - {{/oauthMethods}} - {{/hasOAuthMethods}} - protected void initHttpClient() { - initHttpClient(Collections.emptyList()); - } - - protected void initHttpClient(List interceptors) { - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - builder.addNetworkInterceptor(getProgressInterceptor()); - for (Interceptor interceptor: interceptors) { - builder.addInterceptor(interceptor); - } - {{#useGzipFeature}} - // Enable gzip request compression - builder.addInterceptor(new GzipRequestInterceptor()); - {{/useGzipFeature}} - - httpClient = builder.build(); - } - - protected void init() { - verifyingSsl = true; - - json = new JSON(); - - // Set default User-Agent. - setUserAgent("{{{httpUserAgent}}}{{^httpUserAgent}}OpenAPI-Generator/{{{artifactVersion}}}/java{{/httpUserAgent}}"); - - authentications = new HashMap(); - {{#dynamicOperations}} - - OpenAPI openAPI = new OpenAPIV3Parser().read("openapi/openapi.yaml"); - createOperationLookupMap(openAPI); - {{/dynamicOperations}} - } - - /** - * Get base path - * - * @return Base path - */ - public String getBasePath() { - return basePath; - } - - /** - * Set base path - * - * @param basePath Base path of the URL (e.g {{{basePath}}} - * @return An instance of OkHttpClient - */ - public ApiClient setBasePath(String basePath) { - this.basePath = basePath; - this.serverIndex = null; - return this; - } - - public List getServers() { - return servers; - } - - public ApiClient setServers(List servers) { - this.servers = servers; - return this; - } - - public Integer getServerIndex() { - return serverIndex; - } - - public ApiClient setServerIndex(Integer serverIndex) { - this.serverIndex = serverIndex; - return this; - } - - public Map getServerVariables() { - return serverVariables; - } - - public ApiClient setServerVariables(Map serverVariables) { - this.serverVariables = serverVariables; - return this; - } - - /** - * Get HTTP client - * - * @return An instance of OkHttpClient - */ - public OkHttpClient getHttpClient() { - return httpClient; - } - - /** - * Set HTTP client, which must never be null. - * - * @param newHttpClient An instance of OkHttpClient - * @return Api Client - * @throws java.lang.NullPointerException when newHttpClient is null - */ - public ApiClient setHttpClient(OkHttpClient newHttpClient) { - this.httpClient = Objects.requireNonNull(newHttpClient, "HttpClient must not be null!"); - return this; - } - - /** - * Get JSON - * - * @return JSON object - */ - public JSON getJSON() { - return json; - } - - /** - * Set JSON - * - * @param json JSON object - * @return Api client - */ - public ApiClient setJSON(JSON json) { - this.json = json; - return this; - } - - /** - * True if isVerifyingSsl flag is on - * - * @return True if isVerifySsl flag is on - */ - public boolean isVerifyingSsl() { - return verifyingSsl; - } - - /** - * Configure whether to verify certificate and hostname when making https requests. - * Default to true. - * NOTE: Do NOT set to false in production code, otherwise you would face multiple types of cryptographic attacks. - * - * @param verifyingSsl True to verify TLS/SSL connection - * @return ApiClient - */ - public ApiClient setVerifyingSsl(boolean verifyingSsl) { - this.verifyingSsl = verifyingSsl; - applySslSettings(); - return this; - } - - /** - * Get SSL CA cert. - * - * @return Input stream to the SSL CA cert - */ - public InputStream getSslCaCert() { - return sslCaCert; - } - - /** - * Configure the CA certificate to be trusted when making https requests. - * Use null to reset to default. - * - * @param sslCaCert input stream for SSL CA cert - * @return ApiClient - */ - public ApiClient setSslCaCert(InputStream sslCaCert) { - this.sslCaCert = sslCaCert; - applySslSettings(); - return this; - } - - /** - *

Getter for the field keyManagers.

- * - * @return an array of {@link javax.net.ssl.KeyManager} objects - */ - public KeyManager[] getKeyManagers() { - return keyManagers; - } - - /** - * Configure client keys to use for authorization in an SSL session. - * Use null to reset to default. - * - * @param managers The KeyManagers to use - * @return ApiClient - */ - public ApiClient setKeyManagers(KeyManager[] managers) { - this.keyManagers = managers; - applySslSettings(); - return this; - } - - /** - *

Getter for the field dateFormat.

- * - * @return a {@link java.text.DateFormat} object - */ - public DateFormat getDateFormat() { - return dateFormat; - } - - /** - *

Setter for the field dateFormat.

- * - * @param dateFormat a {@link java.text.DateFormat} object - * @return a {@link {{invokerPackage}}.ApiClient} object - */ - public ApiClient setDateFormat(DateFormat dateFormat) { - JSON.setDateFormat(dateFormat); - return this; - } - - /** - *

Set SqlDateFormat.

- * - * @param dateFormat a {@link java.text.DateFormat} object - * @return a {@link {{invokerPackage}}.ApiClient} object - */ - public ApiClient setSqlDateFormat(DateFormat dateFormat) { - JSON.setSqlDateFormat(dateFormat); - return this; - } - - {{#joda}} - public ApiClient setDateTimeFormat(DateTimeFormatter dateFormat) { - JSON.setDateTimeFormat(dateFormat); - return this; - } - - public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { - JSON.setLocalDateFormat(dateFormat); - return this; - } - - {{/joda}} - {{#jsr310}} - /** - *

Set OffsetDateTimeFormat.

- * - * @param dateFormat a {@link java.time.format.DateTimeFormatter} object - * @return a {@link {{invokerPackage}}.ApiClient} object - */ - public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { - JSON.setOffsetDateTimeFormat(dateFormat); - return this; - } - - /** - *

Set LocalDateFormat.

- * - * @param dateFormat a {@link java.time.format.DateTimeFormatter} object - * @return a {@link {{invokerPackage}}.ApiClient} object - */ - public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { - JSON.setLocalDateFormat(dateFormat); - return this; - } - - {{/jsr310}} - /** - *

Set LenientOnJson.

- * - * @param lenientOnJson a boolean - * @return a {@link {{invokerPackage}}.ApiClient} object - */ - public ApiClient setLenientOnJson(boolean lenientOnJson) { - JSON.setLenientOnJson(lenientOnJson); - return this; - } - - /** - * Get authentications (key: authentication name, value: authentication). - * - * @return Map of authentication objects - */ - 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); - } - - {{#hasHttpBearerMethods}} - /** - * Helper method to set access token for the first Bearer authentication. - * @param bearerToken Bearer token - */ - public void setBearerToken(String bearerToken) { - setBearerToken(() -> bearerToken); - } - - /** - * Helper method to set the supplier of access tokens for Bearer authentication. - * - * @param tokenSupplier The supplier of bearer tokens - */ - 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}} - - /** - * Helper method to set username for the first HTTP basic authentication. - * - * @param username Username - */ - public void setUsername(String username) { - for (Authentication auth : authentications.values()) { - if (auth instanceof HttpBasicAuth) { - ((HttpBasicAuth) auth).setUsername(username); - return; - } - } - throw new RuntimeException("No HTTP basic authentication configured!"); - } - - /** - * Helper method to set password for the first HTTP basic authentication. - * - * @param password Password - */ - public void setPassword(String password) { - for (Authentication auth : authentications.values()) { - if (auth instanceof HttpBasicAuth) { - ((HttpBasicAuth) auth).setPassword(password); - return; - } - } - throw new RuntimeException("No HTTP basic authentication configured!"); - } - - /** - * Helper method to set API key value for the first API key authentication. - * - * @param apiKey API key - */ - public void setApiKey(String apiKey) { - for (Authentication auth : authentications.values()) { - if (auth instanceof ApiKeyAuth) { - ((ApiKeyAuth) auth).setApiKey(apiKey); - return; - } - } - 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 - */ - public void setApiKeyPrefix(String apiKeyPrefix) { - for (Authentication auth : authentications.values()) { - if (auth instanceof ApiKeyAuth) { - ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); - return; - } - } - throw new RuntimeException("No API key authentication configured!"); - } - - /** - * Helper method to set access token for the first OAuth2 authentication. - * - * @param accessToken Access token - */ - public void setAccessToken(String accessToken) { - {{#hasOAuthMethods}} - for (Authentication auth : authentications.values()) { - if (auth instanceof OAuth) { - ((OAuth) auth).setAccessToken(accessToken); - return; - } - } - {{/hasOAuthMethods}} - throw new RuntimeException("No OAuth2 authentication configured!"); - } - - /** - * Helper method to set credentials for AWSV4 Signature - * - * @param accessKey Access Key - * @param secretKey Secret Key - * @param region Region - * @param service Service to access to - */ - public void setAWS4Configuration(String accessKey, String secretKey, String region, String service) { - {{#withAWSV4Signature}} - for (Authentication auth : authentications.values()) { - if (auth instanceof AWS4Auth) { - ((AWS4Auth) auth).setCredentials(accessKey, secretKey); - ((AWS4Auth) auth).setRegion(region); - ((AWS4Auth) auth).setService(service); - return; - } - } - {{/withAWSV4Signature}} - throw new RuntimeException("No AWS4 authentication configured!"); - } - - /** - * Helper method to set credentials for AWSV4 Signature - * - * @param accessKey Access Key - * @param secretKey Secret Key - * @param sessionToken Session Token - * @param region Region - * @param service Service to access to - */ - public void setAWS4Configuration(String accessKey, String secretKey, String sessionToken, String region, String service) { - {{#withAWSV4Signature}} - for (Authentication auth : authentications.values()) { - if (auth instanceof AWS4Auth) { - ((AWS4Auth) auth).setCredentials(accessKey, secretKey, sessionToken); - ((AWS4Auth) auth).setRegion(region); - ((AWS4Auth) auth).setService(service); - return; - } - } - {{/withAWSV4Signature}} - throw new RuntimeException("No AWS4 authentication configured!"); - } - - /** - * Set the User-Agent header's value (by adding to the default header map). - * - * @param userAgent HTTP request's user agent - * @return ApiClient - */ - public ApiClient setUserAgent(String userAgent) { - addDefaultHeader("User-Agent", userAgent); - return this; - } - - /** - * Add a default header. - * - * @param key The header's key - * @param value The header's value - * @return ApiClient - */ - 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 ApiClient - */ - 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 enabled, false otherwise. - */ - public boolean isDebugging() { - return debugging; - } - - /** - * Enable/disable debugging for this API client. - * - * @param debugging To enable (true) or disable (false) debugging - * @return ApiClient - */ - public ApiClient setDebugging(boolean debugging) { - if (debugging != this.debugging) { - if (debugging) { - loggingInterceptor = new HttpLoggingInterceptor(); - loggingInterceptor.setLevel(Level.BODY); - httpClient = httpClient.newBuilder().addInterceptor(loggingInterceptor).build(); - } else { - final OkHttpClient.Builder builder = httpClient.newBuilder(); - builder.interceptors().remove(loggingInterceptor); - httpClient = builder.build(); - loggingInterceptor = null; - } - } - this.debugging = debugging; - return this; - } - - /** - * 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. - * - * @see createTempFile - * @return Temporary folder path - */ - public String getTempFolderPath() { - return tempFolderPath; - } - - /** - * Set the temporary folder path (for downloading files) - * - * @param tempFolderPath Temporary folder path - * @return ApiClient - */ - public ApiClient setTempFolderPath(String tempFolderPath) { - this.tempFolderPath = tempFolderPath; - return this; - } - - /** - * Get connection timeout (in milliseconds). - * - * @return Timeout in milliseconds - */ - public int getConnectTimeout() { - return httpClient.connectTimeoutMillis(); - } - - /** - * Sets the connect timeout (in milliseconds). - * A value of 0 means no timeout, otherwise values must be between 1 and - * {@link java.lang.Integer#MAX_VALUE}. - * - * @param connectionTimeout connection timeout in milliseconds - * @return Api client - */ - public ApiClient setConnectTimeout(int connectionTimeout) { - httpClient = httpClient.newBuilder().connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS).build(); - return this; - } - - /** - * Get read timeout (in milliseconds). - * - * @return Timeout in milliseconds - */ - public int getReadTimeout() { - return httpClient.readTimeoutMillis(); - } - - /** - * Sets the read timeout (in milliseconds). - * A value of 0 means no timeout, otherwise values must be between 1 and - * {@link java.lang.Integer#MAX_VALUE}. - * - * @param readTimeout read timeout in milliseconds - * @return Api client - */ - public ApiClient setReadTimeout(int readTimeout) { - httpClient = httpClient.newBuilder().readTimeout(readTimeout, TimeUnit.MILLISECONDS).build(); - return this; - } - - /** - * Get write timeout (in milliseconds). - * - * @return Timeout in milliseconds - */ - public int getWriteTimeout() { - return httpClient.writeTimeoutMillis(); - } - - /** - * Sets the write timeout (in milliseconds). - * A value of 0 means no timeout, otherwise values must be between 1 and - * {@link java.lang.Integer#MAX_VALUE}. - * - * @param writeTimeout connection timeout in milliseconds - * @return Api client - */ - public ApiClient setWriteTimeout(int writeTimeout) { - httpClient = httpClient.newBuilder().writeTimeout(writeTimeout, TimeUnit.MILLISECONDS).build(); - return this; - } - - {{#hasOAuthMethods}} - /** - * Helper method to configure the token endpoint of the first oauth found in the apiAuthorizations (there should be only one) - * - * @return Token request builder - */ - public TokenRequestBuilder getTokenEndPoint() { - for (Authentication apiAuth : authentications.values()) { - if (apiAuth instanceof RetryingOAuth) { - RetryingOAuth retryingOAuth = (RetryingOAuth) apiAuth; - return retryingOAuth.getTokenRequestBuilder(); - } - } - return null; - } - {{/hasOAuthMethods}} - - /** - * Format the given parameter object into string. - * - * @param param Parameter - * @return String representation of the parameter - */ - public String parameterToString(Object param) { - if (param == null) { - return ""; - } else if (param instanceof Date {{#joda}}|| param instanceof DateTime || param instanceof LocalDate{{/joda}}{{#jsr310}}|| param instanceof OffsetDateTime || param instanceof LocalDate{{/jsr310}}) { - //Serialize to json string and remove the " enclosing characters - String jsonStr = JSON.serialize(param); - return jsonStr.substring(1, jsonStr.length() - 1); - } else if (param instanceof Collection) { - StringBuilder b = new StringBuilder(); - for (Object o : (Collection) param) { - if (b.length() > 0) { - b.append(","); - } - b.append(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, parameterToString(value))); - return params; - } - - {{^dynamicOperations}} - /** - * 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; - } - {{/dynamicOperations}} - {{#dynamicOperations}} - public List parameterToPairs(Parameter param, Collection value) { - List params = new ArrayList(); - - // preconditions - if (param == null || param.getName() == null || param.getName().isEmpty() || value == null || value.isEmpty()) { - return params; - } - - // create the params based on the collection format - if (StyleEnum.FORM.equals(param.getStyle()) && Boolean.TRUE.equals(param.getExplode())) { - for (Object item : value) { - params.add(new Pair(param.getName(), 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 (StyleEnum.SPACEDELIMITED.equals(param.getStyle())) { - delimiter = escapeString(" "); - } else if (StyleEnum.PIPEDELIMITED.equals(param.getStyle())) { - delimiter = escapeString("|"); - } - - StringBuilder sb = new StringBuilder(); - for (Object item : value) { - sb.append(delimiter); - sb.append(escapeString(parameterToString(item))); - } - - params.add(new Pair(param.getName(), sb.substring(delimiter.length()))); - - return params; - } - {{/dynamicOperations}} - - /** - * Formats the specified free-form query parameters to a list of {@code Pair} objects. - * - * @param value The free-form query parameters. - * @return A list of {@code Pair} objects. - */ - public List freeFormParameterToPairs(Object value) { - List params = new ArrayList<>(); - - // preconditions - if (value == null || !(value instanceof Map )) { - return params; - } - - @SuppressWarnings("unchecked") - final Map valuesMap = (Map) value; - - for (Map.Entry entry : valuesMap.entrySet()) { - params.add(new Pair(entry.getKey(), parameterToString(entry.getValue()))); - } - - return params; - } - - - /** - * Formats the specified collection path parameter to a string value. - * - * @param collectionFormat The collection format of the parameter. - * @param value The value of the parameter. - * @return String representation of the parameter - */ - public String collectionPathParameterToString(String collectionFormat, Collection value) { - // create the value based on the collection format - if ("multi".equals(collectionFormat)) { - // not valid for path params - return parameterToString(value); - } - - // collectionFormat is assumed to be "csv" by default - String delimiter = ","; - - if ("ssv".equals(collectionFormat)) { - delimiter = " "; - } else if ("tsv".equals(collectionFormat)) { - delimiter = "\t"; - } else if ("pipes".equals(collectionFormat)) { - delimiter = "|"; - } - - StringBuilder sb = new StringBuilder() ; - for (Object item : value) { - sb.append(delimiter); - sb.append(parameterToString(item)); - } - - return sb.substring(delimiter.length()); - } - - /** - * Sanitize filename by removing path. - * e.g. ../../sun.gif becomes sun.gif - * - * @param filename The filename to be sanitized - * @return The sanitized filename - */ - public String sanitizeFilename(String filename) { - return filename.replaceFirst("^.*[/\\\\]", ""); - } - - /** - * Check if the given MIME is a JSON MIME. - * JSON MIME examples: - * application/json - * application/json; charset=UTF8 - * APPLICATION/JSON - * application/vnd.company+json - * "* / *" is also default to JSON - * @param mime MIME (Multipurpose Internet Mail Extensions) - * @return True if the given MIME is JSON, false otherwise. - */ - 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, - * returns null. If it matches "any", JSON will be used. - */ - public String selectHeaderContentType(String[] contentTypes) { - if (contentTypes.length == 0) { - return null; - } - - if (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 to be escaped - * @return Escaped string - */ - public String escapeString(String str) { - try { - return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); - } catch (UnsupportedEncodingException e) { - return str; - } - } - - /** - * Deserialize response body to Java object, according to the return type and - * the Content-Type response header. - * - * @param Type - * @param response HTTP response - * @param returnType The type of the Java object - * @return The deserialized Java object - * @throws {{invokerPackage}}.ApiException If fail to deserialize response body, i.e. cannot read response body - * or the Content-Type of the response is not supported. - */ - @SuppressWarnings("unchecked") - public T deserialize(Response response, Type returnType) throws ApiException { - if (response == null || returnType == null) { - return null; - } - - if ("byte[]".equals(returnType.toString())) { - // Handle binary response (byte array). - try { - return (T) response.body().bytes(); - } catch (IOException e) { - throw new ApiException(e); - } - } else if (returnType.equals(File.class)) { - // Handle file downloading. - return (T) downloadFileFromResponse(response); - } - - ResponseBody respBody = response.body(); - if (respBody == null) { - return null; - } - - String contentType = response.headers().get("Content-Type"); - if (contentType == null) { - // ensuring a default content type - contentType = "application/json"; - } - try { - if (isJsonMime(contentType)) { - return JSON.deserialize(respBody.byteStream(), returnType); - } else if (returnType.equals(String.class)) { - String respBodyString = respBody.string(); - if (respBodyString.isEmpty()) { - return null; - } - // Expecting string, return the raw response body. - return (T) respBodyString; - } else { - throw new ApiException( - "Content type \"" + contentType + "\" is not supported for type: " + returnType, - response.code(), - response.headers().toMultimap(), - response.body().string()); - } - } catch (IOException e) { - throw new ApiException(e); - } - } - - /** - * Serialize the given Java object into request body according to the object's - * class and the request Content-Type. - * - * @param obj The Java object - * @param contentType The request Content-Type - * @return The serialized request body - * @throws {{invokerPackage}}.ApiException If fail to serialize the given object - */ - public RequestBody serialize(Object obj, String contentType) throws ApiException { - if (obj instanceof byte[]) { - // Binary (byte array) body parameter support. - return RequestBody.create((byte[]) obj, MediaType.parse(contentType)); - } else if (obj instanceof File) { - // File body parameter support. - return RequestBody.create((File) obj, MediaType.parse(contentType)); - } else if ("text/plain".equals(contentType) && obj instanceof String) { - return RequestBody.create((String) obj, MediaType.parse(contentType)); - } else if (isJsonMime(contentType)) { - String content; - if (obj != null) { - content = JSON.serialize(obj); - } else { - content = null; - } - return RequestBody.create(content, MediaType.parse(contentType)); - } else if (obj instanceof String) { - return RequestBody.create((String) obj, MediaType.parse(contentType)); - } else { - throw new ApiException("Content type \"" + contentType + "\" is not supported"); - } - } - - /** - * Download file from the given response. - * - * @param response An instance of the Response object - * @throws {{invokerPackage}}.ApiException If fail to read file content from response and write to disk - * @return Downloaded file - */ - public File downloadFileFromResponse(Response response) throws ApiException { - try { - File file = prepareDownloadFile(response); - BufferedSink sink = Okio.buffer(Okio.sink(file)); - sink.writeAll(response.body().source()); - sink.close(); - return file; - } catch (IOException e) { - throw new ApiException(e); - } - } - - /** - * Prepare file for download - * - * @param response An instance of the Response object - * @return Prepared file for the download - * @throws java.io.IOException If fail to prepare file for download - */ - public File prepareDownloadFile(Response response) throws IOException { - String filename = null; - String contentDisposition = response.header("Content-Disposition"); - 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 = sanitizeFilename(matcher.group(1)); - } - } - - String prefix = null; - 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(); - } - - /** - * {@link #execute(Call, Type)} - * - * @param Type - * @param call An instance of the Call object - * @return ApiResponse<T> - * @throws {{invokerPackage}}.ApiException If fail to execute the call - */ - public ApiResponse execute(Call call) throws ApiException { - return execute(call, null); - } - - /** - * Execute HTTP call and deserialize the HTTP response body into the given return type. - * - * @param returnType The return type used to deserialize HTTP response body - * @param The return type corresponding to (same with) returnType - * @param call Call - * @return ApiResponse object containing response status, headers and - * data, which is a Java object deserialized from response body and would be null - * when returnType is null. - * @throws {{invokerPackage}}.ApiException If fail to execute the call - */ - public ApiResponse execute(Call call, Type returnType) throws ApiException { - try { - Response response = call.execute(); - T data = handleResponse(response, returnType); - return new ApiResponse(response.code(), response.headers().toMultimap(), data); - } catch (IOException e) { - throw new ApiException(e); - } - } - - {{#supportStreaming}} - /** - *

Execute stream.

- * - * @param call a {@link okhttp3.Call} object - * @param returnType a {@link java.lang.reflect.Type} object - * @return a {@link java.io.InputStream} object - * @throws {{invokerPackage}}.ApiException if any. - */ - public InputStream executeStream(Call call, Type returnType) throws ApiException { - try { - Response response = call.execute(); - if (!response.isSuccessful()) { - throw new ApiException(response.code(), response.message(), response.headers().toMultimap(), null); - } - if (response.body() == null) { - return null; - } - return response.body().byteStream(); - } catch (IOException e) { - throw new ApiException(e); - } - } - - {{/supportStreaming}} - /** - * {@link #executeAsync(Call, Type, ApiCallback)} - * - * @param Type - * @param call An instance of the Call object - * @param callback ApiCallback<T> - */ - public void executeAsync(Call call, ApiCallback callback) { - executeAsync(call, null, callback); - } - - /** - * Execute HTTP call asynchronously. - * - * @param Type - * @param call The callback to be executed when the API call finishes - * @param returnType Return type - * @param callback ApiCallback - * @see #execute(Call, Type) - */ - @SuppressWarnings("unchecked") - public void executeAsync(Call call, final Type returnType, final ApiCallback callback) { - call.enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - callback.onFailure(new ApiException(e), 0, null); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - T result; - try { - result = (T) handleResponse(response, returnType); - } catch (ApiException e) { - callback.onFailure(e, response.code(), response.headers().toMultimap()); - return; - } catch (Exception e) { - callback.onFailure(new ApiException(e), response.code(), response.headers().toMultimap()); - return; - } - callback.onSuccess(result, response.code(), response.headers().toMultimap()); - } - }); - } - - /** - * Handle the given response, return the deserialized object when the response is successful. - * - * @param Type - * @param response Response - * @param returnType Return type - * @return Type - * @throws {{invokerPackage}}.ApiException If the response has an unsuccessful status code or - * fail to deserialize the response body - */ - public T handleResponse(Response response, Type returnType) throws ApiException { - if (response.isSuccessful()) { - if (returnType == null || response.code() == 204) { - // returning null if the returnType is not defined, - // or the status code is 204 (No Content) - if (response.body() != null) { - try { - response.body().close(); - } catch (Exception e) { - throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); - } - } - return null; - } else { - return deserialize(response, returnType); - } - } else { - String respBody = null; - if (response.body() != null) { - try { - respBody = response.body().string(); - } catch (IOException e) { - throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); - } - } - throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), respBody); - } - } - - /** - * Build HTTP call with the given options. - * - * @param baseUrl The base URL - * @param path The sub-path of the HTTP URL - * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" - * @param queryParams The query parameters - * @param collectionQueryParams The collection query parameters - * @param body The request body object - * @param headerParams The header parameters - * @param cookieParams The cookie parameters - * @param formParams The form parameters - * @param authNames The authentications to apply - * @param callback Callback for upload/download progress - * @return The HTTP call - * @throws {{invokerPackage}}.ApiException If fail to serialize the request body object - */ - public Call buildCall(String baseUrl, String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) throws ApiException { - Request request = buildRequest(baseUrl, path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, callback); - - return httpClient.newCall(request); - } - - /** - * Build an HTTP request with the given options. - * - * @param baseUrl The base URL - * @param path The sub-path of the HTTP URL - * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" - * @param queryParams The query parameters - * @param collectionQueryParams The collection query parameters - * @param body The request body object - * @param headerParams The header parameters - * @param cookieParams The cookie parameters - * @param formParams The form parameters - * @param authNames The authentications to apply - * @param callback Callback for upload/download progress - * @return The HTTP request - * @throws {{invokerPackage}}.ApiException If fail to serialize the request body object - */ - public Request buildRequest(String baseUrl, String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) throws ApiException { - final String url = buildUrl(baseUrl, path, queryParams, collectionQueryParams); - - // prepare HTTP request body - RequestBody reqBody; - String contentType = headerParams.get("Content-Type"); - String contentTypePure = contentType; - if (contentTypePure != null && contentTypePure.contains(";")) { - contentTypePure = contentType.substring(0, contentType.indexOf(";")); - } - if (!HttpMethod.permitsRequestBody(method)) { - reqBody = null; - } else if ("application/x-www-form-urlencoded".equals(contentTypePure)) { - reqBody = buildRequestBodyFormEncoding(formParams); - } else if ("multipart/form-data".equals(contentTypePure)) { - reqBody = buildRequestBodyMultipart(formParams); - } else if (body == null) { - if ("DELETE".equals(method)) { - // allow calling DELETE without sending a request body - reqBody = null; - } else { - // use an empty request body (for POST, PUT and PATCH) - reqBody = RequestBody.create("", contentType == null ? null : MediaType.parse(contentType)); - } - } else { - reqBody = serialize(body, contentType); - } - - List updatedQueryParams = new ArrayList<>(queryParams); - - // update parameters with authentication settings - updateParamsForAuth(authNames, updatedQueryParams, headerParams, cookieParams, requestBodyToString(reqBody), method, URI.create(url)); - - final Request.Builder reqBuilder = new Request.Builder().url(buildUrl(baseUrl, path, updatedQueryParams, collectionQueryParams)); - processHeaderParams(headerParams, reqBuilder); - processCookieParams(cookieParams, reqBuilder); - - // Associate callback with request (if not null) so interceptor can - // access it when creating ProgressResponseBody - reqBuilder.tag(callback); - - Request request = null; - - if (callback != null && reqBody != null) { - ProgressRequestBody progressRequestBody = new ProgressRequestBody(reqBody, callback); - request = reqBuilder.method(method, progressRequestBody).build(); - } else { - request = reqBuilder.method(method, reqBody).build(); - } - - return request; - } - - /** - * Build full URL by concatenating base path, the given sub path and query parameters. - * - * @param baseUrl The base URL - * @param path The sub path - * @param queryParams The query parameters - * @param collectionQueryParams The collection query parameters - * @return The full URL - */ - public String buildUrl(String baseUrl, String path, List queryParams, List collectionQueryParams) { - final StringBuilder url = new StringBuilder(); - if (baseUrl != null) { - url.append(baseUrl).append(path); - } else { - 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; - } - 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()); - url.append(escapeString(param.getName())).append("=").append(escapeString(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); - } - } - } - - return url.toString(); - } - - /** - * Set header parameters to the request builder, including default headers. - * - * @param headerParams Header parameters in the form of Map - * @param reqBuilder Request.Builder - */ - public void processHeaderParams(Map headerParams, Request.Builder reqBuilder) { - for (Entry param : headerParams.entrySet()) { - reqBuilder.header(param.getKey(), parameterToString(param.getValue())); - } - for (Entry header : defaultHeaderMap.entrySet()) { - if (!headerParams.containsKey(header.getKey())) { - reqBuilder.header(header.getKey(), parameterToString(header.getValue())); - } - } - } - - /** - * Set cookie parameters to the request builder, including default cookies. - * - * @param cookieParams Cookie parameters in the form of Map - * @param reqBuilder Request.Builder - */ - public void processCookieParams(Map cookieParams, Request.Builder reqBuilder) { - for (Entry param : cookieParams.entrySet()) { - reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); - } - for (Entry param : defaultCookieMap.entrySet()) { - if (!cookieParams.containsKey(param.getKey())) { - reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); - } - } - } - - /** - * Update query and header parameters based on authentication settings. - * - * @param authNames The authentications to apply - * @param queryParams List of query parameters - * @param headerParams Map of header parameters - * @param cookieParams Map of cookie parameters - * @param payload HTTP request body - * @param method HTTP method - * @param uri URI - * @throws {{invokerPackage}}.ApiException If fails to update the parameters - */ - public void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams, - Map cookieParams, String payload, String method, URI uri) throws ApiException { - for (String authName : authNames) { - Authentication auth = authentications.get(authName); - if (auth == null) { - throw new RuntimeException("Authentication undefined: " + authName); - } - auth.applyToParams(queryParams, headerParams, cookieParams, payload, method, uri); - } - } - - /** - * Build a form-encoding request body with the given form parameters. - * - * @param formParams Form parameters in the form of Map - * @return RequestBody - */ - public RequestBody buildRequestBodyFormEncoding(Map formParams) { - okhttp3.FormBody.Builder formBuilder = new okhttp3.FormBody.Builder(); - for (Entry param : formParams.entrySet()) { - formBuilder.add(param.getKey(), parameterToString(param.getValue())); - } - return formBuilder.build(); - } - - /** - * Build a multipart (file uploading) request body with the given form parameters, - * which could contain text fields and file fields. - * - * @param formParams Form parameters in the form of Map - * @return RequestBody - */ - public RequestBody buildRequestBodyMultipart(Map formParams) { - MultipartBody.Builder mpBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); - for (Entry param : formParams.entrySet()) { - if (param.getValue() instanceof File) { - File file = (File) param.getValue(); - addPartToMultiPartBuilder(mpBuilder, param.getKey(), file); - } else if (param.getValue() instanceof List) { - List list = (List) param.getValue(); - for (Object item: list) { - if (item instanceof File) { - addPartToMultiPartBuilder(mpBuilder, param.getKey(), (File) item); - } else { - addPartToMultiPartBuilder(mpBuilder, param.getKey(), param.getValue()); - } - } - } else { - addPartToMultiPartBuilder(mpBuilder, param.getKey(), param.getValue()); - } - } - return mpBuilder.build(); - } - - /** - * Guess Content-Type header from the given file (defaults to "application/octet-stream"). - * - * @param file The given file - * @return The guessed Content-Type - */ - public String guessContentTypeFromFile(File file) { - String contentType = URLConnection.guessContentTypeFromName(file.getName()); - if (contentType == null) { - return "application/octet-stream"; - } else { - return contentType; - } - } - - /** - * Add a Content-Disposition Header for the given key and file to the MultipartBody Builder. - * - * @param mpBuilder MultipartBody.Builder - * @param key The key of the Header element - * @param file The file to add to the Header - */ - protected void addPartToMultiPartBuilder(MultipartBody.Builder mpBuilder, String key, File file) { - Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + key + "\"; filename=\"" + file.getName() + "\""); - MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file)); - mpBuilder.addPart(partHeaders, RequestBody.create(file, mediaType)); - } - - /** - * Add a Content-Disposition Header for the given key and complex object to the MultipartBody Builder. - * - * @param mpBuilder MultipartBody.Builder - * @param key The key of the Header element - * @param obj The complex object to add to the Header - */ - protected void addPartToMultiPartBuilder(MultipartBody.Builder mpBuilder, String key, Object obj) { - RequestBody requestBody; - if (obj instanceof String) { - requestBody = RequestBody.create((String) obj, MediaType.parse("text/plain")); - } else { - String content; - if (obj != null) { - content = JSON.serialize(obj); - } else { - content = null; - } - requestBody = RequestBody.create(content, MediaType.parse("application/json")); - } - - Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + key + "\""); - mpBuilder.addPart(partHeaders, requestBody); - } - - /** - * Get network interceptor to add it to the httpClient to track download progress for - * async requests. - */ - protected Interceptor getProgressInterceptor() { - return new Interceptor() { - @Override - public Response intercept(Interceptor.Chain chain) throws IOException { - final Request request = chain.request(); - final Response originalResponse = chain.proceed(request); - if (request.tag() instanceof ApiCallback) { - final ApiCallback callback = (ApiCallback) request.tag(); - return originalResponse.newBuilder() - .body(new ProgressResponseBody(originalResponse.body(), callback)) - .build(); - } - return originalResponse; - } - }; - } - - /** - * Apply SSL related settings to httpClient according to the current values of - * verifyingSsl and sslCaCert. - */ - protected void applySslSettings() { - try { - TrustManager[] trustManagers; - HostnameVerifier hostnameVerifier; - if (!verifyingSsl) { - trustManagers = new TrustManager[]{ - new X509TrustManager() { - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { - } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { - } - - @Override - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new java.security.cert.X509Certificate[]{}; - } - } - }; - hostnameVerifier = new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }; - } else { - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - - if (sslCaCert == null) { - trustManagerFactory.init((KeyStore) null); - } else { - char[] password = null; // Any password will work. - CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); - Collection certificates = certificateFactory.generateCertificates(sslCaCert); - if (certificates.isEmpty()) { - throw new IllegalArgumentException("expected non-empty set of trusted certificates"); - } - KeyStore caKeyStore = newEmptyKeyStore(password); - int index = 0; - for (Certificate certificate : certificates) { - String certificateAlias = "ca" + (index++); - caKeyStore.setCertificateEntry(certificateAlias, certificate); - } - trustManagerFactory.init(caKeyStore); - } - trustManagers = trustManagerFactory.getTrustManagers(); - hostnameVerifier = OkHostnameVerifier.INSTANCE; - } - - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(keyManagers, trustManagers, new SecureRandom()); - httpClient = httpClient.newBuilder() - .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0]) - .hostnameVerifier(hostnameVerifier) - .build(); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - } - - protected KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { - try { - KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - keyStore.load(null, password); - return keyStore; - } catch (IOException e) { - throw new AssertionError(e); - } - } - {{#dynamicOperations}} - - public ApiClient createOperationLookupMap(OpenAPI openAPI) { - operationLookupMap = new HashMap<>(); - for (Map.Entry pathItemEntry : openAPI.getPaths().entrySet()) { - String path = pathItemEntry.getKey(); - PathItem pathItem = pathItemEntry.getValue(); - addOperationLookupEntry(path, "GET", pathItem.getGet()); - addOperationLookupEntry(path, "PUT", pathItem.getPut()); - addOperationLookupEntry(path, "POST", pathItem.getPost()); - addOperationLookupEntry(path, "DELETE", pathItem.getDelete()); - addOperationLookupEntry(path, "OPTIONS", pathItem.getOptions()); - addOperationLookupEntry(path, "HEAD", pathItem.getHead()); - addOperationLookupEntry(path, "PATCH", pathItem.getPatch()); - addOperationLookupEntry(path, "TRACE", pathItem.getTrace()); - } - return this; - } - - protected void addOperationLookupEntry(String path, String method, Operation operation) { - if ( operation != null && operation.getOperationId() != null) { - operationLookupMap.put( - operation.getOperationId(), - new ApiOperation(path, method, operation)); - } - } - - public Map getOperationLookupMap() { - return operationLookupMap; - } - - public String fillParametersFromOperation( - Operation operation, - Map paramMap, - String path, - List queryParams, - List collectionQueryParams, - Map headerParams, - Map cookieParams - ) { - for (Map.Entry entry : paramMap.entrySet()) { - Object value = entry.getValue(); - for (Parameter param : operation.getParameters()) { - if (entry.getKey().equals(param.getName())) { - switch (param.getIn()) { - case "path": - path = path.replace("{" + param.getName() + "}", escapeString(value.toString())); - break; - case "query": - if (value instanceof Collection) { - collectionQueryParams.addAll(parameterToPairs(param, (Collection) value)); - } else { - queryParams.addAll(parameterToPair(param.getName(), value)); - } - break; - case "header": - headerParams.put(param.getName(), parameterToString(value)); - break; - case "cookie": - cookieParams.put(param.getName(), parameterToString(value)); - break; - default: - throw new IllegalStateException("Unexpected param in: " + param.getIn()); - } - - } - } - } - return path; - } - {{/dynamicOperations}} - - /** - * Convert the HTTP request body to a string. - * - * @param requestBody The HTTP request object - * @return The string representation of the HTTP request body - * @throws {{invokerPackage}}.ApiException If fail to serialize the request body object into a string - */ - protected String requestBodyToString(RequestBody requestBody) throws ApiException { - // Payload is only used by some auth schemes; for large/binary uploads we must not buffer. - if (requestBody == null) { - return ""; - } - - // If you *know* none of your auth implementations use payload, you can simply: - // return ""; - - // Otherwise, keep a safe, capped implementation: - try { - final MediaType contentType = requestBody.contentType(); - final long maxBytes = 64L * 1024L; // 64KiB cap - - // Do not attempt to stringify binary bodies. - if (contentType != null) { - final String type = contentType.type(); - final String subtype = contentType.subtype(); - final boolean textLike = - "text".equalsIgnoreCase(type) || - (subtype != null && ( - subtype.toLowerCase().contains("json") || - subtype.toLowerCase().contains("xml") || - subtype.toLowerCase().contains("x-www-form-urlencoded") - )); - if (!textLike) return ""; - } - - final long declaredLength = requestBody.contentLength(); - if (declaredLength > maxBytes) { - return ""; - } - - final Buffer buffer = new Buffer(); - requestBody.writeTo(buffer); - - final long toRead = Math.min(buffer.size(), maxBytes); - final Charset charset = - (contentType != null && contentType.charset() != null) ? contentType.charset() : StandardCharsets.UTF_8; - - return buffer.readString(toRead, charset); - } catch (final IOException e) { - throw new ApiException(e); - } - } -} diff --git a/buildSrc/src/main/resources/vertex-java/oneof_model.mustache b/buildSrc/src/main/resources/vertex-java/oneof_model.mustache deleted file mode 100644 index 50e03ca..0000000 --- a/buildSrc/src/main/resources/vertex-java/oneof_model.mustache +++ /dev/null @@ -1,537 +0,0 @@ - - -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.google.gson.JsonPrimitive; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonArray; -import com.google.gson.JsonParseException; - -import {{invokerPackage}}.JSON; - -// This source file was generated using a Vertex modified version of the oneof_model.mustache template. - -{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{>xmlAnnotation}} -public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-implements}} implements {{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-implements}} { - private static final Logger log = Logger.getLogger({{classname}}.class.getName()); - - public static class CustomTypeAdapterFactory implements TypeAdapterFactory { - @SuppressWarnings("unchecked") - @Override - public TypeAdapter create(Gson gson, TypeToken type) { - if (!{{classname}}.class.isAssignableFrom(type.getRawType())) { - return null; // this class only serializes '{{classname}}' and its subtypes - } - final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class); - {{#composedSchemas}} - {{#oneOf}} - {{^isArray}} - {{^isMap}} - {{^vendorExtensions.x-duplicated-data-type}} - final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = gson.getDelegateAdapter(this, TypeToken.get({{{dataType}}}.class)); - {{/vendorExtensions.x-duplicated-data-type}} - {{/isMap}} - {{/isArray}} - {{#isArray}} - - final Type typeInstance{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = new TypeToken<{{{dataType}}}>(){}.getType(); - final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = (TypeAdapter<{{{dataType}}}>) gson.getDelegateAdapter(this, TypeToken.get(typeInstance{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}})); - {{/isArray}} - {{#isMap}} - final Type typeInstance{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = new TypeToken<{{{dataType}}}>(){}.getType(); - final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = (TypeAdapter<{{{dataType}}}>) gson.getDelegateAdapter(this, TypeToken.get(typeInstance{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}})); - {{/isMap}} - {{/oneOf}} - {{/composedSchemas}} - - return (TypeAdapter) new TypeAdapter<{{classname}}>() { - @Override - public void write(JsonWriter out, {{classname}} value) throws IOException { - if (value == null || value.getActualInstance() == null) { - elementAdapter.write(out, null); - return; - } - - {{#composedSchemas}} - {{#oneOf}} - {{^vendorExtensions.x-duplicated-data-type}} - // check if the actual instance is of the type `{{{dataType}}}` - if (value.getActualInstance() instanceof {{#isArray}}List{{/isArray}}{{#isMap}}Map{{/isMap}}{{^isMap}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isMap}}) { - {{#isPrimitiveType}} - {{^isMap}} - JsonPrimitive primitive = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonPrimitive(); - elementAdapter.write(out, primitive); - return; - {{/isMap}} - {{#isMap}} - JsonObject object = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonObject(); - elementAdapter.write(out, object); - return; - {{/isMap}} - {{/isPrimitiveType}} - {{^isPrimitiveType}} - {{#isArray}} - List list = (List) value.getActualInstance(); - if (list.get(0) instanceof {{{items.dataType}}}) { - JsonArray array = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonArray(); - elementAdapter.write(out, array); - return; - } - {{/isArray}} - {{/isPrimitiveType}} - {{^isMap}} - {{^isArray}} - {{^isPrimitiveType}} - JsonElement element = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()); - elementAdapter.write(out, element); - return; - {{/isPrimitiveType}} - {{/isArray}} - {{/isMap}} - } - {{/vendorExtensions.x-duplicated-data-type}} - {{/oneOf}} - {{/composedSchemas}} - throw new IOException("Failed to serialize as the type doesn't match oneOf schemas: {{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}"); - } - - @Override - public {{classname}} read(JsonReader in) throws IOException { - Object deserialized = null; - JsonElement jsonElement = elementAdapter.read(in); - - {{#useOneOfDiscriminatorLookup}} - {{#discriminator}} - JsonObject jsonObject = jsonElement.getAsJsonObject(); - - // use discriminator value for faster oneOf lookup - {{classname}} new{{classname}} = new {{classname}}(); - if (jsonObject.get("{{{propertyBaseName}}}") == null) { - log.log(Level.WARNING, "Failed to lookup discriminator value for {{classname}} as `{{{propertyBaseName}}}` was not found in the payload or the payload is empty."); - } else { - // look up the discriminator value in the field `{{{propertyBaseName}}}` - switch (jsonObject.get("{{{propertyBaseName}}}").getAsString()) { - {{#mappedModels}} - case "{{{mappingName}}}": - deserialized = adapter{{modelName}}.fromJsonTree(jsonObject); - new{{classname}}.setActualInstance(deserialized); - return new{{classname}}; - {{/mappedModels}} - default: - log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for {{classname}}. Possible values:{{#mappedModels}} {{{mappingName}}}{{/mappedModels}}", jsonObject.get("{{{propertyBaseName}}}").getAsString())); - } - } - - {{/discriminator}} - {{/useOneOfDiscriminatorLookup}} - int match = 0; - ArrayList errorMessages = new ArrayList<>(); - TypeAdapter actualAdapter = elementAdapter; - - {{#composedSchemas}} - {{#oneOf}} - {{^vendorExtensions.x-duplicated-data-type}} - {{^hasVars}} - // deserialize {{{dataType}}} - try { - // validate the JSON object to see if any exception is thrown - {{^isArray}} - {{^isMap}} - {{#isNumber}} - if (!jsonElement.getAsJsonPrimitive().isNumber()) { - throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString())); - } - actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}; - {{/isNumber}} - {{^isNumber}} - {{#isPrimitiveType}} - if (!jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) { - throw new IllegalArgumentException(String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString())); - } - actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}; - {{/isPrimitiveType}} - {{/isNumber}} - {{^isNumber}} - {{^isPrimitiveType}} - {{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(jsonElement); - actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}; - {{/isPrimitiveType}} - {{/isNumber}} - {{/isMap}} - {{/isArray}} - {{#isArray}} - if (!jsonElement.isJsonArray()) { - throw new IllegalArgumentException(String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString())); - } - - JsonArray array = jsonElement.getAsJsonArray(); - // validate array items - for(JsonElement element : array) { - {{#items}} - {{#isNumber}} - if (!jsonElement.getAsJsonPrimitive().isNumber()) { - throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString())); - } - {{/isNumber}} - {{^isNumber}} - {{#isPrimitiveType}} - if (!element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) { - throw new IllegalArgumentException(String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString())); - } - {{/isPrimitiveType}} - {{/isNumber}} - {{^isNumber}} - {{^isPrimitiveType}} - {{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(element); - {{/isPrimitiveType}} - {{/isNumber}} - {{/items}} - } - actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}; - {{/isArray}} - {{#isMap}} - if (!jsonElement.isJsonObject()) { - throw new IllegalArgumentException(String.format("Expected json element to be a object type in the JSON string but got `%s`", jsonElement.toString())); - } - - {{^isFreeFormObject}} - Map map = jsonElement.getAsJsonObject().asMap(); - // validate map items - for(JsonElement element : map.values()) { - {{#items}} - {{#isNumber}} - if (!jsonElement.getAsJsonPrimitive().isNumber()) { - throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString())); - } - {{/isNumber}} - {{^isNumber}} - {{#isPrimitiveType}} - if (!element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) { - throw new IllegalArgumentException(String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString())); - } - {{/isPrimitiveType}} - {{/isNumber}} - {{^isNumber}} - {{^isPrimitiveType}} - {{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(element); - {{/isPrimitiveType}} - {{/isNumber}} - {{/items}} - } - {{/isFreeFormObject}} - actualAdapter = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}; - {{/isMap}} - match++; - log.log(Level.FINER, "Input data matches schema '{{{dataType}}}'"); - } catch (Exception e) { - // deserialization failed, continue - errorMessages.add(String.format("Deserialization for {{{dataType}}} failed with `%s`.", e.getMessage())); - log.log(Level.FINER, "Input data does not match schema '{{{dataType}}}'", e); - } - {{/hasVars}} - {{#hasVars}} - // deserialize {{{.}}} - try { - // validate the JSON object to see if any exception is thrown - {{.}}.validateJsonElement(jsonElement); - actualAdapter = adapter{{.}}; - match++; - log.log(Level.FINER, "Input data matches schema '{{{.}}}'"); - } catch (Exception e) { - // deserialization failed, continue - errorMessages.add(String.format("Deserialization for {{{.}}} failed with `%s`.", e.getMessage())); - log.log(Level.FINER, "Input data does not match schema '{{{.}}}'", e); - } - {{/hasVars}} - {{/vendorExtensions.x-duplicated-data-type}} - {{/oneOf}} - {{/composedSchemas}} - - if (match == 1) { - {{classname}} ret = new {{classname}}(); - ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement)); - return ret; - } - - throw new IOException(String.format("Failed deserialization for {{classname}}: %d classes match result, expected 1. Detailed failure message for oneOf schemas: %s. JSON: %s", match, errorMessages, jsonElement.toString())); - } - }.nullSafe(); - } - } - - // store a list of schema names defined in oneOf - public static final Map> schemas = new HashMap>(); - - public {{classname}}() { - super("oneOf", {{#isNullable}}Boolean.TRUE{{/isNullable}}{{^isNullable}}Boolean.FALSE{{/isNullable}}); - } - - public {{classname}}(Object o) { - super("oneOf", {{#isNullable}}Boolean.TRUE{{/isNullable}}{{^isNullable}}Boolean.FALSE{{/isNullable}}); - setActualInstance(o); - } - - static { - {{#composedSchemas}} - {{#oneOf}} - {{^vendorExtensions.x-duplicated-data-type}} - schemas.put("{{{dataType}}}", {{{baseType}}}.class); - {{/vendorExtensions.x-duplicated-data-type}} - {{/oneOf}} - {{/composedSchemas}} - } - - @Override - public Map> getSchemas() { - return {{classname}}.schemas; - } - - /** - * Set the instance that matches the oneOf child schema, check - * the instance parameter is valid against the oneOf child schemas: - * {{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}} - * - * It could be an instance of the 'oneOf' schemas. - */ - @Override - public void setActualInstance(Object instance) { - {{#isNullable}} - if (instance == null) { - super.setActualInstance(instance); - return; - } - - {{/isNullable}} - {{#composedSchemas}} - {{#oneOf}} - {{^vendorExtensions.x-duplicated-data-type}} - if (instance instanceof {{#isArray}}List{{/isArray}}{{#isMap}}Map{{/isMap}}{{^isMap}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isMap}}) { - {{#isArray}} - List list = (List) instance; - if (list.get(0) instanceof {{{items.dataType}}}) { - super.setActualInstance(instance); - return; - } - {{/isArray}} - {{^isArray}} - super.setActualInstance(instance); - return; - {{/isArray}} - } - - {{/vendorExtensions.x-duplicated-data-type}} - {{/oneOf}} - {{/composedSchemas}} - throw new RuntimeException("Invalid instance type. Must be {{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}"); - } - - /** - * Get the actual instance, which can be the following: - * {{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}} - * - * @return The actual instance ({{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}) - */ - @SuppressWarnings("unchecked") - @Override - public Object getActualInstance() { - return super.getActualInstance(); - } - - {{#composedSchemas}} - {{#oneOf}} - {{^vendorExtensions.x-duplicated-data-type-ignoring-erasure}} - /** - * Get the actual instance of `{{{dataType}}}`. If the actual instance is not `{{{dataType}}}`, - * the ClassCastException will be thrown. - * - * @return The actual instance of `{{{dataType}}}` - * @throws ClassCastException if the instance is not `{{{dataType}}}` - */ - public {{{dataType}}} get{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}() throws ClassCastException { - return ({{{dataType}}})super.getActualInstance(); - } - - {{/vendorExtensions.x-duplicated-data-type-ignoring-erasure}} - {{/oneOf}} - {{/composedSchemas}} - /** - * Validates the JSON Element and throws an exception if issues found - * - * @param jsonElement JSON Element - * @throws IOException if the JSON Element is invalid with respect to {{classname}} - */ - public static void validateJsonElement(JsonElement jsonElement) throws IOException { - {{#useOneOfDiscriminatorLookup}} - {{#discriminator}} - // Vertex customization -- use the same behavior as the read method. - JsonObject jsonObject = jsonElement != null ? jsonElement.getAsJsonObject() : null; - - if (jsonObject != null) { - if (jsonObject.get("type") == null) { - log.log(Level.WARNING, "Failed to lookup discriminator value for {{classname}} as `{{{propertyBaseName}}}` was not found in the payload or the payload is empty."); - } else { - // look up the discriminator value in the field `{{{propertyBaseName}}}` - switch (jsonObject.get("{{{propertyBaseName}}}").getAsString()) { - {{#mappedModels}} - case "{{{mappingName}}}": - {{modelName}}.validateJsonElement(jsonElement); - return; - {{/mappedModels}} - default: - log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for {{classname}}. Possible values:{{#mappedModels}} {{{mappingName}}}{{/mappedModels}}", jsonObject.get("{{{propertyBaseName}}}").getAsString())); - } - } - } - {{/discriminator}} - {{/useOneOfDiscriminatorLookup}} - // validate oneOf schemas one by one - int validCount = 0; - ArrayList errorMessages = new ArrayList<>(); - {{#composedSchemas}} - {{#oneOf}} - {{^vendorExtensions.x-duplicated-data-type}} - // validate the json string with {{{dataType}}} - try { - {{^hasVars}} - {{^isMap}} - {{^isArray}} - {{#isNumber}} - if (!jsonElement.getAsJsonPrimitive().isNumber()) { - throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString())); - } - {{/isNumber}} - {{^isNumber}} - {{#isPrimitiveType}} - if (!jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) { - throw new IllegalArgumentException(String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString())); - } - {{/isPrimitiveType}} - {{/isNumber}} - {{^isNumber}} - {{^isPrimitiveType}} - {{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(jsonElement); - {{/isPrimitiveType}} - {{/isNumber}} - {{/isArray}} - {{/isMap}} - {{#isArray}} - if (!jsonElement.isJsonArray()) { - throw new IllegalArgumentException(String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString())); - } - JsonArray array = jsonElement.getAsJsonArray(); - // validate array items - for(JsonElement element : array) { - {{#items}} - {{#isNumber}} - if (!jsonElement.getAsJsonPrimitive().isNumber()) { - throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString())); - } - {{/isNumber}} - {{^isNumber}} - {{#isPrimitiveType}} - if (!element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) { - throw new IllegalArgumentException(String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString())); - } - {{/isPrimitiveType}} - {{/isNumber}} - {{^isNumber}} - {{^isPrimitiveType}} - {{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(element); - {{/isPrimitiveType}} - {{/isNumber}} - {{/items}} - } - {{/isArray}} - {{#isMap}} - if (!jsonElement.isJsonObject()) { - throw new IllegalArgumentException(String.format("Expected json element to be a object type in the JSON string but got `%s`", jsonElement.toString())); - } - - {{^isFreeFormObject}} - Map map = jsonElement.getAsJsonObject().asMap(); - // validate map items - for(JsonElement element : map.values()) { - {{#items}} - {{#isNumber}} - if (!jsonElement.getAsJsonPrimitive().isNumber()) { - throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString())); - } - {{/isNumber}} - {{^isNumber}} - {{#isPrimitiveType}} - if (!element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) { - throw new IllegalArgumentException(String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString())); - } - {{/isPrimitiveType}} - {{/isNumber}} - {{^isNumber}} - {{^isPrimitiveType}} - {{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.validateJsonElement(element); - {{/isPrimitiveType}} - {{/isNumber}} - {{/items}} - } - {{/isFreeFormObject}} - {{/isMap}} - {{/hasVars}} - {{#hasVars}} - {{{.}}}.validateJsonElement(jsonElement); - validCount++; - {{/hasVars}} - validCount++; - } catch (Exception e) { - errorMessages.add(String.format("Deserialization for {{{dataType}}} failed with `%s`.", e.getMessage())); - // continue to the next one - } - {{/vendorExtensions.x-duplicated-data-type}} - {{/oneOf}} - {{/composedSchemas}} - if (validCount != 1) { - throw new IOException(String.format("The JSON string is invalid for {{classname}} with oneOf schemas: {{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}. %d class(es) match the result, expected 1. Detailed failure message for oneOf schemas: %s. JSON: %s", validCount, errorMessages, jsonElement.toString())); - } - } - - /** - * Create an instance of {{classname}} given an JSON string - * - * @param jsonString JSON string - * @return An instance of {{classname}} - * @throws IOException if the JSON string is invalid with respect to {{classname}} - */ - public static {{{classname}}} fromJson(String jsonString) throws IOException { - return JSON.getGson().fromJson(jsonString, {{{classname}}}.class); - } - - /** - * Convert an instance of {{classname}} to an JSON string - * - * @return JSON string - */ - public String toJson() { - return JSON.getGson().toJson(this); - } -} diff --git a/buildSrc/src/main/resources/vertex-java/pojo.mustache b/buildSrc/src/main/resources/vertex-java/pojo.mustache deleted file mode 100644 index af2ed13..0000000 --- a/buildSrc/src/main/resources/vertex-java/pojo.mustache +++ /dev/null @@ -1,602 +0,0 @@ -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import {{invokerPackage}}.JSON; - -// This source file was generated using a Vertex modified version of the pojo.mustache template. - -/** - * {{description}}{{^description}}{{classname}}{{/description}}{{#isDeprecated}} - * @deprecated{{/isDeprecated}} - */{{#isDeprecated}} -@Deprecated{{/isDeprecated}} -{{#swagger1AnnotationLibrary}} -{{#description}} -@ApiModel(description = "{{{.}}}") -{{/description}} -{{/swagger1AnnotationLibrary}} -{{#swagger2AnnotationLibrary}} -{{#description}} -@Schema(description = "{{{.}}}") -{{/description}} -{{/swagger2AnnotationLibrary}} -{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} -{{#vendorExtensions.x-class-extra-annotation}} -{{{vendorExtensions.x-class-extra-annotation}}} -{{/vendorExtensions.x-class-extra-annotation}} -public class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{{#vendorExtensions.x-implements}}{{#-first}}implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{#-last}} {{/-last}}{{/vendorExtensions.x-implements}}{ -{{#serializableModel}} - private static final long serialVersionUID = 1L; - -{{/serializableModel}} - {{#vars}} - {{#isEnum}} - {{^isContainer}} -{{>modelInnerEnum}} - - {{/isContainer}} - {{#isContainer}} - {{#mostInnerItems}} -{{>modelInnerEnum}} - - {{/mostInnerItems}} - {{/isContainer}} - {{/isEnum}} - public static final String SERIALIZED_NAME_{{nameInSnakeCase}} = "{{baseName}}"; - {{#withXml}} - @Xml{{#isXmlAttribute}}Attribute{{/isXmlAttribute}}{{^isXmlAttribute}}Element{{/isXmlAttribute}}(name = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}}) - {{#isXmlWrapped}} - @XmlElementWrapper(name = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}}) - {{/isXmlWrapped}} - {{/withXml}} - {{#deprecated}} - @Deprecated - {{/deprecated}} - @SerializedName(SERIALIZED_NAME_{{nameInSnakeCase}}) - {{#vendorExtensions.x-field-extra-annotation}} - {{{vendorExtensions.x-field-extra-annotation}}} - {{/vendorExtensions.x-field-extra-annotation}} - {{>nullable_var_annotations}}{{! prevent indent}} - {{#isDiscriminator}}protected{{/isDiscriminator}}{{^isDiscriminator}}private{{/isDiscriminator}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}; - - {{/vars}} - public {{classname}}() { - {{#parent}} - {{#parcelableModel}} - super(); - {{/parcelableModel}} - {{/parent}} - {{#discriminator}} - {{#discriminator.isEnum}} -{{#readWriteVars}}{{#isDiscriminator}}{{#defaultValue}} - this.{{name}} = {{defaultValue}}; -{{/defaultValue}}{{/isDiscriminator}}{{/readWriteVars}} - {{/discriminator.isEnum}} - {{^discriminator.isEnum}} - this.{{{discriminatorName}}} = this.getClass().getSimpleName(); - {{/discriminator.isEnum}} - {{/discriminator}} - } - {{#vendorExtensions.x-has-readonly-properties}} - {{^withXml}} - - public {{classname}}( - {{#readOnlyVars}} - {{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}} - {{/readOnlyVars}} - ) { - this(); - {{#readOnlyVars}} - this.{{name}} = {{name}}; - {{/readOnlyVars}} - } - {{/withXml}} - {{/vendorExtensions.x-has-readonly-properties}} - {{#vars}} - - {{^isReadOnly}} - {{#deprecated}} - @Deprecated - {{/deprecated}} - public {{classname}} {{name}}({{>nullable_var_annotations}} {{{datatypeWithEnum}}} {{name}}) { - this.{{name}} = {{name}}; - return this; - } - {{#isArray}} - - public {{classname}} add{{nameInPascalCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { - if (this.{{name}} == null) { - this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}}; - } - this.{{name}}.add({{name}}Item); - return this; - } - {{/isArray}} - {{#isMap}} - - public {{classname}} put{{nameInPascalCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) { - if (this.{{name}} == null) { - this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}}; - } - this.{{name}}.put(key, {{name}}Item); - return this; - } - {{/isMap}} - - {{/isReadOnly}} - /** - {{#description}} - * {{.}} - {{/description}} - {{^description}} - * Get {{name}} - {{/description}} - {{#minimum}} - * minimum: {{.}} - {{/minimum}} - {{#maximum}} - * maximum: {{.}} - {{/maximum}} - * @return {{name}} - {{#deprecated}} - * @deprecated - {{/deprecated}} - */ -{{#deprecated}} - @Deprecated -{{/deprecated}} - {{>nullable_var_annotations}}{{! prevent indent}} -{{#useBeanValidation}} -{{>beanValidation}} - -{{/useBeanValidation}} -{{#swagger1AnnotationLibrary}} - @ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}value = "{{{description}}}") -{{/swagger1AnnotationLibrary}} -{{#swagger2AnnotationLibrary}} - @Schema({{#example}}example = "{{{.}}}", {{/example}}requiredMode = {{#required}}Schema.RequiredMode.REQUIRED{{/required}}{{^required}}Schema.RequiredMode.NOT_REQUIRED{{/required}}, description = "{{{description}}}") -{{/swagger2AnnotationLibrary}} -{{#vendorExtensions.x-extra-annotation}} - {{{vendorExtensions.x-extra-annotation}}} -{{/vendorExtensions.x-extra-annotation}} - public {{{datatypeWithEnum}}} {{getter}}() { - return {{name}}; - } - - {{^isReadOnly}} -{{#vendorExtensions.x-setter-extra-annotation}} {{{vendorExtensions.x-setter-extra-annotation}}} -{{/vendorExtensions.x-setter-extra-annotation}}{{#deprecated}} @Deprecated -{{/deprecated}} public void {{setter}}({{>nullable_var_annotations}} {{{datatypeWithEnum}}} {{name}}) { - this.{{name}} = {{name}}; - } - {{/isReadOnly}} - - {{/vars}} -{{>libraries/okhttp-gson/additional_properties}} - - - @Override - public boolean equals(Object o) { - {{#useReflectionEqualsHashCode}} - return EqualsBuilder.reflectionEquals(this, o, false, null, true); - {{/useReflectionEqualsHashCode}} - {{^useReflectionEqualsHashCode}} - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - }{{#hasVars}} - {{classname}} {{classVarName}} = ({{classname}}) o; - return {{#vars}}{{#isByteArray}}Arrays{{/isByteArray}}{{^isByteArray}}Objects{{/isByteArray}}.equals(this.{{name}}, {{classVarName}}.{{name}}){{^-last}} && - {{/-last}}{{/vars}}{{#isAdditionalPropertiesTrue}}&& - Objects.equals(this.additionalProperties, {{classVarName}}.additionalProperties){{/isAdditionalPropertiesTrue}}{{#parent}} && - super.equals(o){{/parent}};{{/hasVars}}{{^hasVars}} - return {{#parent}}super.equals(o){{/parent}}{{^parent}}true{{/parent}};{{/hasVars}} - {{/useReflectionEqualsHashCode}} - }{{#vendorExtensions.x-jackson-optional-nullable-helpers}} - - private static boolean equalsNullable(JsonNullable a, JsonNullable b) { - return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); - }{{/vendorExtensions.x-jackson-optional-nullable-helpers}} - - @Override - public int hashCode() { - {{#useReflectionEqualsHashCode}} - return HashCodeBuilder.reflectionHashCode(this); - {{/useReflectionEqualsHashCode}} - {{^useReflectionEqualsHashCode}} - return Objects.hash({{#vars}}{{^isByteArray}}{{name}}{{/isByteArray}}{{#isByteArray}}Arrays.hashCode({{name}}){{/isByteArray}}{{^-last}}, {{/-last}}{{/vars}}{{#parent}}{{#hasVars}}, {{/hasVars}}super.hashCode(){{/parent}}{{#isAdditionalPropertiesTrue}}{{#hasVars}}, {{/hasVars}}{{^hasVars}}{{#parent}}, {{/parent}}{{/hasVars}}additionalProperties{{/isAdditionalPropertiesTrue}}); - {{/useReflectionEqualsHashCode}} - }{{#vendorExtensions.x-jackson-optional-nullable-helpers}} - - private static int hashCodeNullable(JsonNullable a) { - if (a == null) { - return 1; - } - return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; - }{{/vendorExtensions.x-jackson-optional-nullable-helpers}} - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class {{classname}} {\n"); - {{#parent}} - sb.append(" ").append(toIndentedString(super.toString())).append("\n"); - {{/parent}} - {{#vars}} - sb.append(" {{name}}: ").append({{#isPassword}}"*"{{/isPassword}}{{^isPassword}}toIndentedString({{name}}){{/isPassword}}).append("\n"); - {{/vars}} -{{#isAdditionalPropertiesTrue}} - sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); -{{/isAdditionalPropertiesTrue}} - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces - * (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } - -{{#parcelableModel}} - - public void writeToParcel(Parcel out, int flags) { -{{#model}} -{{#isArray}} - out.writeList(this); -{{/isArray}} -{{^isArray}} -{{#parent}} - super.writeToParcel(out, flags); -{{/parent}} -{{#vars}} - out.writeValue({{name}}); -{{/vars}} -{{/isArray}} -{{/model}} - } - - {{classname}}(Parcel in) { -{{#isArray}} - in.readTypedList(this, {{arrayModelType}}.CREATOR); -{{/isArray}} -{{^isArray}} -{{#parent}} - super(in); -{{/parent}} -{{#vars}} -{{#isPrimitiveType}} - {{name}} = ({{{datatypeWithEnum}}})in.readValue(null); -{{/isPrimitiveType}} -{{^isPrimitiveType}} - {{name}} = ({{{datatypeWithEnum}}})in.readValue({{complexType}}.class.getClassLoader()); -{{/isPrimitiveType}} -{{/vars}} -{{/isArray}} - } - - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<{{classname}}> CREATOR = new Parcelable.Creator<{{classname}}>() { - public {{classname}} createFromParcel(Parcel in) { -{{#model}} -{{#isArray}} - {{classname}} result = new {{classname}}(); - result.addAll(in.readArrayList({{arrayModelType}}.class.getClassLoader())); - return result; -{{/isArray}} -{{^isArray}} - return new {{classname}}(in); -{{/isArray}} -{{/model}} - } - public {{classname}}[] newArray(int size) { - return new {{classname}}[size]; - } - }; -{{/parcelableModel}} - - public static HashSet openapiFields; - public static HashSet openapiRequiredFields; - - static { - // a set of all properties/fields (JSON key names) - {{#hasVars}} - openapiFields = new HashSet(Arrays.asList({{#allVars}}"{{baseName}}"{{^-last}}, {{/-last}}{{/allVars}})); - {{/hasVars}} - {{^hasVars}} - openapiFields = new HashSet(0); - {{/hasVars}} - - // a set of required properties/fields (JSON key names) - {{#hasRequired}} - openapiRequiredFields = new HashSet(Arrays.asList({{#requiredVars}}"{{baseName}}"{{^-last}}, {{/-last}}{{/requiredVars}})); - {{/hasRequired}} - {{^hasRequired}} - openapiRequiredFields = new HashSet(0); - {{/hasRequired}} - } - - /** - * Validates the JSON Element and throws an exception if issues found - * - * @param jsonElement JSON Element - * @throws IOException if the JSON Element is invalid with respect to {{classname}} - */ - public static void validateJsonElement(JsonElement jsonElement) throws IOException { - if (jsonElement == null) { - if (!{{classname}}.openapiRequiredFields.isEmpty()) { // has required fields but JSON element is null - throw new IllegalArgumentException(String.format("The required field(s) %s in {{{classname}}} is not found in the empty JSON string", {{classname}}.openapiRequiredFields.toString())); - } - } - {{^hasChildren}} - {{^isAdditionalPropertiesTrue}} - Set> entries = jsonElement.getAsJsonObject().entrySet(); - // check to see if the JSON string contains additional fields - for (Map.Entry entry : entries) { - if (!{{classname}}.openapiFields.contains(entry.getKey())) { - throw new IllegalArgumentException(String.format("The field `%s` in the JSON string is not defined in the `{{classname}}` properties. JSON: %s", entry.getKey(), jsonElement.toString())); - } - } - {{/isAdditionalPropertiesTrue}} - {{#requiredVars}} - {{#-first}} - - // check to make sure all required properties/fields are present in the JSON string - for (String requiredField : {{classname}}.openapiRequiredFields) { - if (jsonElement.getAsJsonObject().get(requiredField) == null) { - throw new IllegalArgumentException(String.format("The required field `%s` is not found in the JSON string: %s", requiredField, jsonElement.toString())); - } - } - {{/-first}} - {{/requiredVars}} - {{/hasChildren}} - {{^discriminator}} - {{#hasVars}} - JsonObject jsonObj = jsonElement.getAsJsonObject(); - {{/hasVars}} - {{#vars}} - {{#isArray}} - {{#items.isModel}} - {{#required}} - // ensure the json data is an array - if (!jsonObj.get("{{{baseName}}}").isJsonArray()) { - throw new IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj.get("{{{baseName}}}").toString())); - } - - JsonArray jsonArray{{name}} = jsonObj.getAsJsonArray("{{{baseName}}}"); - // validate the required field `{{{baseName}}}` (array) - for (int i = 0; i < jsonArray{{name}}.size(); i++) { - {{{items.dataType}}}.validateJsonElement(jsonArray{{name}}.get(i)); - }; - {{/required}} - {{^required}} - if (jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull()) { - JsonArray jsonArray{{name}} = jsonObj.getAsJsonArray("{{{baseName}}}"); - if (jsonArray{{name}} != null) { - // ensure the json data is an array - if (!jsonObj.get("{{{baseName}}}").isJsonArray()) { - throw new IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj.get("{{{baseName}}}").toString())); - } - - // validate the optional field `{{{baseName}}}` (array) - for (int i = 0; i < jsonArray{{name}}.size(); i++) { - {{{items.dataType}}}.validateJsonElement(jsonArray{{name}}.get(i)); - }; - } - } - {{/required}} - {{/items.isModel}} - {{^items.isModel}} - {{^required}} - // ensure the optional json data is an array if present - if (jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull() && !jsonObj.get("{{{baseName}}}").isJsonArray()) { - throw new IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj.get("{{{baseName}}}").toString())); - } - {{/required}} - {{#required}} - // ensure the required json array is present - if (jsonObj.get("{{{baseName}}}") == null) { - throw new IllegalArgumentException("Expected the field `linkedContent` to be an array in the JSON string but got `null`"); - } else if (!jsonObj.get("{{{baseName}}}").isJsonArray()) { - throw new IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj.get("{{{baseName}}}").toString())); - } - {{/required}} - {{/items.isModel}} - {{/isArray}} - {{^isContainer}} - {{#isString}} - if ({{#notRequiredOrIsNullable}}(jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull()) && {{/notRequiredOrIsNullable}}!jsonObj.get("{{{baseName}}}").isJsonPrimitive()) { - throw new IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be a primitive type in the JSON string but got `%s`", jsonObj.get("{{{baseName}}}").toString())); - } - {{/isString}} - {{#isModel}} - {{#required}} - // validate the required field `{{{baseName}}}` - {{{dataType}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - {{/required}} - {{^required}} - // validate the optional field `{{{baseName}}}` - if (jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull()) { - {{{dataType}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - } - {{/required}} - {{/isModel}} - {{#isEnum}} - {{#required}} - // validate the required field `{{{baseName}}}` - {{{datatypeWithEnum}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - {{/required}} - {{^required}} - // validate the optional field `{{{baseName}}}` - if (jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull()) { - {{{datatypeWithEnum}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - } - {{/required}} - {{/isEnum}} - {{#isEnumRef}} - {{#required}} - // validate the required field `{{{baseName}}}` - {{{dataType}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - {{/required}} - {{^required}} - // validate the optional field `{{{baseName}}}` - if (jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull()) { - {{{dataType}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - } - {{/required}} - {{/isEnumRef}} - {{/isContainer}} - {{/vars}} - {{/discriminator}} - {{#hasChildren}} - {{#discriminator}} - - String discriminatorValue = jsonElement.getAsJsonObject().get("{{{propertyBaseName}}}").getAsString(); - switch (discriminatorValue) { - {{#mappedModels}} - case "{{mappingName}}": - {{modelName}}.validateJsonElement(jsonElement); - break; - {{/mappedModels}} - default: - throw new IllegalArgumentException(String.format("The value of the `{{{propertyBaseName}}}` field `%s` does not match any key defined in the discriminator's mapping.", discriminatorValue)); - } - {{/discriminator}} - {{/hasChildren}} - } - -{{^hasChildren}} - public static class CustomTypeAdapterFactory implements TypeAdapterFactory { - @SuppressWarnings("unchecked") - @Override - public TypeAdapter create(Gson gson, TypeToken type) { - if (!{{classname}}.class.isAssignableFrom(type.getRawType())) { - return null; // this class only serializes '{{classname}}' and its subtypes - } - final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class); - final TypeAdapter<{{classname}}> thisAdapter - = gson.getDelegateAdapter(this, TypeToken.get({{classname}}.class)); - - return (TypeAdapter) new TypeAdapter<{{classname}}>() { - @Override - public void write(JsonWriter out, {{classname}} value) throws IOException { - JsonObject obj = thisAdapter.toJsonTree(value).getAsJsonObject(); - {{#isAdditionalPropertiesTrue}} - obj.remove("additionalProperties"); - // serialize additional properties - if (value.getAdditionalProperties() != null) { - for (Map.Entry entry : value.getAdditionalProperties().entrySet()) { - if (entry.getValue() instanceof String) - obj.addProperty(entry.getKey(), (String) entry.getValue()); - else if (entry.getValue() instanceof Number) - obj.addProperty(entry.getKey(), (Number) entry.getValue()); - else if (entry.getValue() instanceof Boolean) - obj.addProperty(entry.getKey(), (Boolean) entry.getValue()); - else if (entry.getValue() instanceof Character) - obj.addProperty(entry.getKey(), (Character) entry.getValue()); - else { - JsonElement jsonElement = gson.toJsonTree(entry.getValue()); - if (jsonElement.isJsonArray()) { - obj.add(entry.getKey(), jsonElement.getAsJsonArray()); - } else { - obj.add(entry.getKey(), jsonElement.getAsJsonObject()); - } - } - } - } - {{/isAdditionalPropertiesTrue}} - elementAdapter.write(out, obj); - } - - @Override - public {{classname}} read(JsonReader in) throws IOException { - JsonElement jsonElement = elementAdapter.read(in); -{{^vendorExtensions.x-skip-validation}} - validateJsonElement(jsonElement); -{{/vendorExtensions.x-skip-validation}} -{{#vendorExtensions.x-skip-validation}} - // vertex:TODO: Skipping validation during deserialization of the JSON element based on configuration - // validateJsonElement(jsonElement); -{{/vendorExtensions.x-skip-validation}} - {{#isAdditionalPropertiesTrue}} - JsonObject jsonObj = jsonElement.getAsJsonObject(); - // store additional fields in the deserialized instance - {{classname}} instance = thisAdapter.fromJsonTree(jsonObj); - for (Map.Entry entry : jsonObj.entrySet()) { - if (!openapiFields.contains(entry.getKey())) { - if (entry.getValue().isJsonPrimitive()) { // primitive type - if (entry.getValue().getAsJsonPrimitive().isString()) - instance.putAdditionalProperty(entry.getKey(), entry.getValue().getAsString()); - else if (entry.getValue().getAsJsonPrimitive().isNumber()) - instance.putAdditionalProperty(entry.getKey(), entry.getValue().getAsNumber()); - else if (entry.getValue().getAsJsonPrimitive().isBoolean()) - instance.putAdditionalProperty(entry.getKey(), entry.getValue().getAsBoolean()); - else - throw new IllegalArgumentException(String.format("The field `%s` has unknown primitive type. Value: %s", entry.getKey(), entry.getValue().toString())); - } else if (entry.getValue().isJsonArray()) { - instance.putAdditionalProperty(entry.getKey(), gson.fromJson(entry.getValue(), List.class)); - } else { // JSON object - instance.putAdditionalProperty(entry.getKey(), gson.fromJson(entry.getValue(), HashMap.class)); - } - } - } - return instance; - {{/isAdditionalPropertiesTrue}} - {{^isAdditionalPropertiesTrue}} - return thisAdapter.fromJsonTree(jsonElement); - {{/isAdditionalPropertiesTrue}} - } - - }.nullSafe(); - } - } -{{/hasChildren}} - - /** - * Create an instance of {{classname}} given an JSON string - * - * @param jsonString JSON string - * @return An instance of {{classname}} - * @throws IOException if the JSON string is invalid with respect to {{classname}} - */ - public static {{{classname}}} fromJson(String jsonString) throws IOException { - return JSON.getGson().fromJson(jsonString, {{{classname}}}.class); - } - - /** - * Convert an instance of {{classname}} to an JSON string - * - * @return JSON string - */ - public String toJson() { - return JSON.getGson().toJson(this); - } -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..3e273f3 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +openapiGeneratorVersion=7.14.0 diff --git a/openapi-generator-plugin/src/main/resources/patches/ApiClient.mustache.patch b/openapi-generator-plugin/src/main/resources/patches/ApiClient.mustache.patch new file mode 100644 index 0000000..ac736c4 --- /dev/null +++ b/openapi-generator-plugin/src/main/resources/patches/ApiClient.mustache.patch @@ -0,0 +1,70 @@ +--- ApiClient.mustache ++++ ApiClient.mustache +@@ -37,6 +37,8 @@ + import java.net.URI; + import java.net.URLConnection; + import java.net.URLEncoder; ++import java.nio.charset.Charset; ++import java.nio.charset.StandardCharsets; + import java.nio.file.Files; + import java.nio.file.Paths; + import java.security.GeneralSecurityException; +@@ -1922,17 +1924,48 @@ + * @throws {{invokerPackage}}.ApiException If fail to serialize the request body object into a string + */ + protected String requestBodyToString(RequestBody requestBody) throws ApiException { +- if (requestBody != null) { +- try { +- final Buffer buffer = new Buffer(); +- requestBody.writeTo(buffer); +- return buffer.readUtf8(); +- } catch (final IOException e) { +- throw new ApiException(e); +- } ++ // Payload is only used by some auth schemes; for large/binary uploads we must not buffer. ++ if (requestBody == null) { ++ return ""; + } + +- // empty http request body +- return ""; ++ // If you *know* none of your auth implementations use payload, you can simply: ++ // return ""; ++ ++ // Otherwise, keep a safe, capped implementation: ++ try { ++ final MediaType contentType = requestBody.contentType(); ++ final long maxBytes = 64L * 1024L; // 64KiB cap ++ ++ // Do not attempt to stringify binary bodies. ++ if (contentType != null) { ++ final String type = contentType.type(); ++ final String subtype = contentType.subtype(); ++ final boolean textLike = ++ "text".equalsIgnoreCase(type) || ++ (subtype != null && ( ++ subtype.toLowerCase().contains("json") || ++ subtype.toLowerCase().contains("xml") || ++ subtype.toLowerCase().contains("x-www-form-urlencoded") ++ )); ++ if (!textLike) return ""; ++ } ++ ++ final long declaredLength = requestBody.contentLength(); ++ if (declaredLength > maxBytes) { ++ return ""; ++ } ++ ++ final Buffer buffer = new Buffer(); ++ requestBody.writeTo(buffer); ++ ++ final long toRead = Math.min(buffer.size(), maxBytes); ++ final Charset charset = ++ (contentType != null && contentType.charset() != null) ? contentType.charset() : StandardCharsets.UTF_8; ++ ++ return buffer.readString(toRead, charset); ++ } catch (final IOException e) { ++ throw new ApiException(e); ++ } + } + } diff --git a/openapi-generator-plugin/src/main/resources/patches/oneof_model.mustache.patch b/openapi-generator-plugin/src/main/resources/patches/oneof_model.mustache.patch new file mode 100644 index 0000000..73825dd --- /dev/null +++ b/openapi-generator-plugin/src/main/resources/patches/oneof_model.mustache.patch @@ -0,0 +1,41 @@ +--- oneof_model.mustache ++++ oneof_model.mustache +@@ -33,6 +33,8 @@ + + import {{invokerPackage}}.JSON; + ++// This source file was generated using a Vertex modified version of the oneof_model.mustache template. ++ + {{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{>xmlAnnotation}} + public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-implements}} implements {{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-implements}} { + private static final Logger log = Logger.getLogger({{classname}}.class.getName()); +@@ -383,6 +385,29 @@ + * @throws IOException if the JSON Element is invalid with respect to {{classname}} + */ + public static void validateJsonElement(JsonElement jsonElement) throws IOException { ++ {{#useOneOfDiscriminatorLookup}} ++ {{#discriminator}} ++ // Vertex customization -- use the same behavior as the read method. ++ JsonObject jsonObject = jsonElement != null ? jsonElement.getAsJsonObject() : null; ++ ++ if (jsonObject != null) { ++ if (jsonObject.get("type") == null) { ++ log.log(Level.WARNING, "Failed to lookup discriminator value for {{classname}} as `{{{propertyBaseName}}}` was not found in the payload or the payload is empty."); ++ } else { ++ // look up the discriminator value in the field `{{{propertyBaseName}}}` ++ switch (jsonObject.get("{{{propertyBaseName}}}").getAsString()) { ++ {{#mappedModels}} ++ case "{{{mappingName}}}": ++ {{modelName}}.validateJsonElement(jsonElement); ++ return; ++ {{/mappedModels}} ++ default: ++ log.log(Level.WARNING, String.format("Failed to lookup discriminator value `%s` for {{classname}}. Possible values:{{#mappedModels}} {{{mappingName}}}{{/mappedModels}}", jsonObject.get("{{{propertyBaseName}}}").getAsString())); ++ } ++ } ++ } ++ {{/discriminator}} ++ {{/useOneOfDiscriminatorLookup}} + // validate oneOf schemas one by one + int validCount = 0; + ArrayList errorMessages = new ArrayList<>(); diff --git a/openapi-generator-plugin/src/main/resources/patches/pojo.mustache.patch b/openapi-generator-plugin/src/main/resources/patches/pojo.mustache.patch new file mode 100644 index 0000000..1f08cfc --- /dev/null +++ b/openapi-generator-plugin/src/main/resources/patches/pojo.mustache.patch @@ -0,0 +1,89 @@ +--- pojo.mustache ++++ pojo.mustache +@@ -21,6 +21,8 @@ + + import {{invokerPackage}}.JSON; + ++// This source file was generated using a Vertex modified version of the pojo.mustache template. ++ + /** + * {{description}}{{^description}}{{classname}}{{/description}}{{#isDeprecated}} + * @deprecated{{/isDeprecated}} +\ No newline at end of file +@@ -49,10 +51,12 @@ + {{#isEnum}} + {{^isContainer}} + {{>modelInnerEnum}} ++ + {{/isContainer}} + {{#isContainer}} + {{#mostInnerItems}} + {{>modelInnerEnum}} ++ + {{/mostInnerItems}} + {{/isContainer}} + {{/isEnum}} +\ No newline at end of file +@@ -70,7 +74,7 @@ + {{#vendorExtensions.x-field-extra-annotation}} + {{{vendorExtensions.x-field-extra-annotation}}} + {{/vendorExtensions.x-field-extra-annotation}} +- {{>nullable_var_annotations}} ++ {{>nullable_var_annotations}}{{! prevent indent}} + {{#isDiscriminator}}protected{{/isDiscriminator}}{{^isDiscriminator}}private{{/isDiscriminator}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}; + + {{/vars}} +\ No newline at end of file +@@ -159,9 +163,10 @@ + {{#deprecated}} + @Deprecated + {{/deprecated}} +- {{>nullable_var_annotations}} ++ {{>nullable_var_annotations}}{{! prevent indent}} + {{#useBeanValidation}} + {{>beanValidation}} ++ + {{/useBeanValidation}} + {{#swagger1AnnotationLibrary}} + @ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}value = "{{{description}}}") +\ No newline at end of file +@@ -187,6 +192,7 @@ + {{/vars}} + {{>libraries/okhttp-gson/additional_properties}} + ++ + @Override + public boolean equals(Object o) { + {{#useReflectionEqualsHashCode}} +\ No newline at end of file +@@ -352,7 +358,6 @@ + } + {{^hasChildren}} + {{^isAdditionalPropertiesTrue}} +- + Set> entries = jsonElement.getAsJsonObject().entrySet(); + // check to see if the JSON string contains additional fields + for (Map.Entry entry : entries) { +\ No newline at end of file +@@ -534,7 +539,13 @@ + @Override + public {{classname}} read(JsonReader in) throws IOException { + JsonElement jsonElement = elementAdapter.read(in); ++{{^vendorExtensions.x-skip-validation}} + validateJsonElement(jsonElement); ++{{/vendorExtensions.x-skip-validation}} ++{{#vendorExtensions.x-skip-validation}} ++ // vertex:TODO: Skipping validation during deserialization of the JSON element based on configuration ++ // validateJsonElement(jsonElement); ++{{/vendorExtensions.x-skip-validation}} + {{#isAdditionalPropertiesTrue}} + JsonObject jsonObj = jsonElement.getAsJsonObject(); + // store additional fields in the deserialized instance +\ No newline at end of file +@@ -588,4 +599,4 @@ + public String toJson() { + return JSON.getGson().toJson(this); + } +-} ++} +\ No newline at end of file diff --git a/openapi-generator-plugin/src/main/resources/vertex-java/pojo.mustache b/openapi-generator-plugin/src/main/resources/vertex-java/pojo.mustache deleted file mode 100644 index e1f16c3..0000000 --- a/openapi-generator-plugin/src/main/resources/vertex-java/pojo.mustache +++ /dev/null @@ -1,601 +0,0 @@ -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import {{invokerPackage}}.JSON; - -/** - * {{description}}{{^description}}{{classname}}{{/description}}{{#isDeprecated}} - * @deprecated{{/isDeprecated}} - */{{#isDeprecated}} -@Deprecated{{/isDeprecated}} -{{#swagger1AnnotationLibrary}} -{{#description}} -@ApiModel(description = "{{{.}}}") -{{/description}} -{{/swagger1AnnotationLibrary}} -{{#swagger2AnnotationLibrary}} -{{#description}} -@Schema(description = "{{{.}}}") -{{/description}} -{{/swagger2AnnotationLibrary}} -{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} -{{#vendorExtensions.x-class-extra-annotation}} -{{{vendorExtensions.x-class-extra-annotation}}} -{{/vendorExtensions.x-class-extra-annotation}} -public class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{{#vendorExtensions.x-implements}}{{#-first}}implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{#-last}} {{/-last}}{{/vendorExtensions.x-implements}}{ -{{#serializableModel}} - private static final long serialVersionUID = 1L; - -{{/serializableModel}} - {{#vars}} - {{#isEnum}} - {{^isContainer}} -{{>modelInnerEnum}} - - {{/isContainer}} - {{#isContainer}} - {{#mostInnerItems}} -{{>modelInnerEnum}} - - {{/mostInnerItems}} - {{/isContainer}} - {{/isEnum}} - public static final String SERIALIZED_NAME_{{nameInSnakeCase}} = "{{baseName}}"; - {{#withXml}} - @Xml{{#isXmlAttribute}}Attribute{{/isXmlAttribute}}{{^isXmlAttribute}}Element{{/isXmlAttribute}}(name = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}}) - {{#isXmlWrapped}} - @XmlElementWrapper(name = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}}) - {{/isXmlWrapped}} - {{/withXml}} - {{#deprecated}} - @Deprecated - {{/deprecated}} - @SerializedName(SERIALIZED_NAME_{{nameInSnakeCase}}) - {{#vendorExtensions.x-field-extra-annotation}} - {{{vendorExtensions.x-field-extra-annotation}}} - {{/vendorExtensions.x-field-extra-annotation}} - {{>nullable_var_annotations}}{{! prevent indent}} - {{#isDiscriminator}}protected{{/isDiscriminator}}{{^isDiscriminator}}private{{/isDiscriminator}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}; - - {{/vars}} - public {{classname}}() { - {{#parent}} - {{#parcelableModel}} - super(); - {{/parcelableModel}} - {{/parent}} - {{#discriminator}} - {{#discriminator.isEnum}} -{{#readWriteVars}}{{#isDiscriminator}}{{#defaultValue}} - this.{{name}} = {{defaultValue}}; -{{/defaultValue}}{{/isDiscriminator}}{{/readWriteVars}} - {{/discriminator.isEnum}} - {{^discriminator.isEnum}} - this.{{{discriminatorName}}} = this.getClass().getSimpleName(); - {{/discriminator.isEnum}} - {{/discriminator}} - } - {{#vendorExtensions.x-has-readonly-properties}} - {{^withXml}} - - public {{classname}}( - {{#readOnlyVars}} - {{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}} - {{/readOnlyVars}} - ) { - this(); - {{#readOnlyVars}} - this.{{name}} = {{name}}; - {{/readOnlyVars}} - } - {{/withXml}} - {{/vendorExtensions.x-has-readonly-properties}} - {{#vars}} - - {{^isReadOnly}} - {{#deprecated}} - @Deprecated - {{/deprecated}} - public {{classname}} {{name}}({{>nullable_var_annotations}} {{{datatypeWithEnum}}} {{name}}) { - this.{{name}} = {{name}}; - return this; - } - {{#isArray}} - - public {{classname}} add{{nameInPascalCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { - if (this.{{name}} == null) { - this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}}; - } - this.{{name}}.add({{name}}Item); - return this; - } - {{/isArray}} - {{#isMap}} - - public {{classname}} put{{nameInPascalCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) { - if (this.{{name}} == null) { - this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}}; - } - this.{{name}}.put(key, {{name}}Item); - return this; - } - {{/isMap}} - - {{/isReadOnly}} - /** - {{#description}} - * {{.}} - {{/description}} - {{^description}} - * Get {{name}} - {{/description}} - {{#minimum}} - * minimum: {{.}} - {{/minimum}} - {{#maximum}} - * maximum: {{.}} - {{/maximum}} - * @return {{name}} - {{#deprecated}} - * @deprecated - {{/deprecated}} - */ -{{#deprecated}} - @Deprecated -{{/deprecated}} - {{>nullable_var_annotations}}{{! prevent indent}} -{{#useBeanValidation}} -{{>beanValidation}} - -{{/useBeanValidation}} -{{#swagger1AnnotationLibrary}} - @ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}value = "{{{description}}}") -{{/swagger1AnnotationLibrary}} -{{#swagger2AnnotationLibrary}} - @Schema({{#example}}example = "{{{.}}}", {{/example}}requiredMode = {{#required}}Schema.RequiredMode.REQUIRED{{/required}}{{^required}}Schema.RequiredMode.NOT_REQUIRED{{/required}}, description = "{{{description}}}") -{{/swagger2AnnotationLibrary}} -{{#vendorExtensions.x-extra-annotation}} - {{{vendorExtensions.x-extra-annotation}}} -{{/vendorExtensions.x-extra-annotation}} - public {{{datatypeWithEnum}}} {{getter}}() { - return {{name}}; - } - - {{^isReadOnly}} -{{#vendorExtensions.x-setter-extra-annotation}} {{{vendorExtensions.x-setter-extra-annotation}}} -{{/vendorExtensions.x-setter-extra-annotation}}{{#deprecated}} @Deprecated -{{/deprecated}} public void {{setter}}({{>nullable_var_annotations}} {{{datatypeWithEnum}}} {{name}}) { - this.{{name}} = {{name}}; - } - {{/isReadOnly}} - - {{/vars}} -{{>libraries/okhttp-gson/additional_properties}} - - - @Override - public boolean equals(Object o) { - {{#useReflectionEqualsHashCode}} - return EqualsBuilder.reflectionEquals(this, o, false, null, true); - {{/useReflectionEqualsHashCode}} - {{^useReflectionEqualsHashCode}} - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - }{{#hasVars}} - {{classname}} {{classVarName}} = ({{classname}}) o; - return {{#vars}}{{#isByteArray}}Arrays{{/isByteArray}}{{^isByteArray}}Objects{{/isByteArray}}.equals(this.{{name}}, {{classVarName}}.{{name}}){{^-last}} && - {{/-last}}{{/vars}}{{#isAdditionalPropertiesTrue}}&& - Objects.equals(this.additionalProperties, {{classVarName}}.additionalProperties){{/isAdditionalPropertiesTrue}}{{#parent}} && - super.equals(o){{/parent}};{{/hasVars}}{{^hasVars}} - return {{#parent}}super.equals(o){{/parent}}{{^parent}}true{{/parent}};{{/hasVars}} - {{/useReflectionEqualsHashCode}} - }{{#vendorExtensions.x-jackson-optional-nullable-helpers}} - - private static boolean equalsNullable(JsonNullable a, JsonNullable b) { - return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); - }{{/vendorExtensions.x-jackson-optional-nullable-helpers}} - - @Override - public int hashCode() { - {{#useReflectionEqualsHashCode}} - return HashCodeBuilder.reflectionHashCode(this); - {{/useReflectionEqualsHashCode}} - {{^useReflectionEqualsHashCode}} - return Objects.hash({{#vars}}{{^isByteArray}}{{name}}{{/isByteArray}}{{#isByteArray}}Arrays.hashCode({{name}}){{/isByteArray}}{{^-last}}, {{/-last}}{{/vars}}{{#parent}}{{#hasVars}}, {{/hasVars}}super.hashCode(){{/parent}}{{#isAdditionalPropertiesTrue}}{{#hasVars}}, {{/hasVars}}{{^hasVars}}{{#parent}}, {{/parent}}{{/hasVars}}additionalProperties{{/isAdditionalPropertiesTrue}}); - {{/useReflectionEqualsHashCode}} - }{{#vendorExtensions.x-jackson-optional-nullable-helpers}} - - private static int hashCodeNullable(JsonNullable a) { - if (a == null) { - return 1; - } - return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; - }{{/vendorExtensions.x-jackson-optional-nullable-helpers}} - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class {{classname}} {\n"); - {{#parent}} - sb.append(" ").append(toIndentedString(super.toString())).append("\n"); - {{/parent}} - {{#vars}} - sb.append(" {{name}}: ").append({{#isPassword}}"*"{{/isPassword}}{{^isPassword}}toIndentedString({{name}}){{/isPassword}}).append("\n"); - {{/vars}} -{{#isAdditionalPropertiesTrue}} - sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); -{{/isAdditionalPropertiesTrue}} - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces - * (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } - -{{#parcelableModel}} - - public void writeToParcel(Parcel out, int flags) { -{{#model}} -{{#isArray}} - out.writeList(this); -{{/isArray}} -{{^isArray}} -{{#parent}} - super.writeToParcel(out, flags); -{{/parent}} -{{#vars}} - out.writeValue({{name}}); -{{/vars}} -{{/isArray}} -{{/model}} - } - - {{classname}}(Parcel in) { -{{#isArray}} - in.readTypedList(this, {{arrayModelType}}.CREATOR); -{{/isArray}} -{{^isArray}} -{{#parent}} - super(in); -{{/parent}} -{{#vars}} -{{#isPrimitiveType}} - {{name}} = ({{{datatypeWithEnum}}})in.readValue(null); -{{/isPrimitiveType}} -{{^isPrimitiveType}} - {{name}} = ({{{datatypeWithEnum}}})in.readValue({{complexType}}.class.getClassLoader()); -{{/isPrimitiveType}} -{{/vars}} -{{/isArray}} - } - - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<{{classname}}> CREATOR = new Parcelable.Creator<{{classname}}>() { - public {{classname}} createFromParcel(Parcel in) { -{{#model}} -{{#isArray}} - {{classname}} result = new {{classname}}(); - result.addAll(in.readArrayList({{arrayModelType}}.class.getClassLoader())); - return result; -{{/isArray}} -{{^isArray}} - return new {{classname}}(in); -{{/isArray}} -{{/model}} - } - public {{classname}}[] newArray(int size) { - return new {{classname}}[size]; - } - }; -{{/parcelableModel}} - - public static HashSet openapiFields; - public static HashSet openapiRequiredFields; - - static { - // a set of all properties/fields (JSON key names) - {{#hasVars}} - openapiFields = new HashSet(Arrays.asList({{#allVars}}"{{baseName}}"{{^-last}}, {{/-last}}{{/allVars}})); - {{/hasVars}} - {{^hasVars}} - openapiFields = new HashSet(0); - {{/hasVars}} - - // a set of required properties/fields (JSON key names) - {{#hasRequired}} - openapiRequiredFields = new HashSet(Arrays.asList({{#requiredVars}}"{{baseName}}"{{^-last}}, {{/-last}}{{/requiredVars}})); - {{/hasRequired}} - {{^hasRequired}} - openapiRequiredFields = new HashSet(0); - {{/hasRequired}} - } - - /** - * Validates the JSON Element and throws an exception if issues found - * - * @param jsonElement JSON Element - * @throws IOException if the JSON Element is invalid with respect to {{classname}} - */ - public static void validateJsonElement(JsonElement jsonElement) throws IOException { - if (jsonElement == null) { - if (!{{classname}}.openapiRequiredFields.isEmpty()) { // has required fields but JSON element is null - throw new IllegalArgumentException(String.format("The required field(s) %s in {{{classname}}} is not found in the empty JSON string", {{classname}}.openapiRequiredFields.toString())); - } - } - {{^hasChildren}} - {{^isAdditionalPropertiesTrue}} - - Set> entries = jsonElement.getAsJsonObject().entrySet(); - // check to see if the JSON string contains additional fields - for (Map.Entry entry : entries) { - if (!{{classname}}.openapiFields.contains(entry.getKey())) { - throw new IllegalArgumentException(String.format("The field `%s` in the JSON string is not defined in the `{{classname}}` properties. JSON: %s", entry.getKey(), jsonElement.toString())); - } - } - {{/isAdditionalPropertiesTrue}} - {{#requiredVars}} - {{#-first}} - - // check to make sure all required properties/fields are present in the JSON string - for (String requiredField : {{classname}}.openapiRequiredFields) { - if (jsonElement.getAsJsonObject().get(requiredField) == null) { - throw new IllegalArgumentException(String.format("The required field `%s` is not found in the JSON string: %s", requiredField, jsonElement.toString())); - } - } - {{/-first}} - {{/requiredVars}} - {{/hasChildren}} - {{^discriminator}} - {{#hasVars}} - JsonObject jsonObj = jsonElement.getAsJsonObject(); - {{/hasVars}} - {{#vars}} - {{#isArray}} - {{#items.isModel}} - {{#required}} - // ensure the json data is an array - if (!jsonObj.get("{{{baseName}}}").isJsonArray()) { - throw new IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj.get("{{{baseName}}}").toString())); - } - - JsonArray jsonArray{{name}} = jsonObj.getAsJsonArray("{{{baseName}}}"); - // validate the required field `{{{baseName}}}` (array) - for (int i = 0; i < jsonArray{{name}}.size(); i++) { - {{{items.dataType}}}.validateJsonElement(jsonArray{{name}}.get(i)); - }; - {{/required}} - {{^required}} - if (jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull()) { - JsonArray jsonArray{{name}} = jsonObj.getAsJsonArray("{{{baseName}}}"); - if (jsonArray{{name}} != null) { - // ensure the json data is an array - if (!jsonObj.get("{{{baseName}}}").isJsonArray()) { - throw new IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj.get("{{{baseName}}}").toString())); - } - - // validate the optional field `{{{baseName}}}` (array) - for (int i = 0; i < jsonArray{{name}}.size(); i++) { - {{{items.dataType}}}.validateJsonElement(jsonArray{{name}}.get(i)); - }; - } - } - {{/required}} - {{/items.isModel}} - {{^items.isModel}} - {{^required}} - // ensure the optional json data is an array if present - if (jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull() && !jsonObj.get("{{{baseName}}}").isJsonArray()) { - throw new IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj.get("{{{baseName}}}").toString())); - } - {{/required}} - {{#required}} - // ensure the required json array is present - if (jsonObj.get("{{{baseName}}}") == null) { - throw new IllegalArgumentException("Expected the field `linkedContent` to be an array in the JSON string but got `null`"); - } else if (!jsonObj.get("{{{baseName}}}").isJsonArray()) { - throw new IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj.get("{{{baseName}}}").toString())); - } - {{/required}} - {{/items.isModel}} - {{/isArray}} - {{^isContainer}} - {{#isString}} - if ({{#notRequiredOrIsNullable}}(jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull()) && {{/notRequiredOrIsNullable}}!jsonObj.get("{{{baseName}}}").isJsonPrimitive()) { - throw new IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be a primitive type in the JSON string but got `%s`", jsonObj.get("{{{baseName}}}").toString())); - } - {{/isString}} - {{#isModel}} - {{#required}} - // validate the required field `{{{baseName}}}` - {{{dataType}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - {{/required}} - {{^required}} - // validate the optional field `{{{baseName}}}` - if (jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull()) { - {{{dataType}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - } - {{/required}} - {{/isModel}} - {{#isEnum}} - {{#required}} - // validate the required field `{{{baseName}}}` - {{{datatypeWithEnum}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - {{/required}} - {{^required}} - // validate the optional field `{{{baseName}}}` - if (jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull()) { - {{{datatypeWithEnum}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - } - {{/required}} - {{/isEnum}} - {{#isEnumRef}} - {{#required}} - // validate the required field `{{{baseName}}}` - {{{dataType}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - {{/required}} - {{^required}} - // validate the optional field `{{{baseName}}}` - if (jsonObj.get("{{{baseName}}}") != null && !jsonObj.get("{{{baseName}}}").isJsonNull()) { - {{{dataType}}}.validateJsonElement(jsonObj.get("{{{baseName}}}")); - } - {{/required}} - {{/isEnumRef}} - {{/isContainer}} - {{/vars}} - {{/discriminator}} - {{#hasChildren}} - {{#discriminator}} - - String discriminatorValue = jsonElement.getAsJsonObject().get("{{{propertyBaseName}}}").getAsString(); - switch (discriminatorValue) { - {{#mappedModels}} - case "{{mappingName}}": - {{modelName}}.validateJsonElement(jsonElement); - break; - {{/mappedModels}} - default: - throw new IllegalArgumentException(String.format("The value of the `{{{propertyBaseName}}}` field `%s` does not match any key defined in the discriminator's mapping.", discriminatorValue)); - } - {{/discriminator}} - {{/hasChildren}} - } - -{{^hasChildren}} - public static class CustomTypeAdapterFactory implements TypeAdapterFactory { - @SuppressWarnings("unchecked") - @Override - public TypeAdapter create(Gson gson, TypeToken type) { - if (!{{classname}}.class.isAssignableFrom(type.getRawType())) { - return null; // this class only serializes '{{classname}}' and its subtypes - } - final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class); - final TypeAdapter<{{classname}}> thisAdapter - = gson.getDelegateAdapter(this, TypeToken.get({{classname}}.class)); - - return (TypeAdapter) new TypeAdapter<{{classname}}>() { - @Override - public void write(JsonWriter out, {{classname}} value) throws IOException { - JsonObject obj = thisAdapter.toJsonTree(value).getAsJsonObject(); - {{#isAdditionalPropertiesTrue}} - obj.remove("additionalProperties"); - // serialize additional properties - if (value.getAdditionalProperties() != null) { - for (Map.Entry entry : value.getAdditionalProperties().entrySet()) { - if (entry.getValue() instanceof String) - obj.addProperty(entry.getKey(), (String) entry.getValue()); - else if (entry.getValue() instanceof Number) - obj.addProperty(entry.getKey(), (Number) entry.getValue()); - else if (entry.getValue() instanceof Boolean) - obj.addProperty(entry.getKey(), (Boolean) entry.getValue()); - else if (entry.getValue() instanceof Character) - obj.addProperty(entry.getKey(), (Character) entry.getValue()); - else { - JsonElement jsonElement = gson.toJsonTree(entry.getValue()); - if (jsonElement.isJsonArray()) { - obj.add(entry.getKey(), jsonElement.getAsJsonArray()); - } else { - obj.add(entry.getKey(), jsonElement.getAsJsonObject()); - } - } - } - } - {{/isAdditionalPropertiesTrue}} - elementAdapter.write(out, obj); - } - - @Override - public {{classname}} read(JsonReader in) throws IOException { - JsonElement jsonElement = elementAdapter.read(in); -{{^vendorExtensions.x-skip-validation}} - validateJsonElement(jsonElement); -{{/vendorExtensions.x-skip-validation}} -{{#vendorExtensions.x-skip-validation}} - // vertex:TODO: Skipping validation during deserialization of the JSON element based on configuration - // validateJsonElement(jsonElement); -{{/vendorExtensions.x-skip-validation}} - {{#isAdditionalPropertiesTrue}} - JsonObject jsonObj = jsonElement.getAsJsonObject(); - // store additional fields in the deserialized instance - {{classname}} instance = thisAdapter.fromJsonTree(jsonObj); - for (Map.Entry entry : jsonObj.entrySet()) { - if (!openapiFields.contains(entry.getKey())) { - if (entry.getValue().isJsonPrimitive()) { // primitive type - if (entry.getValue().getAsJsonPrimitive().isString()) - instance.putAdditionalProperty(entry.getKey(), entry.getValue().getAsString()); - else if (entry.getValue().getAsJsonPrimitive().isNumber()) - instance.putAdditionalProperty(entry.getKey(), entry.getValue().getAsNumber()); - else if (entry.getValue().getAsJsonPrimitive().isBoolean()) - instance.putAdditionalProperty(entry.getKey(), entry.getValue().getAsBoolean()); - else - throw new IllegalArgumentException(String.format("The field `%s` has unknown primitive type. Value: %s", entry.getKey(), entry.getValue().toString())); - } else if (entry.getValue().isJsonArray()) { - instance.putAdditionalProperty(entry.getKey(), gson.fromJson(entry.getValue(), List.class)); - } else { // JSON object - instance.putAdditionalProperty(entry.getKey(), gson.fromJson(entry.getValue(), HashMap.class)); - } - } - } - return instance; - {{/isAdditionalPropertiesTrue}} - {{^isAdditionalPropertiesTrue}} - return thisAdapter.fromJsonTree(jsonElement); - {{/isAdditionalPropertiesTrue}} - } - - }.nullSafe(); - } - } -{{/hasChildren}} - - /** - * Create an instance of {{classname}} given an JSON string - * - * @param jsonString JSON string - * @return An instance of {{classname}} - * @throws IOException if the JSON string is invalid with respect to {{classname}} - */ - public static {{{classname}}} fromJson(String jsonString) throws IOException { - return JSON.getGson().fromJson(jsonString, {{{classname}}}.class); - } - - /** - * Convert an instance of {{classname}} to an JSON string - * - * @return JSON string - */ - public String toJson() { - return JSON.getGson().toJson(this); - } -} \ No newline at end of file