items) {
return new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8));
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public int getFailureCount() {
return (int) (pointReportErrors.count() + histogramReportErrors.count() +
spanReportErrors.count() + eventsReportErrors.count());
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public synchronized void close() {
if (!closed.compareAndSet(false, true)) {
@@ -859,13 +959,13 @@ public synchronized void close() {
/**
* Dequeue and return a batch of at most N items from buffer (where N = batchSize), broken into
* chunks where each chunk has at most M bytes of data (where M = messageSizeBytes).
- *
+ *
* Visible for testing.
*
- * @param buffer The buffer queue to retrieve items from.
- * @param batchSize The maximum number of items to retrieve from the buffer.
- * @param messageSizeBytes The maximum number of bytes in each chunk.
- * @param dropped A counter counting the number of items that are dropped.
+ * @param buffer The buffer queue to retrieve items from.
+ * @param batchSize The maximum number of items to retrieve from the buffer.
+ * @param messageSizeBytes The maximum number of bytes in each chunk.
+ * @param dropped A counter counting the number of items that are dropped.
* @return A batch of items retrieved from buffer.
*/
static List> getBatch(LinkedBlockingQueue buffer, int batchSize,
diff --git a/src/main/java/com/wavefront/sdk/common/clients/service/ReportingService.java b/src/main/java/com/wavefront/sdk/common/clients/service/ReportingService.java
index 6be06407..0eb3868c 100644
--- a/src/main/java/com/wavefront/sdk/common/clients/service/ReportingService.java
+++ b/src/main/java/com/wavefront/sdk/common/clients/service/ReportingService.java
@@ -1,8 +1,10 @@
package com.wavefront.sdk.common.clients.service;
import com.google.common.annotations.VisibleForTesting;
+
import com.wavefront.sdk.common.Constants;
-import com.wavefront.sdk.common.annotation.Nullable;
+import com.wavefront.sdk.common.Utils;
+import com.wavefront.sdk.common.clients.service.token.TokenService;
import com.wavefront.sdk.common.logging.MessageSuppressingLogger;
import java.io.IOException;
@@ -28,7 +30,8 @@ public class ReportingService implements ReportAPI {
// This logger is intended to be configurable in the WavefrontClient.Builder. Given that the invoker controls the
// configuration, this is not a static logger.
private final MessageSuppressingLogger messageSuppressingLogger;
- private final String token;
+
+ private final TokenService tokenService;
private final URI uri;
private static final int CONNECT_TIMEOUT_MILLIS = 30000;
@@ -39,13 +42,14 @@ public class ReportingService implements ReportAPI {
/**
* Constructor for ReportingService.
*
- * @param uri a {@link java.net.URI} object
- * @param token a {@link java.lang.String} object
+ * @param uri a {@link java.net.URI} object
+ * @param tokenService a {@link TokenService} object
* @param reportingServiceLogSuppressTimeSeconds a long
*/
- public ReportingService(URI uri, @Nullable String token, long reportingServiceLogSuppressTimeSeconds) {
+ public ReportingService(URI uri, TokenService tokenService, long reportingServiceLogSuppressTimeSeconds) {
this.uri = uri;
- this.token = token;
+ this.tokenService = tokenService;
+
// Setting suppress time to 0 invalidates the cache used by the message suppressing logger and doesn't log anything.
// So defaulting to the minimum of 1 second.
reportingServiceLogSuppressTimeSeconds = reportingServiceLogSuppressTimeSeconds <= 0 ? 1 : reportingServiceLogSuppressTimeSeconds;
@@ -53,7 +57,9 @@ public ReportingService(URI uri, @Nullable String token, long reportingServiceLo
ReportingService.class.getCanonicalName()), reportingServiceLogSuppressTimeSeconds, TimeUnit.SECONDS);
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public int send(String format, InputStream stream) {
HttpURLConnection urlConn = null;
@@ -65,9 +71,13 @@ public int send(String format, InputStream stream) {
urlConn.setRequestMethod("POST");
urlConn.addRequestProperty("Content-Type", "application/octet-stream");
urlConn.addRequestProperty("Content-Encoding", "gzip");
- if (token != null && !token.equals("")) {
+
+ String token = tokenService.getToken();
+
+ if (!Utils.isNullOrEmpty(token)) {
urlConn.addRequestProperty("Authorization", "Bearer " + token);
}
+
urlConn.setConnectTimeout(CONNECT_TIMEOUT_MILLIS);
urlConn.setReadTimeout(READ_TIMEOUT_MILLIS);
@@ -89,7 +99,9 @@ public int send(String format, InputStream stream) {
return statusCode;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public int sendEvent(InputStream stream) {
HttpURLConnection urlConn = null;
@@ -100,7 +112,9 @@ public int sendEvent(InputStream stream) {
urlConn.setDoOutput(true);
urlConn.setRequestMethod("POST");
- if (token != null && !token.equals("")) {
+ String token = tokenService.getToken();
+
+ if (!Utils.isNullOrEmpty(token)) {
urlConn.addRequestProperty("Authorization", "Bearer " + token);
}
urlConn.setConnectTimeout(CONNECT_TIMEOUT_MILLIS);
diff --git a/src/main/java/com/wavefront/sdk/common/clients/service/token/CSPAuthorizeResponse.java b/src/main/java/com/wavefront/sdk/common/clients/service/token/CSPAuthorizeResponse.java
new file mode 100644
index 00000000..adbe4580
--- /dev/null
+++ b/src/main/java/com/wavefront/sdk/common/clients/service/token/CSPAuthorizeResponse.java
@@ -0,0 +1,23 @@
+package com.wavefront.sdk.common.clients.service.token;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class CSPAuthorizeResponse {
+ @JsonProperty("id_token")
+ public String idToken;
+
+ @JsonProperty("token_type")
+ public String tokenType;
+
+ // This is in seconds
+ @JsonProperty("expires_in")
+ public int expiresIn;
+
+ public String scope;
+
+ @JsonProperty("access_token")
+ public String accessToken;
+
+ @JsonProperty("refresh_token")
+ public String refreshToken;
+}
diff --git a/src/main/java/com/wavefront/sdk/common/clients/service/token/CSPServerToServerTokenService.java b/src/main/java/com/wavefront/sdk/common/clients/service/token/CSPServerToServerTokenService.java
new file mode 100644
index 00000000..40d039da
--- /dev/null
+++ b/src/main/java/com/wavefront/sdk/common/clients/service/token/CSPServerToServerTokenService.java
@@ -0,0 +1,179 @@
+package com.wavefront.sdk.common.clients.service.token;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.wavefront.sdk.common.NamedThreadFactory;
+import com.wavefront.sdk.common.Utils;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+public class CSPServerToServerTokenService implements TokenService, Runnable {
+ private static final Logger log = Logger.getLogger(CSPServerToServerTokenService.class.getCanonicalName());
+
+ private final static String OAUTH_PATH = "/csp/gateway/am/api/auth/authorize";
+ private final static int TEN_MINUTES = 600;
+ private final static int THIRTY_SECONDS = 30;
+ private final static int THREE_MINUTES = 180;
+ private static int DEFAULT_THREAD_DELAY = 60;
+
+ private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("csp-server-to-server-token-service"));
+ private final ObjectMapper mapper = new ObjectMapper();
+ private final AtomicBoolean tokenReady = new AtomicBoolean(false);
+
+ private final String cspBaseURL;
+ private final String cspClientId;
+ private final String cspClientSecret;
+
+ private final int connectTimeoutMillis;
+ private final int readTimeoutMillis;
+ private String cspAccessToken;
+
+ public CSPServerToServerTokenService(final String cspBaseURL, final String cspClientId, final String cspClientSecret) {
+ this.cspBaseURL = cspBaseURL;
+ this.cspClientId = cspClientId;
+ this.cspClientSecret = cspClientSecret;
+ this.connectTimeoutMillis = 30_000;
+ this.readTimeoutMillis = 10_000;
+ }
+
+ public CSPServerToServerTokenService(final String cspBaseURL, final String cspClientId, final String cspClientSecret, final int connectTimeoutMillis, final int readTimeoutMillis) {
+ this.cspBaseURL = cspBaseURL;
+ this.cspClientId = cspClientId;
+ this.cspClientSecret = cspClientSecret;
+ this.connectTimeoutMillis = connectTimeoutMillis;
+ this.readTimeoutMillis = readTimeoutMillis;
+ }
+
+ @Override
+ public synchronized String getToken() {
+ // First access gets the token and is blocking, which schedules the next token fetch.
+ if (!tokenReady.get()) {
+ run();
+ tokenReady.set(true);
+ }
+
+ return cspAccessToken;
+ }
+
+ private String getCSPToken() {
+ HttpURLConnection urlConn = null;
+
+ final String urlParameters = "grant_type=client_credentials";
+ final byte[] postData = urlParameters.getBytes(StandardCharsets.UTF_8);
+
+ try {
+ final URL url = new URL(cspBaseURL + OAUTH_PATH);
+ urlConn = (HttpURLConnection) url.openConnection();
+ urlConn.setDoOutput(true);
+ urlConn.setRequestMethod("POST");
+ urlConn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ urlConn.addRequestProperty("Authorization", "Basic " + buildHttpBasicToken(cspClientId, cspClientSecret));
+ urlConn.setRequestProperty("Content-Length", Integer.toString(postData.length));
+
+ urlConn.setConnectTimeout(connectTimeoutMillis);
+ urlConn.setReadTimeout(readTimeoutMillis);
+
+ //Send request
+ final DataOutputStream wr = new DataOutputStream(urlConn.getOutputStream());
+ wr.write(postData);
+ wr.flush();
+ wr.close();
+
+ final int statusCode = urlConn.getResponseCode();
+
+ if (statusCode == 200) {
+ try {
+ final CSPAuthorizeResponse parsedResponse = mapper.readValue(urlConn.getInputStream(), CSPAuthorizeResponse.class);
+
+ if (!hasDirectIngestScope(parsedResponse.scope)) {
+ log.warning("The CSP response did not find any scope matching 'aoa:directDataIngestion' which is required for Wavefront direct ingestion.");
+ }
+
+ // Schedule token refresh in the future
+ int threadDelay = getThreadDelay(parsedResponse.expiresIn);
+
+ log.info("A CSP token has been received. Will schedule the CSP token to be refreshed in: " + threadDelay + " seconds");
+
+ executor.schedule(this, threadDelay, TimeUnit.SECONDS);
+
+ return parsedResponse.accessToken;
+ } catch (JsonProcessingException e) {
+ log.severe("The request to CSP returned invalid json. Please restart your app.");
+
+ return "INVALID_TOKEN";
+ }
+ } else {
+ log.warning("The request to CSP returned: " + statusCode);
+
+ if (statusCode >= 500 && statusCode < 600) {
+ log.info("The Wavefront SDK will try to reauthenticate with CSP on the next request.");
+ tokenReady.set(false);
+
+ return null;
+ }
+
+ // Anything not 5xx will return INVALID_TOKEN
+ return "INVALID_TOKEN";
+ }
+
+ } catch (IOException ex) {
+ // Connection Problem
+ log.warning("Error connecting to CSP: " + ex.getLocalizedMessage());
+
+ log.info("The Wavefront SDK will try to reauthenticate with CSP on the next request.");
+ tokenReady.set(false);
+
+ return null;
+ }
+ }
+
+ private int getThreadDelay(final int expiresIn) {
+ int retVal;
+
+ if (expiresIn < TEN_MINUTES) {
+ retVal = expiresIn - THIRTY_SECONDS;
+ } else {
+ retVal = expiresIn - THREE_MINUTES;
+ }
+
+ if (retVal <= 0) {
+ retVal = DEFAULT_THREAD_DELAY;
+ }
+
+ return retVal;
+ }
+
+ public synchronized void run() {
+ this.cspAccessToken = getCSPToken();
+ }
+
+ private static List parseScopes(final String scope) {
+ return Arrays.stream(scope.split("\\s")).collect(Collectors.toList());
+ }
+
+ public static boolean hasDirectIngestScope(final String scopeList) {
+ if (!Utils.isNullOrEmpty(scopeList)) {
+ return parseScopes(scopeList).stream().anyMatch(s -> s.contains("aoa:directDataIngestion") || s.contains("aoa/*") || s.contains("aoa:*"));
+ }
+
+ return false;
+ }
+
+ private String buildHttpBasicToken(final String cspClientId, final String cspClientSecret) {
+ final String encodeMe = cspClientId + ":" + cspClientSecret;
+ return Base64.getEncoder().encodeToString(encodeMe.getBytes());
+ }
+}
diff --git a/src/main/java/com/wavefront/sdk/common/clients/service/token/NoopTokenService.java b/src/main/java/com/wavefront/sdk/common/clients/service/token/NoopTokenService.java
new file mode 100644
index 00000000..12173907
--- /dev/null
+++ b/src/main/java/com/wavefront/sdk/common/clients/service/token/NoopTokenService.java
@@ -0,0 +1,13 @@
+package com.wavefront.sdk.common.clients.service.token;
+
+// Primarily for proxy usage
+public class NoopTokenService implements TokenService {
+
+ public NoopTokenService() {
+ }
+
+ @Override
+ public String getToken() {
+ return "";
+ }
+}
diff --git a/src/main/java/com/wavefront/sdk/common/clients/service/token/TokenService.java b/src/main/java/com/wavefront/sdk/common/clients/service/token/TokenService.java
new file mode 100644
index 00000000..89bd97e9
--- /dev/null
+++ b/src/main/java/com/wavefront/sdk/common/clients/service/token/TokenService.java
@@ -0,0 +1,5 @@
+package com.wavefront.sdk.common.clients.service.token;
+
+public interface TokenService {
+ String getToken();
+}
diff --git a/src/main/java/com/wavefront/sdk/common/clients/service/token/WavefrontTokenService.java b/src/main/java/com/wavefront/sdk/common/clients/service/token/WavefrontTokenService.java
new file mode 100644
index 00000000..0a8053df
--- /dev/null
+++ b/src/main/java/com/wavefront/sdk/common/clients/service/token/WavefrontTokenService.java
@@ -0,0 +1,14 @@
+package com.wavefront.sdk.common.clients.service.token;
+
+public class WavefrontTokenService implements TokenService {
+ private final String token;
+
+ public WavefrontTokenService(final String token) {
+ this.token = token;
+ }
+
+ @Override
+ public String getToken() {
+ return token;
+ }
+}
diff --git a/src/test/java/com/wavefront/sdk/common/UtilsTest.java b/src/test/java/com/wavefront/sdk/common/UtilsTest.java
index 4c5661df..0a6e8e04 100644
--- a/src/test/java/com/wavefront/sdk/common/UtilsTest.java
+++ b/src/test/java/com/wavefront/sdk/common/UtilsTest.java
@@ -17,6 +17,7 @@
import static com.wavefront.sdk.common.Utils.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.fail;
/**
@@ -850,4 +851,13 @@ public void convertSemVerToGauge() throws IOException {
assertEquals(1.1010D, Utils.convertSemVerToGauge("1.10.10"));
}
+ @Test
+ public void testIsNullOrEmpty() {
+ assertTrue(Utils.isNullOrEmpty(null));
+
+ assertTrue(Utils.isNullOrEmpty(""));
+
+ assertFalse(Utils.isNullOrEmpty("hello"));
+ }
+
}
diff --git a/src/test/java/com/wavefront/sdk/common/clients/WavefrontClientTest.java b/src/test/java/com/wavefront/sdk/common/clients/WavefrontClientTest.java
index c08b9b33..261f1ab8 100644
--- a/src/test/java/com/wavefront/sdk/common/clients/WavefrontClientTest.java
+++ b/src/test/java/com/wavefront/sdk/common/clients/WavefrontClientTest.java
@@ -3,10 +3,13 @@
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.wavefront.sdk.common.Pair;
+import com.wavefront.sdk.common.clients.service.token.CSPServerToServerTokenService;
+import com.wavefront.sdk.common.clients.service.token.NoopTokenService;
+import com.wavefront.sdk.common.clients.service.token.WavefrontTokenService;
import com.wavefront.sdk.common.metrics.WavefrontSdkDeltaCounter;
import com.wavefront.sdk.entities.histograms.HistogramGranularity;
-
import com.wavefront.sdk.entities.tracing.SpanLog;
+
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
@@ -16,15 +19,28 @@
import org.junit.jupiter.params.provider.ValueSource;
import java.net.ServerSocket;
-import java.util.*;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
-import static com.github.tomakehurst.wiremock.client.WireMock.*;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson;
+import static com.github.tomakehurst.wiremock.client.WireMock.matching;
+import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath;
+import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
-import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
@@ -214,6 +230,28 @@ void canSendTracesToDifferentPort() {
@Nested
class Builder {
+
+ @Test
+ public void tokenServiceClassTest() {
+ WavefrontClient wfClient = new WavefrontClient.Builder("", "TOKEN")
+ .build();
+ assertNotNull(wfClient);
+ assertNotNull(wfClient.getTokenService());
+ assertEquals(wfClient.getTokenService().getClass().getSimpleName(), WavefrontTokenService.class.getSimpleName());
+
+ wfClient = new WavefrontClient.Builder("")
+ .build();
+ assertNotNull(wfClient);
+ assertNotNull(wfClient.getTokenService());
+ assertEquals(wfClient.getTokenService().getClass().getSimpleName(), NoopTokenService.class.getSimpleName());
+
+ wfClient = new WavefrontClient.Builder("", "cspClientId", "cspClientSecret")
+ .build();
+ assertNotNull(wfClient);
+ assertNotNull(wfClient.getTokenService());
+ assertEquals(wfClient.getTokenService().getClass().getSimpleName(), CSPServerToServerTokenService.class.getSimpleName());
+ }
+
@Nested
class ValidateEndpoint {
@Test
diff --git a/src/test/java/com/wavefront/sdk/common/clients/service/CSPServerToServerTokenServiceTest.java b/src/test/java/com/wavefront/sdk/common/clients/service/CSPServerToServerTokenServiceTest.java
new file mode 100644
index 00000000..76d162f0
--- /dev/null
+++ b/src/test/java/com/wavefront/sdk/common/clients/service/CSPServerToServerTokenServiceTest.java
@@ -0,0 +1,127 @@
+package com.wavefront.sdk.common.clients.service;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.stubbing.Scenario;
+import com.wavefront.sdk.common.clients.service.token.CSPServerToServerTokenService;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Field;
+import java.util.UUID;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.*;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+import static com.wavefront.sdk.common.clients.service.token.CSPServerToServerTokenService.hasDirectIngestScope;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CSPServerToServerTokenServiceTest {
+
+ @Test
+ public void testHasDirectIngestScope() {
+ final String uuid = UUID.randomUUID().toString();
+
+ final String scopeString = "external/" + uuid + "/*/aoa:directDataIngestion external/" + uuid + "/aoa:directDataIngestion csp:org_member";
+
+ assertTrue(hasDirectIngestScope(scopeString));
+ assertFalse(hasDirectIngestScope("no direct data ingestion scope"));
+ assertFalse(hasDirectIngestScope(""));
+ assertFalse(hasDirectIngestScope(null));
+ assertTrue(hasDirectIngestScope("aoa/*"));
+ assertTrue(hasDirectIngestScope("some aoa/*"));
+ assertTrue(hasDirectIngestScope("aoa:*"));
+ assertTrue(hasDirectIngestScope("some aoa:*"));
+ }
+
+ @Nested
+ class WireMockTests {
+ WireMockServer mockBackend;
+
+ private final String MOCK_RESPONSE = "{\"scope\":\"scope aoa/*\",\"id_token\":null,\"token_type\":\"bearer\",\"expires_in\":1,\"access_token\":\"accessToken\",\"refresh_token\":null}\n";
+ private final String MOCK_RESPONSE2 = "{\"scope\":\"scope aoa/*\",\"id_token\":null,\"token_type\":\"bearer\",\"expires_in\":1,\"access_token\":\"accessToken2\",\"refresh_token\":null}\n";
+
+ @BeforeEach
+ void setup() {
+ mockBackend = new WireMockServer(wireMockConfig().dynamicPort());
+ }
+
+ @AfterEach
+ void teardown() {
+ mockBackend.stop();
+ }
+
+ @Test
+ void testCSPReturnsAccessToken() {
+ mockBackend.stubFor(WireMock.post(urlPathMatching("/csp/gateway/am/api/auth/authorize")).willReturn(WireMock.ok(MOCK_RESPONSE)));
+ mockBackend.start();
+
+ CSPServerToServerTokenService cspServerToServerTokenService = new CSPServerToServerTokenService(mockBackend.baseUrl(), "N/A", "N/A");
+ assertNotNull(cspServerToServerTokenService);
+ assertEquals(cspServerToServerTokenService.getToken(), "accessToken");
+ }
+
+ @Test
+ void testCSPMultipleAccessTokens() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
+ createWireMockStubWithStates("/csp/gateway/am/api/auth/authorize", Scenario.STARTED, "second", MOCK_RESPONSE);
+ createWireMockStubWithStates("/csp/gateway/am/api/auth/authorize", "second", "third", MOCK_RESPONSE2);
+ mockBackend.start();
+
+ CSPServerToServerTokenService cspServerToServerTokenService = new CSPServerToServerTokenService(mockBackend.baseUrl(), "N/A", "N/A");
+
+ Field field = CSPServerToServerTokenService.class.getDeclaredField("DEFAULT_THREAD_DELAY");
+ field.setAccessible(true);
+ field.set(cspServerToServerTokenService, 1);
+
+ assertNotNull(cspServerToServerTokenService);
+ assertEquals(cspServerToServerTokenService.getToken(), "accessToken");
+ Thread.sleep(2000);
+ assertEquals(cspServerToServerTokenService.getToken(), "accessToken2");
+ }
+
+ @Test
+ void testCSPReturns401() {
+ mockBackend.stubFor(WireMock.post(urlPathMatching("/csp/gateway/am/api/auth/authorize")).willReturn(WireMock.unauthorized()));
+ mockBackend.start();
+
+ CSPServerToServerTokenService cspServerToServerTokenService = new CSPServerToServerTokenService(mockBackend.baseUrl(), "N/A", "N/A");
+ assertEquals(cspServerToServerTokenService.getToken(), "INVALID_TOKEN");
+ }
+
+ @Test
+ void testCSPReturns500() {
+ mockBackend.stubFor(WireMock.post(urlPathMatching("/csp/gateway/am/api/auth/authorize")).willReturn(WireMock.serverError()));
+ mockBackend.start();
+
+ CSPServerToServerTokenService cspServerToServerTokenService = new CSPServerToServerTokenService(mockBackend.baseUrl(), "N/A", "N/A");
+ assertNull(cspServerToServerTokenService.getToken());
+ }
+
+ @Test
+ void testCSPConnectionError() {
+ mockBackend.stubFor(WireMock.post(urlPathMatching("/csp/gateway/am/api/auth/authorize")).willReturn(WireMock.serverError()));
+ mockBackend.setGlobalFixedDelay(5_000);
+ mockBackend.start();
+
+ CSPServerToServerTokenService cspServerToServerTokenService = new CSPServerToServerTokenService(mockBackend.baseUrl(), "N/A", "N/A", 100, 100);
+ assertNull(cspServerToServerTokenService.getToken());
+ }
+
+ private void createWireMockStubWithStates(final String url, final String currentState, final String nextState, final String responseBody) {
+ mockBackend.stubFor(post(urlEqualTo(url))
+ .inScenario("csp")
+ .whenScenarioStateIs(currentState)
+ .willSetStateTo(nextState)
+ .willReturn(aResponse()
+ .withStatus(200)
+ .withBody(responseBody)));
+ }
+
+ }
+}
\ No newline at end of file