From f2c6869436a23a5ad38fa5388f7a44de968afeb0 Mon Sep 17 00:00:00 2001 From: Carlo Goetz Date: Wed, 28 Jan 2026 17:50:43 +0100 Subject: [PATCH 1/2] feat(java) update generator from 7.15 to 7.19, port template changes --- languages/java/templates/ApiClient.mustache | 145 ++++++++++++++---- .../java/templates/build.gradle.mustache | 2 +- scripts/generate-sdk/generate-sdk.sh | 2 +- 3 files changed, 120 insertions(+), 29 deletions(-) diff --git a/languages/java/templates/ApiClient.mustache b/languages/java/templates/ApiClient.mustache index 9f8a693..c2e76bd 100644 --- a/languages/java/templates/ApiClient.mustache +++ b/languages/java/templates/ApiClient.mustache @@ -1,5 +1,5 @@ {{! This template was customized to initialize the ApiClient with the CoreConfiguration to easily setup the KeyFlow Authentication and custom endpoints. }} -{{! Original template: https://github.com/OpenAPITools/openapi-generator/blob/v7.14.0/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache }} +{{! Original template: https://github.com/OpenAPITools/openapi-generator/blob/v7.19.0/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache }} {{>licenseInfo}} @@ -75,10 +75,12 @@ import {{invokerPackage}}.auth.OAuthFlow; import {{invokerPackage}}.auth.AWS4Auth; {{/withAWSV4Signature}} +{{! BEGIN - Added imports for core module }} import cloud.stackit.sdk.core.auth.SetupAuth; import cloud.stackit.sdk.core.config.CoreConfiguration; import cloud.stackit.sdk.core.exception.ApiException; import cloud.stackit.sdk.core.KeyFlowAuthenticator; +{{! END - Added imports for core module }} /** *

ApiClient class.

@@ -118,6 +120,10 @@ public class ApiClient { protected Map defaultCookieMap = new HashMap(); protected String tempFolderPath = null; + {{! BEGIN - deleted field authentications, we use our own Authenticator + protected Map authentications; + }} + protected DateFormat dateFormat; protected DateFormat datetimeFormat; protected boolean lenientDatetimeFormat; @@ -126,22 +132,25 @@ public class ApiClient { protected InputStream sslCaCert; protected boolean verifyingSsl; protected KeyManager[] keyManagers; + protected String tlsServerName; protected OkHttpClient httpClient; protected JSON json; protected HttpLoggingInterceptor loggingInterceptor; + {{! BEGIN - Added CoreConfiguration field to ApiClient }} protected CoreConfiguration configuration; + {{! END - Added CoreConfiguration field to ApiClient }} {{#dynamicOperations}} protected Map operationLookupMap = new HashMap<>(); {{/dynamicOperations}} {{! BEGIN - Removed ApiClient constructor and replaced it with a custom constructors which create the ApiClient with the CoreConfiguration }} - /** - * Basic constructor for ApiClient. - * + /** + * Basic constructor for ApiClient. + * * Not recommended for production use, use the one with the OkHttpClient parameter instead. * * @throws IOException thrown when a file can not be found @@ -151,8 +160,8 @@ public class ApiClient { } /** - * Basic constructor for ApiClient - * + * Basic constructor for ApiClient + * * Not recommended for production use, use the one with the OkHttpClient parameter instead. * * @param config a {@link cloud.stackit.sdk.core.config.CoreConfiguration} object @@ -259,7 +268,7 @@ public class ApiClient { } } RetryingOAuth retryingOAuth = new RetryingOAuth(tokenUrl, clientId, OAuthFlow.{{#lambda.uppercase}}{{#lambda.snakecase}}{{flow}}{{/lambda.snakecase}}{{/lambda.uppercase}}, clientSecret, parameters); - authentications.put( + {authentications.put( "{{name}}", retryingOAuth ); @@ -303,6 +312,9 @@ public class ApiClient { // Set default User-Agent. setUserAgent("{{{httpUserAgent}}}{{^httpUserAgent}}OpenAPI-Generator/{{{artifactVersion}}}/java{{/httpUserAgent}}"); + {{! BEGIN - delete line setting authentications + authentications = new HashMap(); + }} {{#dynamicOperations}} OpenAPI openAPI = new OpenAPIV3Parser().read("openapi/openapi.yaml"); @@ -322,8 +334,8 @@ public class ApiClient { /** * Set base path * - * @param basePath Base path of the URL (e.g {{{basePath}}} - * @return An instance of OkHttpClient + * @param basePath Base path of the URL (e.g {{{basePath}}}) + * @return An instance of ApiClient */ public ApiClient setBasePath(String basePath) { this.basePath = basePath; @@ -456,6 +468,29 @@ public class ApiClient { return this; } + /** + * Get TLS server name for SNI (Server Name Indication). + * + * @return The TLS server name + */ + public String getTlsServerName() { + return tlsServerName; + } + + /** + * Set TLS server name for SNI (Server Name Indication). + * This is used to verify the server certificate against a specific hostname + * instead of the hostname in the URL. + * + * @param tlsServerName The TLS server name to use for certificate verification + * @return ApiClient + */ + public ApiClient setTlsServerName(String tlsServerName) { + this.tlsServerName = tlsServerName; + applySslSettings(); + return this; + } + /** *

Getter for the field dateFormat.

* @@ -533,6 +568,26 @@ public class ApiClient { JSON.setLenientOnJson(lenientOnJson); return this; } + {{! BEGIN - delete authentications getter, we use our own Authenticator + /** + * 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}} /** @@ -559,6 +614,15 @@ public class ApiClient { } {{/hasHttpBearerMethods}} + {{! BEGIN - delete auth related methods, we use our own Authenticator + deleted methods: + setUsername + setPassword + setApiKey + setApiKeyPrefix + setAccessToken + setAWS4Configuration + }} /** * Set the User-Agent header's value (by adding to the default header map). * @@ -791,7 +855,7 @@ public class ApiClient { * @param value The value of the parameter. * @return A list of {@code Pair} objects. */ - public List parameterToPairs(String collectionFormat, String name, Collection value) { + public List parameterToPairs(String collectionFormat, String name, Collection value) { List params = new ArrayList(); // preconditions @@ -1027,7 +1091,7 @@ public class ApiClient { * @param response HTTP response * @param returnType The type of the Java object * @return The deserialized Java object - * @throws cloud.stackit.sdk.core.exception.ApiException If fail to deserialize response body, i.e. cannot read response body + * @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") @@ -1060,7 +1124,17 @@ public class ApiClient { } try { if (isJsonMime(contentType)) { - return JSON.deserialize(respBody.byteStream(), returnType); + if (returnType.equals(String.class)) { + String respBodyString = respBody.string(); + if (respBodyString.isEmpty()) { + return null; + } + // Use String-based deserialize for String return type with fallback + return JSON.deserialize(respBodyString, returnType); + } else { + // Use InputStream-based deserialize which supports responses > 2GB + return JSON.deserialize(respBody.byteStream(), returnType); + } } else if (returnType.equals(String.class)) { String respBodyString = respBody.string(); if (respBodyString.isEmpty()) { @@ -1087,7 +1161,7 @@ public class ApiClient { * @param obj The Java object * @param contentType The request Content-Type * @return The serialized request body - * @throws cloud.stackit.sdk.core.exception.ApiException If fail to serialize the given object + * @throws {{invokerPackage}}.ApiException If fail to serialize the given object */ public RequestBody serialize(Object obj, String contentType) throws ApiException { if (obj instanceof byte[]) { @@ -1117,7 +1191,7 @@ public class ApiClient { * Download file from the given response. * * @param response An instance of the Response object - * @throws cloud.stackit.sdk.core.exception.ApiException If fail to read file content from response and write to disk + * @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 { @@ -1181,7 +1255,7 @@ public class ApiClient { * @param Type * @param call An instance of the Call object * @return ApiResponse<T> - * @throws cloud.stackit.sdk.core.exception.ApiException If fail to execute the call + * @throws {{invokerPackage}}.ApiException If fail to execute the call */ public ApiResponse execute(Call call) throws ApiException { return execute(call, null); @@ -1196,7 +1270,7 @@ public class ApiClient { * @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 cloud.stackit.sdk.core.exception.ApiException If fail to execute the call + * @throws {{invokerPackage}}.ApiException If fail to execute the call */ public ApiResponse execute(Call call, Type returnType) throws ApiException { try { @@ -1215,7 +1289,7 @@ public class ApiClient { * @param call a {@link okhttp3.Call} object * @param returnType a {@link java.lang.reflect.Type} object * @return a {@link java.io.InputStream} object - * @throws cloud.stackit.sdk.core.exception.ApiException if any. + * @throws {{invokerPackage}}.ApiException if any. */ public InputStream executeStream(Call call, Type returnType) throws ApiException { try { @@ -1285,7 +1359,7 @@ public class ApiClient { * @param response Response * @param returnType Return type * @return Type - * @throws cloud.stackit.sdk.core.exception.ApiException If the response has an unsuccessful status code or + * @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 { @@ -1332,7 +1406,7 @@ public class ApiClient { * @param authNames The authentications to apply * @param callback Callback for upload/download progress * @return The HTTP call - * @throws cloud.stackit.sdk.core.exception.ApiException If fail to serialize the request body object + * @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); @@ -1355,7 +1429,7 @@ public class ApiClient { * @param authNames The authentications to apply * @param callback Callback for upload/download progress * @return The HTTP request - * @throws cloud.stackit.sdk.core.exception.ApiException If fail to serialize the request body object + * @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); @@ -1387,6 +1461,10 @@ public class ApiClient { List updatedQueryParams = new ArrayList<>(queryParams); + {{! BEGIN - delete updateParamsForAuth, we use our own Authenticator + // 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); @@ -1425,7 +1503,8 @@ public class ApiClient { 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() + java.util.Locale.ROOT, + "Invalid index %d when selecting the host settings. Must be less than %d", serverIndex, servers.size() )); } baseURL = servers.get(serverIndex).URL(serverVariables); @@ -1497,15 +1576,17 @@ public class ApiClient { */ public void processCookieParams(Map cookieParams, Request.Builder reqBuilder) { for (Entry param : cookieParams.entrySet()) { - reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); + reqBuilder.addHeader("Cookie", String.format(java.util.Locale.ROOT, "%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())); + reqBuilder.addHeader("Cookie", String.format(java.util.Locale.ROOT, "%s=%s", param.getKey(), param.getValue())); } } } + {{! BEGIN - delete updateParamsForAuth method, we use our own Authenticator }} + /** * Build a form-encoding request body with the given form parameters. * @@ -1567,10 +1648,10 @@ public class ApiClient { /** * Add a Content-Disposition Header for the given key and file to the MultipartBody Builder. * - * @param mpBuilder 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)); @@ -1675,7 +1756,17 @@ public class ApiClient { trustManagerFactory.init(caKeyStore); } trustManagers = trustManagerFactory.getTrustManagers(); - hostnameVerifier = OkHostnameVerifier.INSTANCE; + if (tlsServerName != null && !tlsServerName.isEmpty()) { + hostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + // Verify the certificate against tlsServerName instead of the actual hostname + return OkHostnameVerifier.INSTANCE.verify(tlsServerName, session); + } + }; + } else { + hostnameVerifier = OkHostnameVerifier.INSTANCE; + } } SSLContext sslContext = SSLContext.getInstance("TLS"); @@ -1775,7 +1866,7 @@ public class ApiClient { * * @param requestBody The HTTP request object * @return The string representation of the HTTP request body - * @throws cloud.stackit.sdk.core.exception.ApiException If fail to serialize the request body object into a string + * @throws {{invokerPackage}}.ApiException If fail to serialize the request body object into a string */ protected String requestBodyToString(RequestBody requestBody) throws ApiException { if (requestBody != null) { diff --git a/languages/java/templates/build.gradle.mustache b/languages/java/templates/build.gradle.mustache index 1bc1bee..b046a3c 100644 --- a/languages/java/templates/build.gradle.mustache +++ b/languages/java/templates/build.gradle.mustache @@ -28,7 +28,7 @@ dependencies { implementation 'io.gsonfire:gson-fire:1.9.0' implementation 'jakarta.ws.rs:jakarta.ws.rs-api:2.1.6' {{#openApiNullable}} - implementation 'org.openapitools:jackson-databind-nullable:0.2.6' + implementation 'org.openapitools:jackson-databind-nullable:0.2.8' {{/openApiNullable}} {{#withAWSV4Signature}} implementation 'software.amazon.awssdk:auth:2.20.157' diff --git a/scripts/generate-sdk/generate-sdk.sh b/scripts/generate-sdk/generate-sdk.sh index 96d579c..2ab94ea 100755 --- a/scripts/generate-sdk/generate-sdk.sh +++ b/scripts/generate-sdk/generate-sdk.sh @@ -65,7 +65,7 @@ python) java) # When the GENERATOR_VERSION changes, migrate also the templates in templates/java # Renovate: datasource=github-tags depName=OpenAPITools/openapi-generator versioning=semver - GENERATOR_VERSION="v7.15.0" + GENERATOR_VERSION="v7.19.0" ;; *) echo "SDK language not supported." From b0be92a9b85bf87eb465e19baeffa08242fa76f9 Mon Sep 17 00:00:00 2001 From: Carlo Goetz Date: Fri, 30 Jan 2026 17:48:01 +0100 Subject: [PATCH 2/2] fix(java) address review comments - add missing 7.14->7.15 changes - rm misplaced `{` - use custom ApiException --- languages/java/templates/ApiClient.mustache | 22 +++++++++---------- languages/java/templates/api.mustache | 6 ++--- .../java/templates/build.gradle.mustache | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/languages/java/templates/ApiClient.mustache b/languages/java/templates/ApiClient.mustache index c2e76bd..3cb6f3c 100644 --- a/languages/java/templates/ApiClient.mustache +++ b/languages/java/templates/ApiClient.mustache @@ -268,7 +268,7 @@ public class ApiClient { } } RetryingOAuth retryingOAuth = new RetryingOAuth(tokenUrl, clientId, OAuthFlow.{{#lambda.uppercase}}{{#lambda.snakecase}}{{flow}}{{/lambda.snakecase}}{{/lambda.uppercase}}, clientSecret, parameters); - {authentications.put( + authentications.put( "{{name}}", retryingOAuth ); @@ -1091,7 +1091,7 @@ public class ApiClient { * @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 + * @throws cloud.stackit.sdk.core.exception.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") @@ -1161,7 +1161,7 @@ public class ApiClient { * @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 + * @throws cloud.stackit.sdk.core.exception.ApiException If fail to serialize the given object */ public RequestBody serialize(Object obj, String contentType) throws ApiException { if (obj instanceof byte[]) { @@ -1191,7 +1191,7 @@ public class ApiClient { * 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 + * @throws cloud.stackit.sdk.core.exception.ApiException If fail to read file content from response and write to disk * @return Downloaded file */ public File downloadFileFromResponse(Response response) throws ApiException { @@ -1255,7 +1255,7 @@ public class ApiClient { * @param Type * @param call An instance of the Call object * @return ApiResponse<T> - * @throws {{invokerPackage}}.ApiException If fail to execute the call + * @throws cloud.stackit.sdk.core.exception.ApiException If fail to execute the call */ public ApiResponse execute(Call call) throws ApiException { return execute(call, null); @@ -1270,7 +1270,7 @@ public class ApiClient { * @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 + * @throws cloud.stackit.sdk.core.exception.ApiException If fail to execute the call */ public ApiResponse execute(Call call, Type returnType) throws ApiException { try { @@ -1289,7 +1289,7 @@ public class ApiClient { * @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. + * @throws cloud.stackit.sdk.core.exception.ApiException if any. */ public InputStream executeStream(Call call, Type returnType) throws ApiException { try { @@ -1359,7 +1359,7 @@ public class ApiClient { * @param response Response * @param returnType Return type * @return Type - * @throws {{invokerPackage}}.ApiException If the response has an unsuccessful status code or + * @throws cloud.stackit.sdk.core.exception.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 { @@ -1406,7 +1406,7 @@ public class ApiClient { * @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 + * @throws cloud.stackit.sdk.core.exception.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); @@ -1429,7 +1429,7 @@ public class ApiClient { * @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 + * @throws cloud.stackit.sdk.core.exception.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); @@ -1866,7 +1866,7 @@ public class ApiClient { * * @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 + * @throws cloud.stackit.sdk.core.exception.ApiException If fail to serialize the request body object into a string */ protected String requestBodyToString(RequestBody requestBody) throws ApiException { if (requestBody != null) { diff --git a/languages/java/templates/api.mustache b/languages/java/templates/api.mustache index 6f30a0b..96bbb8a 100644 --- a/languages/java/templates/api.mustache +++ b/languages/java/templates/api.mustache @@ -1,5 +1,5 @@ {{! This template was customized to initialize the DefaultApi with the CoreConfiguration to easily setup the KeyFlow Authentication and custom endpoints. }} -{{! Original template: https://github.com/OpenAPITools/openapi-generator/blob/v7.14.0/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/api.mustache }} +{{! Original template: https://github.com/OpenAPITools/openapi-generator/blob/v7.19.0/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/api.mustache }} {{>licenseInfo}} @@ -478,11 +478,11 @@ class {{classname}} { public class API{{operationId}}Request { {{#requiredParams}} - {{>nullable_var_annotations}} + {{>nullable_var_annotations}}{{! prevent indent}} private final {{{dataType}}} {{paramName}}; {{/requiredParams}} {{#optionalParams}} - {{>nullable_var_annotations}} + {{>nullable_var_annotations}}{{! prevent indent}} private {{{dataType}}} {{paramName}}; {{/optionalParams}} diff --git a/languages/java/templates/build.gradle.mustache b/languages/java/templates/build.gradle.mustache index b046a3c..0a4ca33 100644 --- a/languages/java/templates/build.gradle.mustache +++ b/languages/java/templates/build.gradle.mustache @@ -1,5 +1,5 @@ {{! This template was customized to allow the services to be a subprojects in one big gradle project. }} -{{! Original template: https://github.com/OpenAPITools/openapi-generator/blob/v7.14.0/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/build.gradle.mustache }} +{{! Original template: https://github.com/OpenAPITools/openapi-generator/blob/v7.19.0/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/build.gradle.mustache }} ext { {{#swagger1AnnotationLibrary}} @@ -36,7 +36,7 @@ dependencies { {{#hasOAuthMethods}} implementation group: 'org.apache.oltu.oauth2', name: 'org.apache.oltu.oauth2.client', version: '1.0.2' {{/hasOAuthMethods}} - implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.17.0' + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.18.0' {{#joda}} implementation 'joda-time:joda-time:2.9.9' {{/joda}}