diff --git a/commercetools/commercetools-monitoring-datadog/src/main/java/com/commercetools/monitoring/datadog/DatadogMiddleware.java b/commercetools/commercetools-monitoring-datadog/src/main/java/com/commercetools/monitoring/datadog/DatadogMiddleware.java index 6c133964b65..a7f8fe8c10b 100644 --- a/commercetools/commercetools-monitoring-datadog/src/main/java/com/commercetools/monitoring/datadog/DatadogMiddleware.java +++ b/commercetools/commercetools-monitoring-datadog/src/main/java/com/commercetools/monitoring/datadog/DatadogMiddleware.java @@ -43,6 +43,10 @@ public DatadogMiddleware(final ApiClient ddApiClient) { this.apiInstance = new MetricsApi(ddApiClient); } + public DatadogMiddleware(final MetricsApi apiInstance) { + this.apiInstance = apiInstance; + } + @Override public CompletableFuture> invoke(ApiHttpRequest request, Function>> next) { diff --git a/commercetools/commercetools-monitoring-datadog/src/main/java/com/commercetools/monitoring/datadog/DatadogResponseSerializer.java b/commercetools/commercetools-monitoring-datadog/src/main/java/com/commercetools/monitoring/datadog/DatadogResponseSerializer.java index d58cbffec64..f53a3576d13 100644 --- a/commercetools/commercetools-monitoring-datadog/src/main/java/com/commercetools/monitoring/datadog/DatadogResponseSerializer.java +++ b/commercetools/commercetools-monitoring-datadog/src/main/java/com/commercetools/monitoring/datadog/DatadogResponseSerializer.java @@ -25,6 +25,11 @@ public class DatadogResponseSerializer implements ResponseSerializer { private final MetricsApi apiInstance; + public DatadogResponseSerializer(final ResponseSerializer serializer, final MetricsApi apiInstance) { + this.serializer = serializer; + this.apiInstance = apiInstance; + } + public DatadogResponseSerializer(final ResponseSerializer serializer, final ApiClient ddApiClient) { this.serializer = serializer; this.apiInstance = new MetricsApi(ddApiClient); diff --git a/commercetools/commercetools-monitoring-datadog/src/test/java/example/ResponseSerializerTest.java b/commercetools/commercetools-monitoring-datadog/src/test/java/example/ResponseSerializerTest.java new file mode 100644 index 00000000000..d2fb8b37914 --- /dev/null +++ b/commercetools/commercetools-monitoring-datadog/src/test/java/example/ResponseSerializerTest.java @@ -0,0 +1,58 @@ + +package example; + +import com.commercetools.api.models.common.Reference; +import com.commercetools.api.models.product.ProductReference; +import com.commercetools.monitoring.datadog.DatadogResponseSerializer; +import com.datadog.api.client.ApiException; +import com.datadog.api.client.v2.api.MetricsApi; +import com.fasterxml.jackson.core.JsonProcessingException; + +import io.vrap.rmf.base.client.ApiHttpHeaders; +import io.vrap.rmf.base.client.ApiHttpResponse; +import io.vrap.rmf.base.client.ResponseSerializer; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class ResponseSerializerTest { + + @Test + public void testSerialize() throws ApiException, JsonProcessingException { + MetricsApi metricsApi = Mockito.mock(MetricsApi.class); + Mockito.when(metricsApi.submitMetrics(Mockito.any())).thenReturn(null); + DatadogResponseSerializer serializer = new DatadogResponseSerializer(ResponseSerializer.of(), metricsApi); + + Reference reference = ProductReference.builder().id("abc").build(); + + serializer.toJsonByteArray(reference); + + Mockito.verify(metricsApi).submitMetrics(Mockito.argThat(metricPayload -> { + Assertions.assertThat(metricPayload).isNotNull(); + Assertions.assertThat(metricPayload.getSeries().get(0).getMetric()) + .isEqualTo("commercetools.json.serialization"); + return true; + })); + } + + @Test + public void testDeserialize() throws ApiException, JsonProcessingException { + MetricsApi metricsApi = Mockito.mock(MetricsApi.class); + Mockito.when(metricsApi.submitMetrics(Mockito.any())).thenReturn(null); + DatadogResponseSerializer serializer = new DatadogResponseSerializer(ResponseSerializer.of(), metricsApi); + + String responseBody = "{ \"typeId\": \"product\", \"id\": \"abc\" }"; + ApiHttpResponse response = new ApiHttpResponse<>(200, new ApiHttpHeaders(), responseBody.getBytes()); + + ApiHttpResponse reference = serializer.convertResponse(response, Reference.class); + + Assertions.assertThat(reference.getBody()).isInstanceOf(ProductReference.class); + Mockito.verify(metricsApi).submitMetrics(Mockito.argThat(metricPayload -> { + Assertions.assertThat(metricPayload).isNotNull(); + Assertions.assertThat(metricPayload.getSeries().get(0).getMetric()) + .isEqualTo("commercetools.json.deserialization"); + return true; + })); + } +} diff --git a/commercetools/commercetools-monitoring-datadog/src/test/java/example/TelemetryMiddlewareTest.java b/commercetools/commercetools-monitoring-datadog/src/test/java/example/TelemetryMiddlewareTest.java new file mode 100644 index 00000000000..0cc96a0038c --- /dev/null +++ b/commercetools/commercetools-monitoring-datadog/src/test/java/example/TelemetryMiddlewareTest.java @@ -0,0 +1,106 @@ + +package example; + +import static io.vrap.rmf.base.client.utils.ClientUtils.blockingWait; + +import java.net.URI; +import java.net.URISyntaxException; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; + +import com.commercetools.monitoring.datadog.DatadogMiddleware; +import com.datadog.api.client.ApiException; +import com.datadog.api.client.v2.api.MetricsApi; +import com.tngtech.junit.dataprovider.DataProvider; +import com.tngtech.junit.dataprovider.DataProviderExtension; +import com.tngtech.junit.dataprovider.UseDataProvider; +import com.tngtech.junit.dataprovider.UseDataProviderExtension; + +import io.vrap.rmf.base.client.*; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; + +@ExtendWith(DataProviderExtension.class) +@ExtendWith(UseDataProviderExtension.class) +public class TelemetryMiddlewareTest { + + @DataProvider + public static Object[][] responses() { + return new Object[][] { { 200, 2 }, { 201, 2 }, { 400, 3 }, { 401, 3 }, { 403, 3 }, { 404, 3 }, { 409, 3 }, + { 499, 3 }, { 500, 3 }, { 502, 3 }, { 503, 3 }, { 504, 3 }, { 599, 3 }, }; + } + + @TestTemplate + @UseDataProvider("responses") + public void testCounts(int statusCode, int count) throws ApiException { + MetricsApi metricsApi = Mockito.mock(MetricsApi.class); + Mockito.when(metricsApi.submitMetrics(Mockito.any())).thenReturn(null); + DatadogMiddleware middleware = new DatadogMiddleware(metricsApi); + + final ApiHttpRequest request = new ApiHttpRequest(ApiHttpMethod.GET, URI.create("/"), new ApiHttpHeaders(), + "".getBytes()); + + blockingWait( + middleware.invoke(request, + request1 -> CompletableFuture + .completedFuture(new ApiHttpResponse<>(statusCode, new ApiHttpHeaders(), "".getBytes()))), + Duration.ofSeconds(1)); + + Mockito.verify(metricsApi, Mockito.times(count)).submitMetrics(Mockito.argThat(metricPayload -> { + Assertions.assertThat(metricPayload).isNotNull(); + Assertions.assertThat(metricPayload.getSeries().get(0).getMetric()) + .isIn("commercetools.client.duration", "commercetools.client.request.total", + "commercetools.client.request.error"); + return true; + })); + } + + @TestTemplate + @UseDataProvider("responses") + public void testHttpCounts(int statusCode, int count) throws URISyntaxException, ApiException { + MetricsApi metricsApi = Mockito.mock(MetricsApi.class); + Mockito.when(metricsApi.submitMetrics(Mockito.any())).thenReturn(null); + DatadogMiddleware middleware = new DatadogMiddleware(metricsApi); + + ApiHttpClient client = ClientBuilder + .of(new TestHttpClient(request -> CompletableFuture + .completedFuture(new ApiHttpResponse<>(statusCode, new ApiHttpHeaders(), "".getBytes())))) + .withApiBaseUrl(new URI("")) + .withTelemetryMiddleware(middleware) + .withErrorMiddleware() + .build(); + + final ApiHttpRequest request = new ApiHttpRequest(ApiHttpMethod.GET, URI.create("/"), new ApiHttpHeaders(), + "".getBytes()); + try { + blockingWait(client.execute(request), Duration.ofSeconds(1)); + } + catch (ApiHttpException ignored) { + } + + Mockito.verify(metricsApi, Mockito.times(count)).submitMetrics(Mockito.argThat(metricPayload -> { + Assertions.assertThat(metricPayload).isNotNull(); + Assertions.assertThat(metricPayload.getSeries().get(0).getMetric()) + .isIn("commercetools.client.duration", "commercetools.client.request.total", + "commercetools.client.request.error"); + return true; + })); + } + + static class TestHttpClient implements VrapHttpClient { + private final Function>> next; + + public TestHttpClient(Function>> next) { + this.next = next; + } + + @Override + public CompletableFuture> execute(ApiHttpRequest request) { + return next.apply(request); + } + } +} diff --git a/rmf/rmf-java-base/src/test/java/io/vrap/rmf/base/client/http/TelemetryMiddlewareTest.java b/rmf/rmf-java-base/src/test/java/io/vrap/rmf/base/client/http/TelemetryMiddlewareTest.java new file mode 100644 index 00000000000..32091423c22 --- /dev/null +++ b/rmf/rmf-java-base/src/test/java/io/vrap/rmf/base/client/http/TelemetryMiddlewareTest.java @@ -0,0 +1,107 @@ + +package io.vrap.rmf.base.client.http; + +import static io.vrap.rmf.base.client.utils.ClientUtils.blockingWait; + +import java.net.URI; +import java.net.URISyntaxException; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; + +import com.tngtech.junit.dataprovider.DataProvider; +import com.tngtech.junit.dataprovider.DataProviderExtension; +import com.tngtech.junit.dataprovider.UseDataProvider; +import com.tngtech.junit.dataprovider.UseDataProviderExtension; + +import io.vrap.rmf.base.client.*; +import io.vrap.rmf.base.client.error.*; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(DataProviderExtension.class) +@ExtendWith(UseDataProviderExtension.class) +public class TelemetryMiddlewareTest { + + @DataProvider + public static Object[][] responses() { + return new Object[][] { { 200, 1, 0 }, { 201, 1, 0 }, { 400, 1, 1 }, { 401, 1, 1 }, { 403, 1, 1 }, + { 404, 1, 1 }, { 409, 1, 1 }, { 499, 1, 1 }, { 500, 1, 1 }, { 502, 1, 1 }, { 503, 1, 1 }, { 504, 1, 1 }, + { 599, 1, 1 }, }; + } + + @TestTemplate + @UseDataProvider("responses") + public void testCounts(int statusCode, int count, int errorCount) { + TestTelemetryMiddleware middleware = new TestTelemetryMiddleware(); + + final ApiHttpRequest request = new ApiHttpRequest(ApiHttpMethod.GET, URI.create("/"), new ApiHttpHeaders(), + "".getBytes()); + + blockingWait( + middleware.invoke(request, + request1 -> CompletableFuture + .completedFuture(new ApiHttpResponse<>(statusCode, new ApiHttpHeaders(), "".getBytes()))), + Duration.ofSeconds(1)); + + Assertions.assertThat(middleware.count).isEqualTo(count); + Assertions.assertThat(middleware.errorCount).isEqualTo(errorCount); + } + + @TestTemplate + @UseDataProvider("responses") + public void testHttpCounts(int statusCode, int count, int errorCount) throws URISyntaxException { + TestTelemetryMiddleware middleware = new TestTelemetryMiddleware(); + + ApiHttpClient client = ClientBuilder + .of(new TestHttpClient(request -> CompletableFuture + .completedFuture(new ApiHttpResponse<>(statusCode, new ApiHttpHeaders(), "".getBytes())))) + .withApiBaseUrl(new URI("")) + .withTelemetryMiddleware(middleware) + .withErrorMiddleware() + .build(); + + final ApiHttpRequest request = new ApiHttpRequest(ApiHttpMethod.GET, URI.create("/"), new ApiHttpHeaders(), + "".getBytes()); + try { + blockingWait(client.execute(request), Duration.ofSeconds(1)); + } + catch (ApiHttpException ignored) { + } + + Assertions.assertThat(middleware.count).isEqualTo(count); + Assertions.assertThat(middleware.errorCount).isEqualTo(errorCount); + } + + static class TestHttpClient implements VrapHttpClient { + private final Function>> next; + + public TestHttpClient(Function>> next) { + this.next = next; + } + + @Override + public CompletableFuture> execute(ApiHttpRequest request) { + return next.apply(request); + } + } + + static class TestTelemetryMiddleware implements TelemetryMiddleware { + + public long count = 0; + public long errorCount = 0; + @Override + public CompletableFuture> invoke(ApiHttpRequest request, + Function>> next) { + return next.apply(request).thenApply(response -> { + count++; + if (response.getStatusCode() >= 400) { + errorCount++; + } + return response; + }); + } + } +}