From 6f2cc8b7fadce373375cb23bda74c8326d6e97ed Mon Sep 17 00:00:00 2001 From: Ruben Hoenle Date: Tue, 16 Sep 2025 18:41:04 +0200 Subject: [PATCH 1/5] feat(sdk): allow passing OkHttpClient objects relates to STACKITSDK-238 --- .../sdk/core/KeyFlowAuthenticator.java | 154 ++++++++++++------ .../stackit/sdk/core/KeyFlowInterceptor.java | 12 ++ .../stackit/sdk/core/auth/SetupAuth.java | 99 ++++++----- .../stackit/sdk/core/utils/TestUtils.java | 6 + .../sdk/core/KeyFlowAuthenticatorTest.java | 25 ++- .../sdk/core/KeyFlowInterceptorTest.java | 4 + .../stackit/sdk/core/auth/SetupAuthTest.java | 89 ++++------ examples/authentication/build.gradle | 1 + examples/custom-http-client/build.gradle | 6 + .../examples/CustomHttpClientExample.java | 55 +++++++ examples/iaas/build.gradle | 1 + examples/resourcemanager/build.gradle | 1 + services/iaas/settings.gradle | 1 - .../cloud/stackit/sdk/iaas/ApiClient.java | 67 ++++++-- .../stackit/sdk/iaas/api/DefaultApi.java | 41 ++++- .../cloud/stackit/sdk/iaas/api/IaasApi.java | 49 ++++++ .../stackit/sdk/iaas/api/DefaultApiTest.java | 66 ++++++++ .../stackit/sdk/iaas/api/IaasApiTest.java | 67 ++++++++ services/resourcemanager/settings.gradle | 1 - .../sdk/resourcemanager/ApiClient.java | 67 ++++++-- .../sdk/resourcemanager/api/DefaultApi.java | 41 ++++- .../api/ResourceManagerApi.java | 50 ++++++ .../resourcemanager/api/DefaultApiTest.java | 66 ++++++++ .../api/ResourceManagerApiTest.java | 67 ++++++++ 24 files changed, 840 insertions(+), 196 deletions(-) create mode 100644 core/src/main/java/cloud/stackit/sdk/core/utils/TestUtils.java create mode 100644 examples/custom-http-client/build.gradle create mode 100644 examples/custom-http-client/src/main/java/cloud/stackit/sdk/customhttpclient/examples/CustomHttpClientExample.java delete mode 100644 services/iaas/settings.gradle create mode 100644 services/iaas/src/test/java/cloud/stackit/sdk/iaas/api/DefaultApiTest.java create mode 100644 services/iaas/src/test/java/cloud/stackit/sdk/iaas/api/IaasApiTest.java delete mode 100644 services/resourcemanager/settings.gradle create mode 100644 services/resourcemanager/src/test/java/cloud/stackit/sdk/resourcemanager/api/DefaultApiTest.java create mode 100644 services/resourcemanager/src/test/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApiTest.java diff --git a/core/src/main/java/cloud/stackit/sdk/core/KeyFlowAuthenticator.java b/core/src/main/java/cloud/stackit/sdk/core/KeyFlowAuthenticator.java index b0e664a..d321ed3 100644 --- a/core/src/main/java/cloud/stackit/sdk/core/KeyFlowAuthenticator.java +++ b/core/src/main/java/cloud/stackit/sdk/core/KeyFlowAuthenticator.java @@ -1,5 +1,6 @@ package cloud.stackit.sdk.core; +import cloud.stackit.sdk.core.auth.SetupAuth; import cloud.stackit.sdk.core.config.CoreConfiguration; import cloud.stackit.sdk.core.config.EnvironmentVariables; import cloud.stackit.sdk.core.exception.ApiException; @@ -23,16 +24,18 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; import okhttp3.*; +import org.jetbrains.annotations.NotNull; -/** KeyFlowAuthenticator handles the Key Flow Authentication based on the Service Account Key. */ -public class KeyFlowAuthenticator { - private final String REFRESH_TOKEN = "refresh_token"; - private final String ASSERTION = "assertion"; - private final String DEFAULT_TOKEN_ENDPOINT = "https://service-account.api.stackit.cloud/token"; - private final long DEFAULT_TOKEN_LEEWAY = 60; - private final int CONNECT_TIMEOUT = 10; - private final int WRITE_TIMEOUT = 10; - private final int READ_TIMEOUT = 10; +/* KeyFlowAuthenticator handles the Key Flow Authentication based on the Service Account Key. */ +public class KeyFlowAuthenticator implements Authenticator { + private static final String REFRESH_TOKEN = "refresh_token"; + private static final String ASSERTION = "assertion"; + private static final String DEFAULT_TOKEN_ENDPOINT = + "https://service-account.api.stackit.cloud/token"; + private static final long DEFAULT_TOKEN_LEEWAY = 60; + private static final int CONNECT_TIMEOUT = 10; + private static final int WRITE_TIMEOUT = 10; + private static final int READ_TIMEOUT = 10; private final OkHttpClient httpClient; private final ServiceAccountKey saKey; @@ -41,6 +44,100 @@ public class KeyFlowAuthenticator { private final String tokenUrl; private long tokenLeewayInSeconds = DEFAULT_TOKEN_LEEWAY; + /** + * Creates the initial service account and refreshes expired access token. + * + * @deprecated use constructor with OkHttpClient instead to prevent resource leaks. Will be + * removed in April 2026. + * @param cfg Configuration to set a custom token endpoint and the token expiration leeway. + * @param saKey Service Account Key, which should be used for the authentication + */ + @Deprecated + public KeyFlowAuthenticator(CoreConfiguration cfg, ServiceAccountKey saKey) { + this(new OkHttpClient(), cfg, saKey, new EnvironmentVariables()); + } + + /** + * Creates the initial service account and refreshes expired access token. + * + * @deprecated use constructor with OkHttpClient instead to prevent resource leaks. Will be + * removed in April 2026. + * @param cfg Configuration to set a custom token endpoint and the token expiration leeway. + * @param saKey Service Account Key, which should be used for the authentication + */ + @Deprecated + public KeyFlowAuthenticator( + CoreConfiguration cfg, + ServiceAccountKey saKey, + EnvironmentVariables environmentVariables) { + this(new OkHttpClient(), cfg, saKey, environmentVariables); + } + + /** + * Creates the initial service account and refreshes expired access token. + * + * @param httpClient OkHttpClient object + * @param cfg Configuration to set a custom token endpoint and the token expiration leeway. + */ + public KeyFlowAuthenticator(OkHttpClient httpClient, CoreConfiguration cfg) throws IOException { + this(httpClient, cfg, SetupAuth.setupKeyFlow(cfg), new EnvironmentVariables()); + } + + /** + * Creates the initial service account and refreshes expired access token. + * + * @param httpClient OkHttpClient object + * @param cfg Configuration to set a custom token endpoint and the token expiration leeway. + * @param saKey Service Account Key, which should be used for the authentication + */ + public KeyFlowAuthenticator( + OkHttpClient httpClient, CoreConfiguration cfg, ServiceAccountKey saKey) { + this(httpClient, cfg, saKey, new EnvironmentVariables()); + } + + protected KeyFlowAuthenticator( + OkHttpClient httpClient, + CoreConfiguration cfg, + ServiceAccountKey saKey, + EnvironmentVariables environmentVariables) { + this.saKey = saKey; + this.gson = new Gson(); + this.httpClient = + httpClient + .newBuilder() + .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS) + .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS) + .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS) + .build(); + + if (Utils.isStringSet(cfg.getTokenCustomUrl())) { + this.tokenUrl = cfg.getTokenCustomUrl(); + } else if (Utils.isStringSet(environmentVariables.getStackitTokenBaseurl())) { + this.tokenUrl = environmentVariables.getStackitTokenBaseurl(); + } else { + this.tokenUrl = DEFAULT_TOKEN_ENDPOINT; + } + if (cfg.getTokenExpirationLeeway() != null && cfg.getTokenExpirationLeeway() > 0) { + this.tokenLeewayInSeconds = cfg.getTokenExpirationLeeway(); + } + } + + @Override + public Request authenticate(Route route, @NotNull Response response) throws IOException { + String accessToken; + try { + accessToken = getAccessToken(); + } catch (ApiException | InvalidKeySpecException e) { + throw new RuntimeException(e); + } + + // Return a new request with the refreshed token + return response.request() + .newBuilder() + .header("Authorization", "Bearer " + accessToken) + .build(); + } + protected static class KeyFlowTokenResponse { @SerializedName("access_token") private String accessToken; @@ -79,45 +176,6 @@ protected String getAccessToken() { } } - public KeyFlowAuthenticator(CoreConfiguration cfg, ServiceAccountKey saKey) { - this(cfg, saKey, null); - } - - /** - * Creates the initial service account and refreshes expired access token. - * - * @param cfg Configuration to set a custom token endpoint and the token expiration leeway. - * @param saKey Service Account Key, which should be used for the authentication - */ - public KeyFlowAuthenticator( - CoreConfiguration cfg, - ServiceAccountKey saKey, - EnvironmentVariables environmentVariables) { - this.saKey = saKey; - this.gson = new Gson(); - this.httpClient = - new OkHttpClient.Builder() - .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS) - .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS) - .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS) - .build(); - - if (environmentVariables == null) { - environmentVariables = new EnvironmentVariables(); - } - - if (Utils.isStringSet(cfg.getTokenCustomUrl())) { - this.tokenUrl = cfg.getTokenCustomUrl(); - } else if (Utils.isStringSet(environmentVariables.getStackitTokenBaseurl())) { - this.tokenUrl = environmentVariables.getStackitTokenBaseurl(); - } else { - this.tokenUrl = DEFAULT_TOKEN_ENDPOINT; - } - if (cfg.getTokenExpirationLeeway() != null && cfg.getTokenExpirationLeeway() > 0) { - this.tokenLeewayInSeconds = cfg.getTokenExpirationLeeway(); - } - } - /** * Returns access token. If the token is expired it creates a new token. * diff --git a/core/src/main/java/cloud/stackit/sdk/core/KeyFlowInterceptor.java b/core/src/main/java/cloud/stackit/sdk/core/KeyFlowInterceptor.java index b9fdafd..d310489 100644 --- a/core/src/main/java/cloud/stackit/sdk/core/KeyFlowInterceptor.java +++ b/core/src/main/java/cloud/stackit/sdk/core/KeyFlowInterceptor.java @@ -8,14 +8,26 @@ import okhttp3.Response; import org.jetbrains.annotations.NotNull; +@Deprecated +/* + * @deprecated use KeyFlowAuthenticator instead. Will be removed in April 2026. + * */ public class KeyFlowInterceptor implements Interceptor { private final KeyFlowAuthenticator authenticator; + @Deprecated + /* + * @deprecated use KeyFlowAuthenticator instead. Will be removed in April 2026. + * */ public KeyFlowInterceptor(KeyFlowAuthenticator authenticator) { this.authenticator = authenticator; } @NotNull @Override + @Deprecated + /* + * @deprecated use KeyFlowAuthenticator instead. Will be removed in April 2026. + * */ public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); diff --git a/core/src/main/java/cloud/stackit/sdk/core/auth/SetupAuth.java b/core/src/main/java/cloud/stackit/sdk/core/auth/SetupAuth.java index bee989c..b79c08d 100644 --- a/core/src/main/java/cloud/stackit/sdk/core/auth/SetupAuth.java +++ b/core/src/main/java/cloud/stackit/sdk/core/auth/SetupAuth.java @@ -21,60 +21,58 @@ import okhttp3.Interceptor; public class SetupAuth { - private final EnvironmentVariables env; - private Interceptor authHandler; - private final CoreConfiguration cfg; - private final String defaultCredentialsFilePath = - FileSystemView.getFileSystemView().getHomeDirectory() - + File.separator - + ".stackit" - + File.separator - + "credentials.json"; + /* + * @deprecated Will be removed in April 2026. + */ + @Deprecated private CoreConfiguration cfg; + + /* + * @deprecated Will be removed in April 2026. + */ + @Deprecated private Interceptor authHandler; /** * Set up the KeyFlow Authentication and can be integrated in an OkHttp client, by adding * `SetupAuth().getAuthHandler()` as interceptor. This relies on the configuration methods via * ENVs or the credentials file in `$HOME/.stackit/credentials.json` * - * @throws CredentialsInFileNotFoundException when no configuration is set or can be found + * @deprecated Use static methods of SetupAuth instead or just use the KeyFlowAuthenticator and + * let it handle the rest. Will be removed in April 2026. */ - public SetupAuth() throws CredentialsInFileNotFoundException { - this(new CoreConfiguration(), new EnvironmentVariables()); - } + @Deprecated + // TODO: constructor of SetupAuth should be private after deprecated constructors/methods are + // removed (only static methods should remain) + public SetupAuth() {} /** * Set up the KeyFlow Authentication and can be integrated in an OkHttp client, by adding * `SetupAuth().getAuthHandler()` as interceptor. * + * @deprecated Use static methods of SetupAuth instead or just use the KeyFlowAuthenticator and + * let it handle the rest. Will be removed in April 2026. * @param cfg Configuration which describes, which service account and token endpoint should be * used - * @throws IOException when a file can be found - * @throws CredentialsInFileNotFoundException when no credentials are set or can be found */ - public SetupAuth(CoreConfiguration cfg) throws IOException, CredentialsInFileNotFoundException { - this(cfg, new EnvironmentVariables()); + @Deprecated + // TODO: constructor of SetupAuth should be private after deprecated constructors/methods are + // removed (only static methods should remain) + public SetupAuth(CoreConfiguration cfg) { + this.cfg = cfg; } - /** - * Set up the KeyFlow Authentication and can be integrated in an OkHttp client, by adding - * `SetupAuth().getAuthHandler()` as interceptor. - * - * @param cfg Configuration which describes, which service account and token endpoint should be - * used - * @throws CredentialsInFileNotFoundException when no credentials are set or can be found + /* + * @deprecated Use static methods of SetupAuth instead or just use the KeyFlowAuthenticator and let it handle the rest. Will be removed in April 2026. */ - protected SetupAuth(CoreConfiguration cfg, EnvironmentVariables environmentVariables) - throws CredentialsInFileNotFoundException { - - this.cfg = cfg != null ? cfg : new CoreConfiguration(); - this.env = environmentVariables != null ? environmentVariables : new EnvironmentVariables(); - } - + @Deprecated public void init() throws IOException { ServiceAccountKey saKey = setupKeyFlow(cfg); authHandler = new KeyFlowInterceptor(new KeyFlowAuthenticator(cfg, saKey)); } + /* + * @deprecated Use static methods of SetupAuth instead or just use the KeyFlowAuthenticator and let it handle the rest. Will be removed in April 2026. + */ + @Deprecated public Interceptor getAuthHandler() { if (authHandler == null) { throw new RuntimeException("init() has to be called first"); @@ -82,6 +80,14 @@ public Interceptor getAuthHandler() { return authHandler; } + private static String getDefaultCredentialsFilePath() { + return FileSystemView.getFileSystemView().getHomeDirectory() + + File.separator + + ".stackit" + + File.separator + + "credentials.json"; + } + /** * setupKeyFlow return first found ServiceAccountKey Reads the configured options in the * following order @@ -114,12 +120,17 @@ public Interceptor getAuthHandler() { * can be found * @throws IOException thrown when a file can not be found */ - protected ServiceAccountKey setupKeyFlow(CoreConfiguration cfg) + public static ServiceAccountKey setupKeyFlow(CoreConfiguration cfg) + throws CredentialsInFileNotFoundException, IOException { + return setupKeyFlow(cfg, new EnvironmentVariables()); + } + + protected static ServiceAccountKey setupKeyFlow(CoreConfiguration cfg, EnvironmentVariables env) throws CredentialsInFileNotFoundException, IOException { // Explicit config in code if (Utils.isStringSet(cfg.getServiceAccountKey())) { ServiceAccountKey saKey = ServiceAccountKey.loadFromJson(cfg.getServiceAccountKey()); - loadPrivateKey(cfg, saKey); + loadPrivateKey(cfg, env, saKey); return saKey; } @@ -129,7 +140,7 @@ protected ServiceAccountKey setupKeyFlow(CoreConfiguration cfg) Files.readAllBytes(Paths.get(cfg.getServiceAccountKeyPath())), StandardCharsets.UTF_8); ServiceAccountKey saKey = ServiceAccountKey.loadFromJson(fileContent); - loadPrivateKey(cfg, saKey); + loadPrivateKey(cfg, env, saKey); return saKey; } @@ -137,7 +148,7 @@ protected ServiceAccountKey setupKeyFlow(CoreConfiguration cfg) if (Utils.isStringSet(env.getStackitServiceAccountKey())) { ServiceAccountKey saKey = ServiceAccountKey.loadFromJson(env.getStackitServiceAccountKey().trim()); - loadPrivateKey(cfg, saKey); + loadPrivateKey(cfg, env, saKey); return saKey; } @@ -147,7 +158,7 @@ protected ServiceAccountKey setupKeyFlow(CoreConfiguration cfg) Files.readAllBytes(Paths.get(env.getStackitServiceAccountKeyPath())), StandardCharsets.UTF_8); ServiceAccountKey saKey = ServiceAccountKey.loadFromJson(fileContent); - loadPrivateKey(cfg, saKey); + loadPrivateKey(cfg, env, saKey); return saKey; } @@ -155,7 +166,7 @@ protected ServiceAccountKey setupKeyFlow(CoreConfiguration cfg) String credentialsFilePath = Utils.isStringSet(env.getStackitCredentialsPath()) ? env.getStackitCredentialsPath() - : defaultCredentialsFilePath; + : getDefaultCredentialsFilePath(); String saKeyJson = readValueFromCredentialsFile( @@ -164,15 +175,16 @@ protected ServiceAccountKey setupKeyFlow(CoreConfiguration cfg) EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH); ServiceAccountKey saKey = ServiceAccountKey.loadFromJson(saKeyJson); - loadPrivateKey(cfg, saKey); + loadPrivateKey(cfg, env, saKey); return saKey; } - protected void loadPrivateKey(CoreConfiguration cfg, ServiceAccountKey saKey) + protected static void loadPrivateKey( + CoreConfiguration cfg, EnvironmentVariables env, ServiceAccountKey saKey) throws PrivateKeyNotFoundException { if (!saKey.getCredentials().isPrivateKeySet()) { try { - String privateKey = getPrivateKey(cfg); + String privateKey = getPrivateKey(cfg, env); saKey.getCredentials().setPrivateKey(privateKey); } catch (Exception e) { throw new PrivateKeyNotFoundException("could not find private key", e); @@ -209,7 +221,7 @@ protected void loadPrivateKey(CoreConfiguration cfg, ServiceAccountKey saKey) * @throws IOException throws if the provided path can not be found or the file within the * pathKey can not be found */ - private String getPrivateKey(CoreConfiguration cfg) + private static String getPrivateKey(CoreConfiguration cfg, EnvironmentVariables env) throws CredentialsInFileNotFoundException, IOException { // Explicit code config // Get private key @@ -246,7 +258,7 @@ private String getPrivateKey(CoreConfiguration cfg) String credentialsFilePath = Utils.isStringSet(env.getStackitCredentialsPath()) ? env.getStackitCredentialsPath() - : defaultCredentialsFilePath; + : getDefaultCredentialsFilePath(); return readValueFromCredentialsFile( credentialsFilePath, @@ -266,7 +278,8 @@ private String getPrivateKey(CoreConfiguration cfg) * @throws IOException throws if the provided path can not be found or the file within the * pathKey can not be found */ - protected String readValueFromCredentialsFile(String path, String valueKey, String pathKey) + protected static String readValueFromCredentialsFile( + String path, String valueKey, String pathKey) throws IOException, CredentialsInFileNotFoundException { // Read credentials file String fileContent = diff --git a/core/src/main/java/cloud/stackit/sdk/core/utils/TestUtils.java b/core/src/main/java/cloud/stackit/sdk/core/utils/TestUtils.java new file mode 100644 index 0000000..a027a5b --- /dev/null +++ b/core/src/main/java/cloud/stackit/sdk/core/utils/TestUtils.java @@ -0,0 +1,6 @@ +package cloud.stackit.sdk.core.utils; + +public class TestUtils { + public static final String MOCK_SERVICE_ACCOUNT_KEY = + "{\"id\":\"id\",\"publicKey\":\"publicKey\",\"createdAt\":\"2025-03-26T15:08:45.915+00:00\",\"keyType\":\"keyType\",\"keyOrigin\":\"keyOrigin\",\"keyAlgorithm\":\"keyAlgo\",\"active\":true,\"validUntil\":\"2025-03-26T15:08:45.915+00:00\",\"credentials\":{\"aud\":\"aud\",\"iss\":\"iss\",\"kid\":\"kid\",\"privateKey\":\"privateKey\",\"sub\":\"sub\"}}\n"; +} diff --git a/core/src/test/java/cloud/stackit/sdk/core/KeyFlowAuthenticatorTest.java b/core/src/test/java/cloud/stackit/sdk/core/KeyFlowAuthenticatorTest.java index 2ca7c24..dac8362 100644 --- a/core/src/test/java/cloud/stackit/sdk/core/KeyFlowAuthenticatorTest.java +++ b/core/src/test/java/cloud/stackit/sdk/core/KeyFlowAuthenticatorTest.java @@ -17,6 +17,7 @@ import java.time.temporal.ChronoUnit; import java.util.Date; import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.junit.jupiter.api.AfterEach; @@ -26,7 +27,8 @@ class KeyFlowAuthenticatorTest { private static MockWebServer mockWebServer; private ServiceAccountKey defaultSaKey; - private final String privateKey = + private OkHttpClient httpClient; + private static final String PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n" + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0jVPq7ACbkwW6\n" + "ojf6akoAlqkSLpAaTESOKEw6Hi2chr6gV4I1jtVLJM5K1e+vR+bKFBAzBVk9NCKS\n" @@ -58,7 +60,7 @@ class KeyFlowAuthenticatorTest { ServiceAccountKey createDummyServiceAccount() { ServiceAccountCredentials credentials = - new ServiceAccountCredentials("aud", "iss", "kid", privateKey, "sub"); + new ServiceAccountCredentials("aud", "iss", "kid", PRIVATE_KEY, "sub"); return new ServiceAccountKey( "id", "publicKey", @@ -95,6 +97,7 @@ void setUp() throws IOException { mockWebServer = new MockWebServer(); mockWebServer.start(); defaultSaKey = createDummyServiceAccount(); + httpClient = new OkHttpClient(); } @AfterEach @@ -118,7 +121,8 @@ void getAccessToken_response200_noException() CoreConfiguration cfg = new CoreConfiguration().tokenCustomUrl(url.toString()); // Use mockWebServer - KeyFlowAuthenticator keyFlowAuthenticator = new KeyFlowAuthenticator(cfg, defaultSaKey); + KeyFlowAuthenticator keyFlowAuthenticator = + new KeyFlowAuthenticator(httpClient, cfg, defaultSaKey); assertDoesNotThrow(keyFlowAuthenticator::getAccessToken); assertEquals(responseBody.getAccessToken(), keyFlowAuthenticator.getAccessToken()); @@ -143,7 +147,8 @@ void getAccessToken_expiredToken_noException() CoreConfiguration cfg = new CoreConfiguration().tokenCustomUrl(url.toString()); // Use mockWebServer - KeyFlowAuthenticator keyFlowAuthenticator = new KeyFlowAuthenticator(cfg, defaultSaKey); + KeyFlowAuthenticator keyFlowAuthenticator = + new KeyFlowAuthenticator(httpClient, cfg, defaultSaKey); keyFlowAuthenticator.setToken(expiredKey); assertEquals(newToken.getAccessToken(), keyFlowAuthenticator.getAccessToken()); @@ -162,7 +167,7 @@ void createAccessToken_response200WithEmptyBody_throwsException() { // Init keyFlowAuthenticator KeyFlowAuthenticator keyFlowAuthenticator = - new KeyFlowAuthenticator(cfg, createDummyServiceAccount()); + new KeyFlowAuthenticator(httpClient, cfg, createDummyServiceAccount()); assertThrows(JsonSyntaxException.class, keyFlowAuthenticator::createAccessToken); } @@ -180,7 +185,7 @@ void createAccessToken_response400_throwsApiException() { // Init keyFlowAuthenticator KeyFlowAuthenticator keyFlowAuthenticator = - new KeyFlowAuthenticator(cfg, createDummyServiceAccount()); + new KeyFlowAuthenticator(httpClient, cfg, createDummyServiceAccount()); assertThrows(ApiException.class, keyFlowAuthenticator::createAccessToken); } @@ -201,7 +206,8 @@ void createAccessToken_response200WithValidResponse_noException() new CoreConfiguration().tokenCustomUrl(url.toString()); // Use mockWebServer // Init keyFlowAuthenticator - KeyFlowAuthenticator keyFlowAuthenticator = new KeyFlowAuthenticator(cfg, defaultSaKey); + KeyFlowAuthenticator keyFlowAuthenticator = + new KeyFlowAuthenticator(httpClient, cfg, defaultSaKey); assertDoesNotThrow(keyFlowAuthenticator::createAccessToken); } @@ -222,7 +228,8 @@ void createAccessTokenWithRefreshToken_response200WithValidResponse_noException( new CoreConfiguration().tokenCustomUrl(url.toString()); // Use mockWebServer // Prepare keyFlowAuthenticator - KeyFlowAuthenticator keyFlowAuthenticator = new KeyFlowAuthenticator(cfg, defaultSaKey); + KeyFlowAuthenticator keyFlowAuthenticator = + new KeyFlowAuthenticator(httpClient, cfg, defaultSaKey); keyFlowAuthenticator.setToken(mockedBody); assertDoesNotThrow(keyFlowAuthenticator::createAccessTokenWithRefreshToken); @@ -243,7 +250,7 @@ void createAccessTokenWithRefreshToken_response200WithEmptyBody_throwsException( // Prepare keyFlowAuthenticator KeyFlowAuthenticator keyFlowAuthenticator = - new KeyFlowAuthenticator(cfg, createDummyServiceAccount()); + new KeyFlowAuthenticator(httpClient, cfg, createDummyServiceAccount()); keyFlowAuthenticator.setToken(mockResponse); // Refresh token diff --git a/core/src/test/java/cloud/stackit/sdk/core/KeyFlowInterceptorTest.java b/core/src/test/java/cloud/stackit/sdk/core/KeyFlowInterceptorTest.java index 4927ad7..b6f4b39 100644 --- a/core/src/test/java/cloud/stackit/sdk/core/KeyFlowInterceptorTest.java +++ b/core/src/test/java/cloud/stackit/sdk/core/KeyFlowInterceptorTest.java @@ -18,7 +18,11 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +/* + * @deprecated use KeyFlowAuthenticator instead. Will be removed in April 2026. + * */ @ExtendWith(MockitoExtension.class) +@Deprecated class KeyFlowInterceptorTest { @Mock private KeyFlowAuthenticator authenticator; diff --git a/core/src/test/java/cloud/stackit/sdk/core/auth/SetupAuthTest.java b/core/src/test/java/cloud/stackit/sdk/core/auth/SetupAuthTest.java index 8946567..7d6871a 100644 --- a/core/src/test/java/cloud/stackit/sdk/core/auth/SetupAuthTest.java +++ b/core/src/test/java/cloud/stackit/sdk/core/auth/SetupAuthTest.java @@ -21,7 +21,6 @@ import java.util.HashMap; import java.util.Map; import javax.swing.filechooser.FileSystemView; -import okhttp3.Interceptor; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -65,24 +64,6 @@ Path createJsonFile(Map content) throws IOException { return file; } - @Test - void getAccessToken_withoutRunningInit_throwsException() throws IOException { - SetupAuth setupAuth = new SetupAuth(); - assertThrows(RuntimeException.class, setupAuth::getAuthHandler); - } - - @Test - void getAccessToken_withRunningInit_returnsInterceptor() throws IOException { - ServiceAccountKey saKey = createDummyServiceAccount("privateKey"); - String initSaKeyJson = new Gson().toJson(saKey); - - CoreConfiguration config = new CoreConfiguration().serviceAccountKey(initSaKeyJson); - - SetupAuth setupAuth = new SetupAuth(config); - setupAuth.init(); - assertInstanceOf(Interceptor.class, setupAuth.getAuthHandler()); - } - @Test void setupKeyFlow_readServiceAccountFromPath() throws IOException, InvalidKeySpecException, ApiException { @@ -97,7 +78,7 @@ void setupKeyFlow_readServiceAccountFromPath() CoreConfiguration cfg = new CoreConfiguration() .serviceAccountKeyPath(saKeyPath.toAbsolutePath().toString()); - ServiceAccountKey parsedSaKey = new SetupAuth().setupKeyFlow(cfg); + ServiceAccountKey parsedSaKey = SetupAuth.setupKeyFlow(cfg); assertEquals(initSaKey, parsedSaKey); } @@ -111,7 +92,7 @@ void setupKeyFlow_readServiceAccountFromConfig() // Create config and read setup auth with the previous created saKey CoreConfiguration cfg = new CoreConfiguration().serviceAccountKey(initSaKeyJson); - ServiceAccountKey parsedSaKey = new SetupAuth().setupKeyFlow(cfg); + ServiceAccountKey parsedSaKey = SetupAuth.setupKeyFlow(cfg); assertEquals(initSaKey, parsedSaKey); } @@ -127,7 +108,7 @@ void setupKeyFlow_readServiceAccountFromKeyEnv() throws IOException { // Create config and read setup auth with the previous created saKey CoreConfiguration cfg = new CoreConfiguration(); - ServiceAccountKey parsedSaKey = new SetupAuth(cfg, envs).setupKeyFlow(cfg); + ServiceAccountKey parsedSaKey = SetupAuth.setupKeyFlow(cfg, envs); assertEquals(initSaKey, parsedSaKey); } @@ -149,7 +130,7 @@ void setupKeyFlow_readServiceAccountFromKeyPathEnv() throws IOException { // Create config and read setup auth with the previous created saKey CoreConfiguration cfg = new CoreConfiguration(); - ServiceAccountKey parsedSaKey = new SetupAuth(cfg, envs).setupKeyFlow(cfg); + ServiceAccountKey parsedSaKey = SetupAuth.setupKeyFlow(cfg, envs); assertEquals(initSaKey, parsedSaKey); } @@ -171,9 +152,8 @@ void setupKeyFlow_readServiceAccountFromPathWithoutPrivateKey_throwsException() .credentialsFilePath( // make sure that the defaultCredentialsFile is not // used invalidCredentialsFilePath); - SetupAuth auth = new SetupAuth(); - assertThrows(PrivateKeyNotFoundException.class, () -> auth.setupKeyFlow(cfg)); + assertThrows(PrivateKeyNotFoundException.class, () -> SetupAuth.setupKeyFlow(cfg)); } @Test @@ -190,9 +170,8 @@ void setupKeyFlow_readServiceAccountFromConfigWithoutPrivateKey_throwsException( .credentialsFilePath( // make sure that the defaultCredentialsFile is not // used invalidCredentialsFilePath); - SetupAuth auth = new SetupAuth(); - assertThrows(PrivateKeyNotFoundException.class, () -> auth.setupKeyFlow(cfg)); + assertThrows(PrivateKeyNotFoundException.class, () -> SetupAuth.setupKeyFlow(cfg)); } @Test @@ -200,12 +179,11 @@ void loadPrivateKey_setPrivateKeyFromConfig() throws IOException, InvalidKeySpecException, ApiException { final String prvKey = "prvKey"; ServiceAccountKey saKey = createDummyServiceAccount(null); - SetupAuth setupAuth = new SetupAuth(); CoreConfiguration cfg = new CoreConfiguration().privateKey(prvKey); assertNull(saKey.getCredentials().getPrivateKey()); - assertDoesNotThrow(() -> setupAuth.loadPrivateKey(cfg, saKey)); + assertDoesNotThrow(() -> SetupAuth.loadPrivateKey(cfg, new EnvironmentVariables(), saKey)); assertEquals(prvKey, saKey.getCredentials().getPrivateKey()); } @@ -217,11 +195,10 @@ void loadPrivateKey_doesNotOverwriteExistingPrivateKey() // Create Service Account ServiceAccountKey saKey = createDummyServiceAccount(initialPrivateKey); - SetupAuth setupAuth = new SetupAuth(); CoreConfiguration cfg = new CoreConfiguration().privateKey(cfgPrivateKey); assertEquals(initialPrivateKey, saKey.getCredentials().getPrivateKey()); - assertDoesNotThrow(() -> setupAuth.loadPrivateKey(cfg, saKey)); + assertDoesNotThrow(() -> SetupAuth.loadPrivateKey(cfg, new EnvironmentVariables(), saKey)); assertEquals(initialPrivateKey, saKey.getCredentials().getPrivateKey()); } @@ -236,12 +213,11 @@ void loadPrivateKey_setPrivateKeyPath() // Create Service Account ServiceAccountKey saKey = createDummyServiceAccount(null); - SetupAuth setupAuth = new SetupAuth(); CoreConfiguration cfg = new CoreConfiguration().privateKeyPath(tempPrvKeyFile.toAbsolutePath().toString()); assertNull(saKey.getCredentials().getPrivateKey()); - assertDoesNotThrow(() -> setupAuth.loadPrivateKey(cfg, saKey)); + assertDoesNotThrow(() -> SetupAuth.loadPrivateKey(cfg, new EnvironmentVariables(), saKey)); assertEquals(privateKeyContent, saKey.getCredentials().getPrivateKey()); } @@ -271,13 +247,12 @@ void loadPrivateKey_setPrivateKeyPathViaCredentialsFile() // Create ServiceAccount ServiceAccountKey saKey = createDummyServiceAccount(null); - SetupAuth setupAuth = new SetupAuth(); CoreConfiguration cfg = new CoreConfiguration() .credentialsFilePath(tempCredentialsFile.toAbsolutePath().toString()); assertNull(saKey.getCredentials().getPrivateKey()); - assertDoesNotThrow(() -> setupAuth.loadPrivateKey(cfg, saKey)); + assertDoesNotThrow(() -> SetupAuth.loadPrivateKey(cfg, new EnvironmentVariables(), saKey)); assertEquals(privateKeyContent, saKey.getCredentials().getPrivateKey()); } @@ -299,14 +274,13 @@ void loadPrivateKey_setPrivateKeyViaCredentialsFile() // Create dummy service account and config ServiceAccountKey saKey = createDummyServiceAccount(null); - SetupAuth setupAuth = new SetupAuth(); CoreConfiguration cfg = new CoreConfiguration() .credentialsFilePath(tempCredentialsFile.toAbsolutePath().toString()); assertNull(saKey.getCredentials().getPrivateKey()); - assertDoesNotThrow(() -> setupAuth.loadPrivateKey(cfg, saKey)); + assertDoesNotThrow(() -> SetupAuth.loadPrivateKey(cfg, new EnvironmentVariables(), saKey)); assertEquals(privateKeyContent, saKey.getCredentials().getPrivateKey()); } @@ -317,10 +291,9 @@ void loadPrivateKey_setPrivateKeyViaEnv() throws IOException { when(envs.getStackitPrivateKey()).thenReturn(prvKey); CoreConfiguration cfg = new CoreConfiguration(); - SetupAuth setupAuth = new SetupAuth(cfg, envs); assertNull(saKey.getCredentials().getPrivateKey()); - assertDoesNotThrow(() -> setupAuth.loadPrivateKey(cfg, saKey)); + assertDoesNotThrow(() -> SetupAuth.loadPrivateKey(cfg, envs, saKey)); assertEquals(prvKey, saKey.getCredentials().getPrivateKey()); } @@ -336,10 +309,9 @@ void loadPrivateKey_setPrivateKeyPathViaEnv() throws IOException { .thenReturn(tempPrvKeyFile.toAbsolutePath().toString()); CoreConfiguration cfg = new CoreConfiguration(); - SetupAuth setupAuth = new SetupAuth(cfg, envs); assertNull(saKey.getCredentials().getPrivateKey()); - assertDoesNotThrow(() -> setupAuth.loadPrivateKey(cfg, saKey)); + assertDoesNotThrow(() -> SetupAuth.loadPrivateKey(cfg, envs, saKey)); assertEquals(prvKey, saKey.getCredentials().getPrivateKey()); } @@ -362,12 +334,11 @@ void loadPrivateKey_setPrivateKeyViaCredentialsFileInEnv() // Create dummy service account and config ServiceAccountKey saKey = createDummyServiceAccount(null); CoreConfiguration cfg = new CoreConfiguration(); - SetupAuth setupAuth = new SetupAuth(cfg, envs); when(envs.getStackitCredentialsPath()) .thenReturn(tempCredentialsFile.toAbsolutePath().toString()); assertNull(saKey.getCredentials().getPrivateKey()); - assertDoesNotThrow(() -> setupAuth.loadPrivateKey(cfg, saKey)); + assertDoesNotThrow(() -> SetupAuth.loadPrivateKey(cfg, envs, saKey)); assertEquals(privateKeyContent, saKey.getCredentials().getPrivateKey()); } @@ -385,12 +356,13 @@ void loadPrivateKey_invalidPrivateKeyPath_throwsException() + "path.pem"; ServiceAccountKey saKey = createDummyServiceAccount(null); - SetupAuth setupAuth = new SetupAuth(); CoreConfiguration cfg = new CoreConfiguration().privateKeyPath(invalidPath); assertNull(saKey.getCredentials().getPrivateKey()); - assertThrows(PrivateKeyNotFoundException.class, () -> setupAuth.loadPrivateKey(cfg, saKey)); + assertThrows( + PrivateKeyNotFoundException.class, + () -> SetupAuth.loadPrivateKey(cfg, new EnvironmentVariables(), saKey)); } @Test @@ -414,11 +386,10 @@ void readValueFromCredentialsFile_keyAndKeyPathSet_returnsKeyValue() Path credentialsFile = createJsonFile(credentialsFileContent); String result = - new SetupAuth() - .readValueFromCredentialsFile( - credentialsFile.toAbsolutePath().toString(), - EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY, - EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH); + SetupAuth.readValueFromCredentialsFile( + credentialsFile.toAbsolutePath().toString(), + EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY, + EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH); assertEquals(keyContent, result); } @@ -435,11 +406,10 @@ void readValueFromCredentialsFile_keySet_returnsKeyValue() Path credentialsFile = createJsonFile(credentialsFileContent); String result = - new SetupAuth() - .readValueFromCredentialsFile( - credentialsFile.toAbsolutePath().toString(), - EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY, - EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH); + SetupAuth.readValueFromCredentialsFile( + credentialsFile.toAbsolutePath().toString(), + EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY, + EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH); assertEquals(keyContent, result); } @@ -461,11 +431,10 @@ void readValueFromCredentialsFile_KeyPathSet_returnsKeyValue() Path credentialsFile = createJsonFile(credentialsFileContent); String result = - new SetupAuth() - .readValueFromCredentialsFile( - credentialsFile.toAbsolutePath().toString(), - EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY, - EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH); + SetupAuth.readValueFromCredentialsFile( + credentialsFile.toAbsolutePath().toString(), + EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY, + EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH); assertEquals(keyPathContent, result); } diff --git a/examples/authentication/build.gradle b/examples/authentication/build.gradle index 55e63ea..6b1caea 100644 --- a/examples/authentication/build.gradle +++ b/examples/authentication/build.gradle @@ -1,5 +1,6 @@ dependencies { implementation project (':services:resourcemanager') + implementation 'com.squareup.okhttp3:okhttp:4.12.0' } ext.mainClassName = 'cloud.stackit.sdk.authentication.examples.AuthenticationExample' diff --git a/examples/custom-http-client/build.gradle b/examples/custom-http-client/build.gradle new file mode 100644 index 0000000..9a419de --- /dev/null +++ b/examples/custom-http-client/build.gradle @@ -0,0 +1,6 @@ +dependencies { + implementation project (':services:iaas') + implementation 'com.squareup.okhttp3:okhttp:4.12.0' +} + +ext.mainClassName = 'cloud.stackit.sdk.customhttpclient.examples.CustomHttpClientExample' diff --git a/examples/custom-http-client/src/main/java/cloud/stackit/sdk/customhttpclient/examples/CustomHttpClientExample.java b/examples/custom-http-client/src/main/java/cloud/stackit/sdk/customhttpclient/examples/CustomHttpClientExample.java new file mode 100644 index 0000000..7ce4577 --- /dev/null +++ b/examples/custom-http-client/src/main/java/cloud/stackit/sdk/customhttpclient/examples/CustomHttpClientExample.java @@ -0,0 +1,55 @@ +package cloud.stackit.sdk.customhttpclient.examples; + +import cloud.stackit.sdk.core.KeyFlowAuthenticator; +import cloud.stackit.sdk.core.config.CoreConfiguration; +import cloud.stackit.sdk.core.exception.ApiException; +import cloud.stackit.sdk.iaas.api.IaasApi; +import cloud.stackit.sdk.iaas.model.*; +import java.io.IOException; +import java.util.UUID; +import okhttp3.OkHttpClient; + +/* + * This example shows how to pass an existing OkHttpClient object to the STACKIT SDKs ApiClient. + * + * The example shows how to set the authorization header in the OkHttpClient object (required!). + * + * NOTE: Passing the http client is optional, see our other examples where no OkHttpClient object is passed. + * In this case the STACKIT SDK ApiClients will just create their own OkHttpClient objects. + * Nevertheless, for production usage try to use one single OkHttpClient object for everything to take advantage of the + * shared connection pool and to prevent resource leaks. + * + * */ +public class CustomHttpClientExample { + public static void main(String[] args) throws IOException { + // Credentials are read from the credentialsFile in `~/.stackit/credentials.json` or the env + // STACKIT_SERVICE_ACCOUNT_KEY_PATH / STACKIT_SERVICE_ACCOUNT_KEY + CoreConfiguration configuration = new CoreConfiguration(); + + OkHttpClient httpClient = new OkHttpClient(); + KeyFlowAuthenticator authenticator = new KeyFlowAuthenticator(httpClient, configuration); + httpClient = httpClient.newBuilder().authenticator(authenticator).build(); + + // Pass the custom http client + IaasApi iaasApi = new IaasApi(httpClient, configuration); + + // the id of your STACKIT project, read from env var for this example + String projectIdString = System.getenv("STACKIT_PROJECT_ID"); + if (projectIdString == null || projectIdString.isEmpty()) { + System.err.println("Environment variable 'STACKIT_PROJECT_ID' not found."); + return; + } + UUID projectId = UUID.fromString(projectIdString); + + try { + /* list all servers */ + ServerListResponse servers = iaasApi.listServers(projectId, false, null); + System.out.println("\nAvailable servers: "); + for (Server server : servers.getItems()) { + System.out.println("* " + server.getId() + " | " + server.getName()); + } + } catch (ApiException e) { + throw new RuntimeException(e); + } + } +} diff --git a/examples/iaas/build.gradle b/examples/iaas/build.gradle index 4318a91..cadc5a6 100644 --- a/examples/iaas/build.gradle +++ b/examples/iaas/build.gradle @@ -1,5 +1,6 @@ dependencies { implementation project (':services:iaas') + implementation 'com.squareup.okhttp3:okhttp:4.12.0' } ext.mainClassName = 'cloud.stackit.sdk.iaas.examples.IaaSExample' diff --git a/examples/resourcemanager/build.gradle b/examples/resourcemanager/build.gradle index 13ea16b..992cb44 100644 --- a/examples/resourcemanager/build.gradle +++ b/examples/resourcemanager/build.gradle @@ -1,5 +1,6 @@ dependencies { implementation project (':services:resourcemanager') + implementation 'com.squareup.okhttp3:okhttp:4.12.0' } ext.mainClassName = 'cloud.stackit.sdk.resourcemanager.examples.ResourcemanagerExample' diff --git a/services/iaas/settings.gradle b/services/iaas/settings.gradle deleted file mode 100644 index fc3bfe7..0000000 --- a/services/iaas/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "stackit-sdk-iaas" diff --git a/services/iaas/src/main/java/cloud/stackit/sdk/iaas/ApiClient.java b/services/iaas/src/main/java/cloud/stackit/sdk/iaas/ApiClient.java index 0e95c89..a724f2b 100644 --- a/services/iaas/src/main/java/cloud/stackit/sdk/iaas/ApiClient.java +++ b/services/iaas/src/main/java/cloud/stackit/sdk/iaas/ApiClient.java @@ -12,7 +12,7 @@ package cloud.stackit.sdk.iaas; -import cloud.stackit.sdk.core.auth.SetupAuth; +import cloud.stackit.sdk.core.KeyFlowAuthenticator; import cloud.stackit.sdk.core.config.CoreConfiguration; import cloud.stackit.sdk.core.exception.ApiException; import java.io.File; @@ -90,22 +90,50 @@ public class ApiClient { protected JSON json; protected HttpLoggingInterceptor loggingInterceptor; - protected SetupAuth authenticationInterceptor; protected CoreConfiguration configuration; - /** 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 + */ public ApiClient() throws IOException { - this(new CoreConfiguration()); + this(null, new CoreConfiguration()); } /** - * Basic constructor with custom CoreConfiguration + * 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} object + * @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()) { @@ -116,13 +144,15 @@ public ApiClient(CoreConfiguration config) throws IOException { } 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; + } } protected void initHttpClient() { @@ -135,8 +165,6 @@ protected void initHttpClient(List interceptors) { for (Interceptor interceptor : interceptors) { builder.addInterceptor(interceptor); } - // Adds the Authorization header to requests - builder.addInterceptor(authenticationInterceptor.getAuthHandler()); httpClient = builder.build(); } @@ -198,6 +226,15 @@ public ApiClient setServerVariables(Map serverVariables) { return this; } + /** + * Get HTTP client + * + * @return An instance of OkHttpClient + */ + public OkHttpClient getHttpClient() { + return httpClient; + } + /** * Get JSON * diff --git a/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/DefaultApi.java b/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/DefaultApi.java index 9ad35e5..44de926 100644 --- a/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/DefaultApi.java +++ b/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/DefaultApi.java @@ -108,6 +108,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import okhttp3.OkHttpClient; // Package-private access to enforce service-specific API usage (DefaultApi => Api) class DefaultApi { @@ -115,15 +116,51 @@ class DefaultApi { private int localHostIndex; private String localCustomBaseUrl; + /** + * Basic constructor for DefaultApi + * + *

For production use consider using the constructor with the OkHttpClient parameter. + * + * @throws IOException + */ public DefaultApi() throws IOException { - this(new CoreConfiguration()); + 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); } public ApiClient getApiClient() { diff --git a/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/IaasApi.java b/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/IaasApi.java index e0b87aa..57774d1 100644 --- a/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/IaasApi.java +++ b/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/IaasApi.java @@ -1,14 +1,63 @@ +/* + * IaaS-API + * This API allows you to create and modify IaaS resources. + * + * The version of the OpenAPI document: 1 + * Contact: stackit-iaas@mail.schwarz + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + package cloud.stackit.sdk.iaas.api; import cloud.stackit.sdk.core.config.CoreConfiguration; import java.io.IOException; +import okhttp3.OkHttpClient; public class IaasApi extends DefaultApi { + /** + * Basic constructor for IaasApi + * + *

For production use consider using the constructor with the OkHttpClient parameter. + * + * @throws IOException + */ public IaasApi() throws IOException { super(); } + /** + * Basic Constructor for IaasApi + * + *

For production use consider using the constructor with the OkHttpClient parameter. + * + * @param config your STACKIT SDK CoreConfiguration + * @throws IOException + */ public IaasApi(CoreConfiguration configuration) throws IOException { super(configuration); } + + /** + * Constructor for IaasApi + * + * @param httpClient OkHttpClient object + * @throws IOException + */ + public IaasApi(OkHttpClient httpClient) throws IOException { + super(httpClient); + } + + /** + * Constructor for IaasApi + * + * @param httpClient OkHttpClient object + * @param configuraction your STACKIT SDK CoreConfiguration + * @throws IOException + */ + public IaasApi(OkHttpClient httpClient, CoreConfiguration configuration) throws IOException { + super(httpClient, configuration); + } } diff --git a/services/iaas/src/test/java/cloud/stackit/sdk/iaas/api/DefaultApiTest.java b/services/iaas/src/test/java/cloud/stackit/sdk/iaas/api/DefaultApiTest.java new file mode 100644 index 0000000..7485ca9 --- /dev/null +++ b/services/iaas/src/test/java/cloud/stackit/sdk/iaas/api/DefaultApiTest.java @@ -0,0 +1,66 @@ +/* + * IaaS-API + * This API allows you to create and modify IaaS resources. + * + * The version of the OpenAPI document: 1 + * Contact: stackit-iaas@mail.schwarz + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +package cloud.stackit.sdk.iaas; + +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/services/iaas/src/test/java/cloud/stackit/sdk/iaas/api/IaasApiTest.java b/services/iaas/src/test/java/cloud/stackit/sdk/iaas/api/IaasApiTest.java new file mode 100644 index 0000000..6466b93 --- /dev/null +++ b/services/iaas/src/test/java/cloud/stackit/sdk/iaas/api/IaasApiTest.java @@ -0,0 +1,67 @@ +/* + * IaaS-API + * This API allows you to create and modify IaaS resources. + * + * The version of the OpenAPI document: 1 + * Contact: stackit-iaas@mail.schwarz + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +package cloud.stackit.sdk.iaas.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 IaasApi */ +public class IaasApiTest { + @Test + public void TestCustomHttpClient() throws IOException { + // before + CoreConfiguration conf = + new CoreConfiguration().serviceAccountKey(TestUtils.MOCK_SERVICE_ACCOUNT_KEY); + + // when + OkHttpClient httpClient = new OkHttpClient(); + IaasApi api = new IaasApi(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 + IaasApi api = new IaasApi(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()); + } +} diff --git a/services/resourcemanager/settings.gradle b/services/resourcemanager/settings.gradle deleted file mode 100644 index 4a13898..0000000 --- a/services/resourcemanager/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "stackit-sdk-resourcemanager" diff --git a/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/ApiClient.java b/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/ApiClient.java index 1982b1a..8b6a3a0 100644 --- a/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/ApiClient.java +++ b/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/ApiClient.java @@ -12,7 +12,7 @@ package cloud.stackit.sdk.resourcemanager; -import cloud.stackit.sdk.core.auth.SetupAuth; +import cloud.stackit.sdk.core.KeyFlowAuthenticator; import cloud.stackit.sdk.core.config.CoreConfiguration; import cloud.stackit.sdk.core.exception.ApiException; import java.io.File; @@ -89,22 +89,50 @@ public class ApiClient { protected JSON json; protected HttpLoggingInterceptor loggingInterceptor; - protected SetupAuth authenticationInterceptor; protected CoreConfiguration configuration; - /** 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 + */ public ApiClient() throws IOException { - this(new CoreConfiguration()); + this(null, new CoreConfiguration()); } /** - * Basic constructor with custom CoreConfiguration + * 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} object + * @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()) { @@ -115,13 +143,15 @@ public ApiClient(CoreConfiguration config) throws IOException { } 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; + } } protected void initHttpClient() { @@ -134,8 +164,6 @@ protected void initHttpClient(List interceptors) { for (Interceptor interceptor : interceptors) { builder.addInterceptor(interceptor); } - // Adds the Authorization header to requests - builder.addInterceptor(authenticationInterceptor.getAuthHandler()); httpClient = builder.build(); } @@ -197,6 +225,15 @@ public ApiClient setServerVariables(Map serverVariables) { return this; } + /** + * Get HTTP client + * + * @return An instance of OkHttpClient + */ + public OkHttpClient getHttpClient() { + return httpClient; + } + /** * Get JSON * diff --git a/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/DefaultApi.java b/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/DefaultApi.java index 6ca7519..b202096 100644 --- a/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/DefaultApi.java +++ b/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/DefaultApi.java @@ -40,6 +40,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import okhttp3.OkHttpClient; // Package-private access to enforce service-specific API usage (DefaultApi => Api) class DefaultApi { @@ -47,15 +48,51 @@ class DefaultApi { private int localHostIndex; private String localCustomBaseUrl; + /** + * Basic constructor for DefaultApi + * + *

For production use consider using the constructor with the OkHttpClient parameter. + * + * @throws IOException + */ public DefaultApi() throws IOException { - this(new CoreConfiguration()); + 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); } public ApiClient getApiClient() { diff --git a/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApi.java b/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApi.java index c48b804..ee397d5 100644 --- a/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApi.java +++ b/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApi.java @@ -1,14 +1,64 @@ +/* + * Resource Manager API + * API v2 to manage resource containers - organizations, folders, projects incl. labels ### Resource Management STACKIT resource management handles the terms _Organization_, _Folder_, _Project_, _Label_, and the hierarchical structure between them. Technically, organizations, folders, and projects are _Resource Containers_ to which a _Label_ can be attached to. The STACKIT _Resource Manager_ provides CRUD endpoints to query and to modify the state. ### Organizations STACKIT organizations are the base element to create and to use cloud-resources. An organization is bound to one customer account. Organizations have a lifecycle. - Organizations are always the root node in resource hierarchy and do not have a parent ### Projects STACKIT projects are needed to use cloud-resources. Projects serve as wrapper for underlying technical structures and processes. Projects have a lifecycle. Projects compared to folders may have different policies. - Projects are optional, but mandatory for cloud-resource usage - A project can be created having either an organization, or a folder as parent - A project must not have a project as parent - Project names under the same parent must not be unique - Root organization cannot be changed ### Label STACKIT labels are key-value pairs including a resource container reference. Labels can be defined and attached freely to resource containers by which resources can be organized and queried. - Policy-based, immutable labels may exists + * + * The version of the OpenAPI document: 2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + package cloud.stackit.sdk.resourcemanager.api; import cloud.stackit.sdk.core.config.CoreConfiguration; import java.io.IOException; +import okhttp3.OkHttpClient; public class ResourceManagerApi extends DefaultApi { + /** + * Basic constructor for ResourceManagerApi + * + *

For production use consider using the constructor with the OkHttpClient parameter. + * + * @throws IOException + */ public ResourceManagerApi() throws IOException { super(); } + /** + * Basic Constructor for ResourceManagerApi + * + *

For production use consider using the constructor with the OkHttpClient parameter. + * + * @param config your STACKIT SDK CoreConfiguration + * @throws IOException + */ public ResourceManagerApi(CoreConfiguration configuration) throws IOException { super(configuration); } + + /** + * Constructor for ResourceManagerApi + * + * @param httpClient OkHttpClient object + * @throws IOException + */ + public ResourceManagerApi(OkHttpClient httpClient) throws IOException { + super(httpClient); + } + + /** + * Constructor for ResourceManagerApi + * + * @param httpClient OkHttpClient object + * @param configuraction your STACKIT SDK CoreConfiguration + * @throws IOException + */ + public ResourceManagerApi(OkHttpClient httpClient, CoreConfiguration configuration) + throws IOException { + super(httpClient, configuration); + } } diff --git a/services/resourcemanager/src/test/java/cloud/stackit/sdk/resourcemanager/api/DefaultApiTest.java b/services/resourcemanager/src/test/java/cloud/stackit/sdk/resourcemanager/api/DefaultApiTest.java new file mode 100644 index 0000000..74cd721 --- /dev/null +++ b/services/resourcemanager/src/test/java/cloud/stackit/sdk/resourcemanager/api/DefaultApiTest.java @@ -0,0 +1,66 @@ +/* + * Resource Manager API + * API v2 to manage resource containers - organizations, folders, projects incl. labels ### Resource Management STACKIT resource management handles the terms _Organization_, _Folder_, _Project_, _Label_, and the hierarchical structure between them. Technically, organizations, folders, and projects are _Resource Containers_ to which a _Label_ can be attached to. The STACKIT _Resource Manager_ provides CRUD endpoints to query and to modify the state. ### Organizations STACKIT organizations are the base element to create and to use cloud-resources. An organization is bound to one customer account. Organizations have a lifecycle. - Organizations are always the root node in resource hierarchy and do not have a parent ### Projects STACKIT projects are needed to use cloud-resources. Projects serve as wrapper for underlying technical structures and processes. Projects have a lifecycle. Projects compared to folders may have different policies. - Projects are optional, but mandatory for cloud-resource usage - A project can be created having either an organization, or a folder as parent - A project must not have a project as parent - Project names under the same parent must not be unique - Root organization cannot be changed ### Label STACKIT labels are key-value pairs including a resource container reference. Labels can be defined and attached freely to resource containers by which resources can be organized and queried. - Policy-based, immutable labels may exists + * + * The version of the OpenAPI document: 2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +package cloud.stackit.sdk.resourcemanager; + +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/services/resourcemanager/src/test/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApiTest.java b/services/resourcemanager/src/test/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApiTest.java new file mode 100644 index 0000000..2e56b96 --- /dev/null +++ b/services/resourcemanager/src/test/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApiTest.java @@ -0,0 +1,67 @@ +/* + * Resource Manager API + * API v2 to manage resource containers - organizations, folders, projects incl. labels ### Resource Management STACKIT resource management handles the terms _Organization_, _Folder_, _Project_, _Label_, and the hierarchical structure between them. Technically, organizations, folders, and projects are _Resource Containers_ to which a _Label_ can be attached to. The STACKIT _Resource Manager_ provides CRUD endpoints to query and to modify the state. ### Organizations STACKIT organizations are the base element to create and to use cloud-resources. An organization is bound to one customer account. Organizations have a lifecycle. - Organizations are always the root node in resource hierarchy and do not have a parent ### Projects STACKIT projects are needed to use cloud-resources. Projects serve as wrapper for underlying technical structures and processes. Projects have a lifecycle. Projects compared to folders may have different policies. - Projects are optional, but mandatory for cloud-resource usage - A project can be created having either an organization, or a folder as parent - A project must not have a project as parent - Project names under the same parent must not be unique - Root organization cannot be changed ### Label STACKIT labels are key-value pairs including a resource container reference. Labels can be defined and attached freely to resource containers by which resources can be organized and queried. - Policy-based, immutable labels may exists + * + * The version of the OpenAPI document: 2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +package cloud.stackit.sdk.resourcemanager.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 ResourceManagerApi */ +public class ResourceManagerApiTest { + @Test + public void TestCustomHttpClient() throws IOException { + // before + CoreConfiguration conf = + new CoreConfiguration().serviceAccountKey(TestUtils.MOCK_SERVICE_ACCOUNT_KEY); + + // when + OkHttpClient httpClient = new OkHttpClient(); + ResourceManagerApi api = new ResourceManagerApi(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 + ResourceManagerApi api = new ResourceManagerApi(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()); + } +} From 9e3cb97739bd08dac375bfcebf02466aa3ebbaf7 Mon Sep 17 00:00:00 2001 From: Ruben Hoenle Date: Tue, 23 Sep 2025 16:38:38 +0200 Subject: [PATCH 2/5] review findings on generator side --- .../iaas/src/main/java/cloud/stackit/sdk/iaas/api/IaasApi.java | 2 +- .../stackit/sdk/resourcemanager/api/ResourceManagerApi.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/IaasApi.java b/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/IaasApi.java index 57774d1..c602505 100644 --- a/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/IaasApi.java +++ b/services/iaas/src/main/java/cloud/stackit/sdk/iaas/api/IaasApi.java @@ -54,7 +54,7 @@ public IaasApi(OkHttpClient httpClient) throws IOException { * Constructor for IaasApi * * @param httpClient OkHttpClient object - * @param configuraction your STACKIT SDK CoreConfiguration + * @param configuration your STACKIT SDK CoreConfiguration * @throws IOException */ public IaasApi(OkHttpClient httpClient, CoreConfiguration configuration) throws IOException { diff --git a/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApi.java b/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApi.java index ee397d5..19c552f 100644 --- a/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApi.java +++ b/services/resourcemanager/src/main/java/cloud/stackit/sdk/resourcemanager/api/ResourceManagerApi.java @@ -54,7 +54,7 @@ public ResourceManagerApi(OkHttpClient httpClient) throws IOException { * Constructor for ResourceManagerApi * * @param httpClient OkHttpClient object - * @param configuraction your STACKIT SDK CoreConfiguration + * @param configuration your STACKIT SDK CoreConfiguration * @throws IOException */ public ResourceManagerApi(OkHttpClient httpClient, CoreConfiguration configuration) From 4f0b36f38f322ccd32fca204568b5004a0567cea Mon Sep 17 00:00:00 2001 From: Ruben Hoenle Date: Tue, 23 Sep 2025 17:08:28 +0200 Subject: [PATCH 3/5] add note to readme --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e39fa67..95ce856 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,12 @@ Add the dependencies to your project's build file (replace `` with ## Examples -Examples on services, configuration and authentication possibilities can be found in the [examples folder](https://github.com/stackitcloud/stackit-sdk-java/tree/main/examples). +Examples on services, configuration and authentication possibilities can be found in the [examples folder](/examples). + +> [!WARNING] +> For production usage, especially when working with multiple STACKIT SDK modules, consider passing your own `OkHttpClient` +> object (as recommended in the [OkHttpClient lib docs](https://square.github.io/okhttp/3.x/okhttp/index.html?okhttp3/OkHttpClient.html)). +> See our [custom HTTP client example](/examples/custom-http-client/src/main/java/cloud/stackit/sdk/customhttpclient/examples/CustomHttpClientExample.java) for reference. ## Authorization From fabac6be4a092b5a879b1c36b869b44860618b20 Mon Sep 17 00:00:00 2001 From: Ruben Hoenle Date: Wed, 24 Sep 2025 16:23:12 +0200 Subject: [PATCH 4/5] add changelog entries --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ core/CHANGELOG.md | 10 ++++++++++ core/VERSION | 2 +- services/iaas/CHANGELOG.md | 8 ++++++++ services/iaas/VERSION | 2 +- services/resourcemanager/CHANGELOG.md | 8 ++++++++ services/resourcemanager/VERSION | 2 +- 7 files changed, 56 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 877dcdf..9528cb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +## Release (2025-XX-XX) +- `core`: [v0.2.0](core/CHANGELOG.md#v020) + - **Feature:** Support for passing custom OkHttpClient objects + - `KeyFlowAuthenticator`: Add new constructors with an `OkHttpClientParam` + - Marked constructors without `OkHttpClient` param as deprecated, use new constructors with `OkHttpClient` instead + - `KeyFlowAuthenticator` implements `okhttp3.Authenticator` interface now + - added method `KeyFlowAuthenticator.authenticate()` + - Marked `KeyFlowInterceptor` class as deprecated, use `KeyFlowAuthenticator` instead + - Marked `SetupAuth` constructors and methods `SetupAuth.init()` and `SetupAuth.getAuthHandler()` as deprecated + - all other methods of `SetupAuth` are marked as `static` now, only these will remain in the future +- `iaas`: [v0.2.0](iaas/CHANGELOG.md#v020) + - **Feature:** Support for passing custom OkHttpClient objects + - `ApiClient` + - Added constructors with `OkHttpClient` param (recommended for production use) + - Use new `KeyFlowAuthenticator` `okhttp3.Authenticator` implementation instead of request interceptor for authentication + - `DefaultApi`: Added constructors with `OkHttpClient` param (recommended for production use) + - `IaasApi`: Added constructors with `OkHttpClient` param (recommended for production use) +- `resourcemanager`: [v0.2.0](resourcemanager/CHANGELOG.md#v020) + - **Feature:** Support for passing custom OkHttpClient objects + - `ApiClient` + - Added constructors with `OkHttpClient` param (recommended for production use) + - Use new `KeyFlowAuthenticator` `okhttp3.Authenticator` implementation instead of request interceptor for authentication + - `DefaultApi`: Added constructors with `OkHttpClient` param (recommended for production use) + - `ResourceManagerApi`: Added constructors with `OkHttpClient` param (recommended for production use) +- `examples`: + - Add example how to use custom `OkHttpClient` object + ## Release (2025-09-09) - `core`: [v0.1.0](core/CHANGELOG.md#v010) - Initial onboarding of STACKIT Java SDK core lib diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 5b294a0..324370b 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,2 +1,12 @@ +## v0.2.0 +- **Feature:** Support for passing custom OkHttpClient objects + - `KeyFlowAuthenticator`: Add new constructors with an `OkHttpClientParam` + - Marked constructors without `OkHttpClient` param as deprecated, use new constructors with `OkHttpClient` instead + - `KeyFlowAuthenticator` implements `okhttp3.Authenticator` interface now + - added method `KeyFlowAuthenticator.authenticate()` + - Marked `KeyFlowInterceptor` class as deprecated, use `KeyFlowAuthenticator` instead + - Marked `SetupAuth` constructors and methods `SetupAuth.init()` and `SetupAuth.getAuthHandler()` as deprecated + - all other methods of `SetupAuth` are marked as `static` now, only these will remain in the future + ## v0.1.0 - Initial onboarding of STACKIT Java SDK core lib diff --git a/core/VERSION b/core/VERSION index 6e8bf73..0ea3a94 100644 --- a/core/VERSION +++ b/core/VERSION @@ -1 +1 @@ -0.1.0 +0.2.0 diff --git a/services/iaas/CHANGELOG.md b/services/iaas/CHANGELOG.md index 4892bff..c72e4b1 100644 --- a/services/iaas/CHANGELOG.md +++ b/services/iaas/CHANGELOG.md @@ -1,2 +1,10 @@ +## v0.2.0 +- **Feature:** Support for passing custom OkHttpClient objects + - `ApiClient` + - Added constructors with `OkHttpClient` param (recommended for production use) + - Use new `KeyFlowAuthenticator` `okhttp3.Authenticator` implementation instead of request interceptor for authentication + - `DefaultApi`: Added constructors with `OkHttpClient` param (recommended for production use) + - `IaasApi`: Added constructors with `OkHttpClient` param (recommended for production use) + ## v0.1.0 - Initial onboarding of STACKIT Java SDK for IaaS service diff --git a/services/iaas/VERSION b/services/iaas/VERSION index 6e8bf73..0ea3a94 100644 --- a/services/iaas/VERSION +++ b/services/iaas/VERSION @@ -1 +1 @@ -0.1.0 +0.2.0 diff --git a/services/resourcemanager/CHANGELOG.md b/services/resourcemanager/CHANGELOG.md index e43ed7a..d9d7ce4 100644 --- a/services/resourcemanager/CHANGELOG.md +++ b/services/resourcemanager/CHANGELOG.md @@ -1,2 +1,10 @@ +## v0.2.0 +- **Feature:** Support for passing custom OkHttpClient objects + - `ApiClient` + - Added constructors with `OkHttpClient` param (recommended for production use) + - Use new `KeyFlowAuthenticator` `okhttp3.Authenticator` implementation instead of request interceptor for authentication + - `DefaultApi`: Added constructors with `OkHttpClient` param (recommended for production use) + - `ResourceManagerApi`: Added constructors with `OkHttpClient` param (recommended for production use) + ## v0.1.0 - Initial onboarding of STACKIT Java SDK for Resourcemanager service diff --git a/services/resourcemanager/VERSION b/services/resourcemanager/VERSION index 6e8bf73..0ea3a94 100644 --- a/services/resourcemanager/VERSION +++ b/services/resourcemanager/VERSION @@ -1 +1 @@ -0.1.0 +0.2.0 From bad2e3f941259046e19e811383a9dac8dc620d69 Mon Sep 17 00:00:00 2001 From: Ruben Hoenle Date: Wed, 24 Sep 2025 16:32:33 +0200 Subject: [PATCH 5/5] fix changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9528cb2..2a4bce8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,14 +8,14 @@ - Marked `KeyFlowInterceptor` class as deprecated, use `KeyFlowAuthenticator` instead - Marked `SetupAuth` constructors and methods `SetupAuth.init()` and `SetupAuth.getAuthHandler()` as deprecated - all other methods of `SetupAuth` are marked as `static` now, only these will remain in the future -- `iaas`: [v0.2.0](iaas/CHANGELOG.md#v020) +- `iaas`: [v0.2.0](services/iaas/CHANGELOG.md#v020) - **Feature:** Support for passing custom OkHttpClient objects - `ApiClient` - Added constructors with `OkHttpClient` param (recommended for production use) - Use new `KeyFlowAuthenticator` `okhttp3.Authenticator` implementation instead of request interceptor for authentication - `DefaultApi`: Added constructors with `OkHttpClient` param (recommended for production use) - `IaasApi`: Added constructors with `OkHttpClient` param (recommended for production use) -- `resourcemanager`: [v0.2.0](resourcemanager/CHANGELOG.md#v020) +- `resourcemanager`: [v0.2.0](services/resourcemanager/CHANGELOG.md#v020) - **Feature:** Support for passing custom OkHttpClient objects - `ApiClient` - Added constructors with `OkHttpClient` param (recommended for production use)