diff --git a/openapi-generator-config-java.yml b/openapi-generator-config-java.yml
index 7daf919..fb4b055 100644
--- a/openapi-generator-config-java.yml
+++ b/openapi-generator-config-java.yml
@@ -18,3 +18,6 @@ files:
custom/serviceApi.mustache:
templateType: API
destinationFilename: ServiceApi.java
+ custom/serviceApiTest.mustache:
+ templateType: APITests
+ destinationFilename: ServiceApiTest.java
diff --git a/scripts/generate-sdk/.openapi-generator-ignore-java b/scripts/generate-sdk/.openapi-generator-ignore-java
index 274c522..6323e06 100644
--- a/scripts/generate-sdk/.openapi-generator-ignore-java
+++ b/scripts/generate-sdk/.openapi-generator-ignore-java
@@ -26,7 +26,7 @@ gradle/**
# ApiException from core library should be used (avoid multiple ApiException imports in case different services are used at the same time)
**/ApiException.java
-# Authentication classes are not required because the KeyFlowAuth is attached as interceptor
+# Authentication classes are not required because the KeyFlowAuth is attached as authenticator
**/auth/**
# Service Configuration is not required. It only stores a defaultApiClient
diff --git a/scripts/generate-sdk/languages/java.sh b/scripts/generate-sdk/languages/java.sh
index 875380a..19364b0 100644
--- a/scripts/generate-sdk/languages/java.sh
+++ b/scripts/generate-sdk/languages/java.sh
@@ -118,7 +118,7 @@ generate_java_sdk() {
--git-host "${GIT_HOST}" \
--git-user-id "${GIT_USER_ID}" \
--git-repo-id "${GIT_REPO_ID}" \
- --global-property apis,models,modelTests=false,modelDocs=false,apiDocs=false,apiTests=false,supportingFiles \
+ --global-property apis,models,modelTests=false,modelDocs=false,apiDocs=false,apiTests=true,supportingFiles \
--additional-properties="artifactId=${service},artifactDescription=${SERVICE_DESCRIPTION},invokerPackage=cloud.stackit.sdk.${service},modelPackage=cloud.stackit.sdk.${service}.model,apiPackage=cloud.stackit.sdk.${service}.api,serviceName=${service_pascal_case}" >/dev/null \
--http-user-agent stackit-sdk-java/"${service}" \
--config openapi-generator-config-java.yml
@@ -129,6 +129,10 @@ generate_java_sdk() {
if [ -f "$api_file" ]; then
mv "$api_file" "${SERVICES_FOLDER}/${service}/src/main/java/cloud/stackit/sdk/${service}/api/${service_pascal_case}Api.java"
fi
+ api_test_file="${SERVICES_FOLDER}/${service}/src/test/java/cloud/stackit/sdk/${service}/api/DefaultApiTestServiceApiTest.java"
+ if [ -f "$api_test_file" ]; then
+ mv "$api_test_file" "${SERVICES_FOLDER}/${service}/src/test/java/cloud/stackit/sdk/${service}/api/${service_pascal_case}ApiTest.java"
+ fi
# Remove unnecessary files
rm "${SERVICES_FOLDER}/${service}/.openapi-generator-ignore"
diff --git a/templates/java/ApiClient.mustache b/templates/java/ApiClient.mustache
index 247d062..9f8a693 100644
--- a/templates/java/ApiClient.mustache
+++ b/templates/java/ApiClient.mustache
@@ -78,6 +78,7 @@ import {{invokerPackage}}.auth.AWS4Auth;
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;
/**
*
ApiClient class.
@@ -130,7 +131,6 @@ public class ApiClient {
protected JSON json;
protected HttpLoggingInterceptor loggingInterceptor;
- protected SetupAuth authenticationInterceptor;
protected CoreConfiguration configuration;
@@ -138,42 +138,68 @@ public class ApiClient {
protected Map operationLookupMap = new HashMap<>();
{{/dynamicOperations}}
- /**
- * Basic constructor for ApiClient
+ {{! BEGIN - Removed ApiClient constructor and replaced it with a custom constructors which create the ApiClient with the CoreConfiguration }}
+ /**
+ * 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
*/
public ApiClient() throws IOException {
- {{! BEGIN - replace basic constructur }}
- this(new CoreConfiguration());
- {{! END - replace basic constructur }}
+ this(null, new CoreConfiguration());
}
- {{! BEGIN - Removed ApiClient constructor with OkHttpClient as param and replaced it with a custom constructor which creates the ApiClient with the CoreConfiguration }}
/**
- * Basic constructor with custom CoreConfiguration
- *
- * @param config a {@link cloud.stackit.sdk.core.config} object
- * @throws IOException thrown when a file can not be found
- */
+ * 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
+ * @throws IOException thrown when a file can not be found
+ */
public ApiClient(CoreConfiguration config) throws IOException {
+ this(null, config);
+ }
+
+ /**
+ * Constructor for ApiClient with OkHttpClient parameter. Recommended for production use.
+ *
+ * @param httpClient a OkHttpClient object
+ * @throws IOException thrown when a file can not be found
+ */
+ public ApiClient(OkHttpClient httpClient) throws IOException {
+ this(httpClient, new CoreConfiguration());
+ }
+
+ /**
+ * Constructor for ApiClient with OkHttpClient parameter. Recommended for production use.
+ *
+ * @param httpClient a OkHttpClient object
+ * @param config a {@link cloud.stackit.sdk.core.config.CoreConfiguration} object
+ * @throws IOException thrown when a file can not be found
+ */
+ public ApiClient(OkHttpClient httpClient, CoreConfiguration config) throws IOException {
init();
- if (config.getCustomEndpoint() != null && !config.getCustomEndpoint().trim().isEmpty()) {
- basePath = config.getCustomEndpoint();
- }
- if (config.getDefaultHeader() != null) {
- defaultHeaderMap = config.getDefaultHeader();
- }
+ if (config.getCustomEndpoint() != null && !config.getCustomEndpoint().trim().isEmpty()) {
+ basePath = config.getCustomEndpoint();
+ }
+ if (config.getDefaultHeader() != null) {
+ defaultHeaderMap = config.getDefaultHeader();
+ }
this.configuration = config;
- // Setup AuthHandler
- SetupAuth auth;
- auth = new SetupAuth(config);
- auth.init();
- authenticationInterceptor = auth;
-
- initHttpClient();
+ if (httpClient == null) {
+ initHttpClient();
+ KeyFlowAuthenticator authenticator = new KeyFlowAuthenticator(this.httpClient, config);
+ this.httpClient = this.httpClient.newBuilder().authenticator(authenticator).build();
+ } else {
+ // Authorization has to be configured manually in case a custom http client object is passed
+ this.httpClient = httpClient;
+ }
}
- {{! END - Removed ApiClient constructor with OkHttpClient as param and replaced it with a custom constructor which creates the ApiClient with the CoreConfiguration }}
+ {{! END - Removed ApiClient constructor and replaced it with a custom constructors which create the ApiClient with the CoreConfiguration }}
{{#hasOAuthMethods}}
{{#oauthMethods}}
@@ -261,8 +287,6 @@ public class ApiClient {
for (Interceptor interceptor: interceptors) {
builder.addInterceptor(interceptor);
}
- // Adds the Authorization header to requests
- builder.addInterceptor(authenticationInterceptor.getAuthHandler());
{{#useGzipFeature}}
// Enable gzip request compression
builder.addInterceptor(new GzipRequestInterceptor());
@@ -334,7 +358,15 @@ public class ApiClient {
return this;
}
- {{! remove getter and setter for httpClient because this would configure a client without the authentication interceptor }}
+ /**
+ * Get HTTP client
+ *
+ * @return An instance of OkHttpClient
+ */
+ public OkHttpClient getHttpClient() {
+ return httpClient;
+ }
+ {{! remove setter for httpClient because this would configure a client without the authentication interceptor }}
{{! if this will be requested in a feature-request, it should be implemented with the core configuration module like in the Go SDK }}
/**
diff --git a/templates/java/api.mustache b/templates/java/api.mustache
index eba41c8..6f30a0b 100644
--- a/templates/java/api.mustache
+++ b/templates/java/api.mustache
@@ -54,6 +54,7 @@ import java.io.InputStream;
{{/supportStreaming}}
import cloud.stackit.sdk.core.config.CoreConfiguration;
import cloud.stackit.sdk.core.exception.ApiException;
+import okhttp3.OkHttpClient;
{{#operations}}
// Package-private access to enforce service-specific API usage (DefaultApi => Api)
@@ -63,16 +64,52 @@ class {{classname}} {
private String localCustomBaseUrl;
{{! BEGIN - Remove default constructor and replaced with constructor which uses CoreConfiguration }}
- public {{classname}}() throws IOException {
- this(new CoreConfiguration());
- }
-
- public {{classname}}(CoreConfiguration config) throws IOException {
- if (config.getCustomEndpoint() != null && !config.getCustomEndpoint().trim().isEmpty()) {
+ /**
+ * Basic constructor for DefaultApi
+ *
+ * For production use consider using the constructor with the OkHttpClient parameter.
+ *
+ * @throws IOException
+ */
+ public DefaultApi() throws IOException {
+ this(null, new CoreConfiguration());
+ }
+
+ /**
+ * Basic Constructor for DefaultApi
+ *
+ * For production use consider using the constructor with the OkHttpClient parameter.
+ *
+ * @param config your STACKIT SDK CoreConfiguration
+ * @throws IOException
+ */
+ public DefaultApi(CoreConfiguration config) throws IOException {
+ this(null, config);
+ }
+
+ /**
+ * Constructor for DefaultApi
+ *
+ * @param httpClient OkHttpClient object
+ * @throws IOException
+ */
+ public DefaultApi(OkHttpClient httpClient) throws IOException {
+ this(httpClient, new CoreConfiguration());
+ }
+
+ /**
+ * Constructor for DefaultApi
+ *
+ * @param httpClient OkHttpClient object
+ * @param config your STACKIT SDK CoreConfiguration
+ * @throws IOException
+ */
+ public DefaultApi(OkHttpClient httpClient, CoreConfiguration config) throws IOException {
+ if (config.getCustomEndpoint() != null && !config.getCustomEndpoint().trim().isEmpty()) {
localCustomBaseUrl = config.getCustomEndpoint();
}
- this.localVarApiClient = new ApiClient(config);
- }
+ this.localVarApiClient = new ApiClient(httpClient, config);
+ }
{{! END - Remove default constructor and replaced with constructor which uses CoreConfiguration }}
public ApiClient getApiClient() {
diff --git a/templates/java/api_test.mustache b/templates/java/api_test.mustache
new file mode 100644
index 0000000..466db0c
--- /dev/null
+++ b/templates/java/api_test.mustache
@@ -0,0 +1,59 @@
+{{! This template had to be customized because of our changes to the DefaultApi and ApiClient classes }}
+{{! Original template: https://github.com/OpenAPITools/openapi-generator/blob/v7.15.0/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/api_test.mustache }}
+
+{{>licenseInfo}}
+
+package {{invokerPackage}};
+
+import cloud.stackit.sdk.core.KeyFlowAuthenticator;
+import cloud.stackit.sdk.core.auth.SetupAuth;
+import cloud.stackit.sdk.core.config.CoreConfiguration;
+import cloud.stackit.sdk.core.utils.TestUtils;
+import java.io.IOException;
+import okhttp3.Authenticator;
+import okhttp3.OkHttpClient;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class DefaultApiTest {
+ @Test
+ public void TestCustomHttpClient() throws IOException {
+ // before
+ CoreConfiguration conf =
+ new CoreConfiguration().serviceAccountKey(TestUtils.MOCK_SERVICE_ACCOUNT_KEY);
+
+ // when
+ OkHttpClient httpClient = new OkHttpClient();
+ ApiClient apiClient = new ApiClient(httpClient, conf);
+
+ // then
+ Assertions.assertEquals(httpClient, apiClient.getHttpClient());
+ // make sure the http client object is exactly the same object
+ Assertions.assertSame(httpClient, apiClient.getHttpClient());
+ }
+
+ @Test
+ public void TestNoCustomHttpClient() throws IOException {
+ // before
+ CoreConfiguration conf =
+ new CoreConfiguration().serviceAccountKey(TestUtils.MOCK_SERVICE_ACCOUNT_KEY);
+
+ // when
+ ApiClient apiClient = new ApiClient(conf);
+
+ // then
+ /*
+ * verify a fresh OkHttpClient got created which will have the auth header set
+ * by the {@link cloud.stackit.sdk.core.KeyFlowAuthenticator}
+ */
+ OkHttpClient httpClient = new OkHttpClient();
+ Authenticator authenticator =
+ new KeyFlowAuthenticator(httpClient, conf, SetupAuth.setupKeyFlow(conf));
+ httpClient = httpClient.newBuilder().authenticator(authenticator).build();
+
+ Assertions.assertNotNull(apiClient.getHttpClient());
+ Assertions.assertEquals(
+ httpClient.authenticator().getClass(),
+ apiClient.getHttpClient().authenticator().getClass());
+ }
+}
diff --git a/templates/java/custom/ApiClientTest.mustache b/templates/java/custom/ApiClientTest.mustache
new file mode 100644
index 0000000..f090566
--- /dev/null
+++ b/templates/java/custom/ApiClientTest.mustache
@@ -0,0 +1,57 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}};
+
+import cloud.stackit.sdk.core.KeyFlowAuthenticator;
+import cloud.stackit.sdk.core.auth.SetupAuth;
+import cloud.stackit.sdk.core.config.CoreConfiguration;
+import cloud.stackit.sdk.core.utils.TestUtils;
+import java.io.IOException;
+import okhttp3.Authenticator;
+import okhttp3.OkHttpClient;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/** Tests for ApiClient */
+public class ApiClientTest {
+ @Test
+ public void TestCustomHttpClient() throws IOException {
+ // before
+ CoreConfiguration conf =
+ new CoreConfiguration().serviceAccountKey(TestUtils.MOCK_SERVICE_ACCOUNT_KEY);
+
+ // when
+ OkHttpClient httpClient = new OkHttpClient();
+ ApiClient apiClient = new ApiClient(httpClient, conf);
+
+ // then
+ Assertions.assertEquals(httpClient, apiClient.getHttpClient());
+ // make sure the http client object is exactly the same object
+ Assertions.assertSame(httpClient, apiClient.getHttpClient());
+ }
+
+ @Test
+ public void TestNoCustomHttpClient() throws IOException {
+ // before
+ CoreConfiguration conf =
+ new CoreConfiguration().serviceAccountKey(TestUtils.MOCK_SERVICE_ACCOUNT_KEY);
+
+ // when
+ ApiClient apiClient = new ApiClient(conf);
+
+ // then
+ /*
+ * verify a fresh OkHttpClient got created which will have the auth header set
+ * by the {@link cloud.stackit.sdk.core.KeyFlowAuthenticator}
+ */
+ OkHttpClient httpClient = new OkHttpClient();
+ Authenticator authenticator =
+ new KeyFlowAuthenticator(httpClient, conf, SetupAuth.setupKeyFlow(conf));
+ httpClient = httpClient.newBuilder().authenticator(authenticator).build();
+
+ Assertions.assertNotNull(apiClient.getHttpClient());
+ Assertions.assertEquals(
+ httpClient.authenticator().getClass(),
+ apiClient.getHttpClient().authenticator().getClass());
+ }
+}
diff --git a/templates/java/custom/serviceApi.mustache b/templates/java/custom/serviceApi.mustache
index 7f90aa9..9fcae87 100644
--- a/templates/java/custom/serviceApi.mustache
+++ b/templates/java/custom/serviceApi.mustache
@@ -1,15 +1,54 @@
+{{>licenseInfo}}
+
package {{invokerPackage}}.api;
import cloud.stackit.sdk.core.config.CoreConfiguration;
import java.io.IOException;
+import okhttp3.OkHttpClient;
public class {{serviceName}}Api extends DefaultApi {
+ /**
+ * Basic constructor for {{serviceName}}Api
+ *
+ * For production use consider using the constructor with the OkHttpClient parameter.
+ *
+ * @throws IOException
+ */
public {{serviceName}}Api() throws IOException {
super();
}
+ /**
+ * Basic Constructor for {{serviceName}}Api
+ *
+ * For production use consider using the constructor with the OkHttpClient parameter.
+ *
+ * @param config your STACKIT SDK CoreConfiguration
+ * @throws IOException
+ */
public {{serviceName}}Api(CoreConfiguration configuration)
throws IOException {
super(configuration);
}
-}
\ No newline at end of file
+
+ /**
+ * Constructor for {{serviceName}}Api
+ *
+ * @param httpClient OkHttpClient object
+ * @throws IOException
+ */
+ public {{serviceName}}Api(OkHttpClient httpClient) throws IOException {
+ super(httpClient);
+ }
+
+ /**
+ * Constructor for {{serviceName}}Api
+ *
+ * @param httpClient OkHttpClient object
+ * @param configuration your STACKIT SDK CoreConfiguration
+ * @throws IOException
+ */
+ public {{serviceName}}Api(OkHttpClient httpClient, CoreConfiguration configuration) throws IOException {
+ super(httpClient, configuration);
+ }
+}
diff --git a/templates/java/custom/serviceApiTest.mustache b/templates/java/custom/serviceApiTest.mustache
new file mode 100644
index 0000000..3d5e79f
--- /dev/null
+++ b/templates/java/custom/serviceApiTest.mustache
@@ -0,0 +1,57 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}}.api;
+
+import cloud.stackit.sdk.core.KeyFlowAuthenticator;
+import cloud.stackit.sdk.core.auth.SetupAuth;
+import cloud.stackit.sdk.core.config.CoreConfiguration;
+import cloud.stackit.sdk.core.utils.TestUtils;
+import java.io.IOException;
+import okhttp3.Authenticator;
+import okhttp3.OkHttpClient;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/** API tests for {{serviceName}}Api */
+public class {{serviceName}}ApiTest {
+ @Test
+ public void TestCustomHttpClient() throws IOException {
+ // before
+ CoreConfiguration conf =
+ new CoreConfiguration().serviceAccountKey(TestUtils.MOCK_SERVICE_ACCOUNT_KEY);
+
+ // when
+ OkHttpClient httpClient = new OkHttpClient();
+ {{serviceName}}Api api = new {{serviceName}}Api(httpClient);
+
+ // then
+ Assertions.assertEquals(httpClient, api.getApiClient().getHttpClient());
+ // make sure the http client object is exactly the same object
+ Assertions.assertSame(httpClient, api.getApiClient().getHttpClient());
+ }
+
+ @Test
+ public void TestNoCustomHttpClient() throws IOException {
+ // before
+ CoreConfiguration conf =
+ new CoreConfiguration().serviceAccountKey(TestUtils.MOCK_SERVICE_ACCOUNT_KEY);
+
+ // when
+ {{serviceName}}Api api = new {{serviceName}}Api(conf);
+
+ // then
+ /*
+ * verify a fresh OkHttpClient got created which will have the auth header set
+ * by the {@link cloud.stackit.sdk.core.KeyFlowAuthenticator}
+ */
+ OkHttpClient httpClient = new OkHttpClient();
+ Authenticator authenticator =
+ new KeyFlowAuthenticator(httpClient, conf, SetupAuth.setupKeyFlow(conf));
+ httpClient = httpClient.newBuilder().authenticator(authenticator).build();
+
+ Assertions.assertNotNull(api.getApiClient().getHttpClient());
+ Assertions.assertEquals(
+ httpClient.authenticator().getClass(),
+ api.getApiClient().getHttpClient().authenticator().getClass());
+ }
+}