From 9bb798e5b6ee1d1a61b4f1042b0b92f73c4a5d25 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Mon, 22 May 2023 13:18:13 -0400 Subject: [PATCH 01/14] Add count metrics bt command and flag for error --- .../v1/metrics/ErrorRequestMetricsFilter.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/ErrorRequestMetricsFilter.java diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/ErrorRequestMetricsFilter.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/ErrorRequestMetricsFilter.java new file mode 100644 index 0000000000..457489ebe4 --- /dev/null +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/ErrorRequestMetricsFilter.java @@ -0,0 +1,100 @@ +package io.stargate.sgv2.jsonapi.api.v1.metrics; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; +import io.stargate.sgv2.api.common.config.MetricsConfig; +import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.regex.Pattern; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import org.jboss.resteasy.reactive.server.ServerResponseFilter; + +/** + * The filter for counting HTTP requests per tenant. Controlled by {@link + * MetricsConfig.TenantRequestCounterConfig}. + */ +@ApplicationScoped +public class ErrorRequestMetricsFilter { + + // split pattern for the user agent, extract only first part of the agent + private static final Pattern USER_AGENT_SPLIT = Pattern.compile("[\\s/]"); + + // same as V1 io.stargate.core.metrics.StargateMetricConstants#UNKNOWN + private static final String UNKNOWN_VALUE = "unknown"; + + /** The {@link MeterRegistry} to report to. */ + private final MeterRegistry meterRegistry; + + /** The {@link ObjectMapper} to get command name from request. */ + private final ObjectMapper objectMapper; + + /** The tag for error being true, created only once. */ + private final Tag errorTrue = Tag.of("error", "true"); + + /** Tag that represent the api module. */ + private final Tag apiTag = Tag.of("module", "jsonapi"); + + /** The tag for error being false, created only once. */ + private final Tag errorFalse = Tag.of("error", "false"); + + /** Default constructor. */ + @Inject + public ErrorRequestMetricsFilter(MeterRegistry meterRegistry, ObjectMapper objectMapper) { + this.meterRegistry = meterRegistry; + this.objectMapper = objectMapper; + } + + /** + * Filter that this bean produces. + * + * @param requestContext {@link ContainerRequestContext} + * @param responseContext {@link ContainerResponseContext} + */ + @ServerResponseFilter + public void record( + ContainerRequestContext requestContext, ContainerResponseContext responseContext) { + String commandName = getCommandName(requestContext.getEntityStream()); + // resolve error + boolean error = responseContext.getStatus() != 200 || checkForErrors(responseContext); + Tag errorTag = error ? errorTrue : errorFalse; + Tag commandTag = Tag.of("command", commandName); + Tag statusCodeTag = Tag.of("statusCode", String.valueOf(responseContext.getStatus())); + + Tags tags = Tags.of(apiTag, commandTag, statusCodeTag, errorTag); + // record + meterRegistry.counter("http_server_requests_seconds_custom_count", tags).increment(); + } + + private String getCommandName(InputStream inputStream) { + try { + // reset the stream to fetch from beginning of stream + inputStream.reset(); + // get body from requestContext + final Iterator fieldIterator = objectMapper.readTree(inputStream).fieldNames(); + return fieldIterator.hasNext() ? fieldIterator.next() : UNKNOWN_VALUE; + } catch (IOException e) { + return UNKNOWN_VALUE; + } + } + + /** + * Checks if the response contains an error. + * + * @param responseContext {@link ContainerResponseContext} + * @return true if the response contains an error, false otherwise + */ + public boolean checkForErrors(ContainerResponseContext responseContext) { + Object entity = responseContext.getEntity(); + if (entity instanceof CommandResult commandResult) { + return commandResult.errors() != null && !commandResult.errors().isEmpty(); + } + return false; + } +} From 9d87299d5d50a98f1e3de20bcdcd65fd5d627f86 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Tue, 23 May 2023 11:36:43 -0400 Subject: [PATCH 02/14] Custom metrics for count by command --- .../api/model/command/CommandResult.java | 10 - .../api/security/ErrorChallengeSender.java | 43 ++- .../api/v1/metrics/MetricsConstants.java | 7 + ...sFilter.java => RequestMetricsFilter.java} | 25 +- .../sgv2/jsonapi/api/MetricsTest.java | 263 ++++++++++++++++++ .../jsonapi/api/UnauthorizedMetricsTest.java | 84 ------ .../serializers/ErrorSerializerTest.java | 4 +- 7 files changed, 322 insertions(+), 114 deletions(-) create mode 100644 src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java rename src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/{ErrorRequestMetricsFilter.java => RequestMetricsFilter.java} (83%) create mode 100644 src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java delete mode 100644 src/test/java/io/stargate/sgv2/jsonapi/api/UnauthorizedMetricsTest.java diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandResult.java b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandResult.java index 5bbcb5a0d3..3829925dbd 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandResult.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandResult.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.JsonNode; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -184,15 +183,6 @@ public record Error( "Error fields can not contain the reserved message key."); } } - - /** - * Constructor that sets documents only the message. - * - * @param message Error message. - */ - public Error(String message) { - this(message, Collections.emptyMap(), Response.Status.OK); - } } /** diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java b/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java index ef1a6b3c9c..282409c6d1 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java @@ -2,16 +2,22 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; import io.quarkus.vertx.http.runtime.security.ChallengeData; import io.smallrye.mutiny.Uni; import io.stargate.sgv2.api.common.security.challenge.ChallengeSender; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; +import io.stargate.sgv2.jsonapi.api.v1.metrics.MetricsConstants; import io.vertx.ext.web.RoutingContext; +import java.util.Collections; import java.util.List; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,18 +34,38 @@ public class ErrorChallengeSender implements ChallengeSender { /** Result is always constant */ private final CommandResult commandResult; + /** The {@link MeterRegistry} to report to. */ + private final MeterRegistry meterRegistry; + + /** The tag for error being true, created only once. */ + private final Tag errorTag = Tag.of("error", "true"); + + /** Tag that represent the api module. */ + private final Tag apiTag = Tag.of("module", "jsonapi"); + + /** + * The command name tag for auth token missing. Don't have access to request message body, so + * hardcoded to unknown + */ + private final Tag commandTag = Tag.of("command", MetricsConstants.UNKNOWN_VALUE); + + /** The tag for status code, as only 401 will be returned from here */ + private final Tag statusCodeTag = Tag.of("statusCode", String.valueOf(401)); + @Inject public ErrorChallengeSender( @ConfigProperty(name = "stargate.auth.header-based.header-name", defaultValue = "") String headerName, - ObjectMapper objectMapper) { + ObjectMapper objectMapper, + MeterRegistry meterRegistry) { this.objectMapper = objectMapper; - + this.meterRegistry = meterRegistry; // create the response String message = "Role unauthorized for operation: Missing token, expecting one in the %s header." .formatted(headerName); - CommandResult.Error error = new CommandResult.Error(message); + CommandResult.Error error = + new CommandResult.Error(message, Collections.emptyMap(), Response.Status.UNAUTHORIZED); commandResult = new CommandResult(List.of(error)); } @@ -59,12 +85,17 @@ public Uni apply(RoutingContext context, ChallengeData challengeData) { .headers() .set(HttpHeaders.CONTENT_LENGTH, String.valueOf(response.getBytes().length)); - // always set status to 200 - context.response().setStatusCode(200); + // Return the status code from the challenge data + context.response().setStatusCode(challengeData.status); + + // Add metrics + Tags tags = Tags.of(apiTag, commandTag, statusCodeTag, errorTag); + // record + meterRegistry.counter("http_server_requests_custom_seconds_count", tags).increment(); // write and map to true return Uni.createFrom() - .completionStage(context.response().write(response).map(true).toCompletionStage()); + .completionStage(context.response().write(response).map(false).toCompletionStage()); } catch (JsonProcessingException e) { LOG.error("Unable to serialize CommandResult instance {} to JSON.", commandResult, e); diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java new file mode 100644 index 0000000000..04b1262f9e --- /dev/null +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java @@ -0,0 +1,7 @@ +package io.stargate.sgv2.jsonapi.api.v1.metrics; + +public class MetricsConstants { + + // same as V1 io.stargate.core.metrics.StargateMetricConstants#UNKNOWN + public static final String UNKNOWN_VALUE = "unknown"; +} diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/ErrorRequestMetricsFilter.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/RequestMetricsFilter.java similarity index 83% rename from src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/ErrorRequestMetricsFilter.java rename to src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/RequestMetricsFilter.java index 457489ebe4..064b2acddd 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/ErrorRequestMetricsFilter.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/RequestMetricsFilter.java @@ -4,7 +4,6 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; -import io.stargate.sgv2.api.common.config.MetricsConfig; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; import java.io.IOException; import java.io.InputStream; @@ -16,19 +15,13 @@ import javax.ws.rs.container.ContainerResponseContext; import org.jboss.resteasy.reactive.server.ServerResponseFilter; -/** - * The filter for counting HTTP requests per tenant. Controlled by {@link - * MetricsConfig.TenantRequestCounterConfig}. - */ +/** The filter for counting HTTP requests per command. = */ @ApplicationScoped -public class ErrorRequestMetricsFilter { +public class RequestMetricsFilter { // split pattern for the user agent, extract only first part of the agent private static final Pattern USER_AGENT_SPLIT = Pattern.compile("[\\s/]"); - // same as V1 io.stargate.core.metrics.StargateMetricConstants#UNKNOWN - private static final String UNKNOWN_VALUE = "unknown"; - /** The {@link MeterRegistry} to report to. */ private final MeterRegistry meterRegistry; @@ -46,7 +39,7 @@ public class ErrorRequestMetricsFilter { /** Default constructor. */ @Inject - public ErrorRequestMetricsFilter(MeterRegistry meterRegistry, ObjectMapper objectMapper) { + public RequestMetricsFilter(MeterRegistry meterRegistry, ObjectMapper objectMapper) { this.meterRegistry = meterRegistry; this.objectMapper = objectMapper; } @@ -69,18 +62,24 @@ public void record( Tags tags = Tags.of(apiTag, commandTag, statusCodeTag, errorTag); // record - meterRegistry.counter("http_server_requests_seconds_custom_count", tags).increment(); + meterRegistry.counter("http_server_requests_custom_seconds_count", tags).increment(); } + /** + * Gets the command name from the request. + * + * @param inputStream {@link InputStream} + * @return the command name + */ private String getCommandName(InputStream inputStream) { try { // reset the stream to fetch from beginning of stream inputStream.reset(); // get body from requestContext final Iterator fieldIterator = objectMapper.readTree(inputStream).fieldNames(); - return fieldIterator.hasNext() ? fieldIterator.next() : UNKNOWN_VALUE; + return fieldIterator.hasNext() ? fieldIterator.next() : MetricsConstants.UNKNOWN_VALUE; } catch (IOException e) { - return UNKNOWN_VALUE; + return MetricsConstants.UNKNOWN_VALUE; } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java new file mode 100644 index 0000000000..b38e3c5ff6 --- /dev/null +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java @@ -0,0 +1,263 @@ +package io.stargate.sgv2.jsonapi.api; + +import static io.restassured.RestAssured.given; +import static io.stargate.sgv2.common.IntegrationTestUtils.getAuthToken; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.blankString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.TestProfile; +import io.restassured.http.ContentType; +import io.stargate.sgv2.api.common.config.constants.HttpConstants; +import io.stargate.sgv2.common.testprofiles.NoGlobalResourcesTestProfile; +import io.stargate.sgv2.jsonapi.api.v1.CollectionResource; +import io.stargate.sgv2.jsonapi.api.v1.GeneralResource; +import io.stargate.sgv2.jsonapi.api.v1.NamespaceResource; +import java.util.List; +import org.junit.jupiter.api.Test; + +@QuarkusTest +@TestProfile(NoGlobalResourcesTestProfile.Impl.class) +public class MetricsTest { + + @Test + public void unauthorizedNamespaceResource() { + String json = + """ + { + "createCollection": { + "name": "whatever" + } + } + """; + + // ensure namespace not in tags when no auth token used + given() + .contentType(ContentType.JSON) + .body(json) + .when() + .post(GeneralResource.BASE_PATH) + .then() + .statusCode(401); + + String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); + List httpMetrics = + metrics.lines().filter(line -> line.startsWith("http_server_requests_seconds")).toList(); + assertThat(httpMetrics) + .allSatisfy( + line -> + assertThat(line) + .containsAnyOf( + "uri=\"/v1\"", + "uri=\"/v1/{namespace}\"", + "uri=\"/v1/{namespace}/{collection}\"")); + + httpMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("http_server_requests_custom_seconds_count") + && line.contains("command=\"unknown\"") + && line.contains("error=\"true\"") + && line.contains("statusCode=\"401\"")) + .toList(); + assertThat(httpMetrics).hasSize(1); + } + + @Test + public void invalidCommandNamespaceResourceMetrics() { + String json = """ + { + "createCollection": { + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(NamespaceResource.BASE_PATH, "namespaceName") + .then() + .statusCode(200); + + String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); + List httpMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("http_server_requests_custom_seconds_count") + && line.contains("command=\"createCollection\"") + && line.contains("error=\"true\"") + && line.contains("statusCode=\"200\"")) + .toList(); + assertThat(httpMetrics).hasSize(1); + } + + @Test + public void unauthorizedCollectionResource() { + String json = """ + { + "find": { + } + } + """; + + // ensure namespace not in tags when no auth token used + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, "bad-auth-token") + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, "keyspace", "collection") + .then() + .statusCode(401); + + String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); + List httpMetrics = + metrics.lines().filter(line -> line.startsWith("http_server_requests_seconds")).toList(); + + assertThat(httpMetrics) + .allSatisfy( + line -> + assertThat(line) + .containsAnyOf( + "uri=\"/v1\"", + "uri=\"/v1/{namespace}\"", + "uri=\"/v1/{namespace}/{collection}\"")); + + httpMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("http_server_requests_custom_seconds_count") + && line.contains("command=\"find\"") + && line.contains("error=\"true\"") + && line.contains("statusCode=\"401\"")) + .toList(); + assertThat(httpMetrics).hasSize(1); + } + + @Test + public void invalidCollectionResourceMetrics() { + String json = """ + { + "bad_command": { + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, "namespaceName", "collectionName") + .then() + .statusCode(200); + + String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); + List httpMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("http_server_requests_custom_seconds_count") + && line.contains("command=\"bad_command\"") + && line.contains("error=\"true\"") + && line.contains("statusCode=\"200\"")) + .toList(); + assertThat(httpMetrics) + .allSatisfy( + line -> { + assertThat(line).contains("command=\"bad_command\""); + assertThat(line).contains("statusCode=\"200\""); + assertThat(line).contains("error=\"true\""); + }); + } + + @Test + public void unauthorizedGeneralResource() { + String json = + """ + { + "createNamespace": { + "name": "purchase_database" + } + } + """; + + // ensure namespace not in tags when no auth token used + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, "bad-auth-token") + .contentType(ContentType.JSON) + .body(json) + .when() + .post(GeneralResource.BASE_PATH) + .then() + .statusCode(401); + + String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); + List httpMetrics = + metrics.lines().filter(line -> line.startsWith("http_server_requests_seconds")).toList(); + + assertThat(httpMetrics) + .allSatisfy( + line -> + assertThat(line) + .containsAnyOf( + "uri=\"/v1\"", + "uri=\"/v1/{namespace}\"", + "uri=\"/v1/{namespace}/{collection}\"")); + + httpMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("http_server_requests_custom_seconds_count") + && line.contains("command=\"createNamespace\"") + && line.contains("error=\"true\"") + && line.contains("statusCode=\"401\"")) + .toList(); + assertThat(httpMetrics).hasSize(1); + } + + @Test + public void invalidCommandGeneralResourceMetrics() { + String json = """ + { + "createNamespace": { + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(GeneralResource.BASE_PATH) + .then() + .statusCode(200) + .body("errors[0].message", is(not(blankString()))) + .body("errors[0].exceptionClass", is("ConstraintViolationException")); + + String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); + List httpMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("http_server_requests_custom_seconds_count") + && line.contains("command=\"createNamespace\"") + && line.contains("error=\"true\"") + && line.contains("statusCode=\"200\"")) + .toList(); + assertThat(httpMetrics).hasSize(1); + } +} diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/UnauthorizedMetricsTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/UnauthorizedMetricsTest.java deleted file mode 100644 index 60a9c30ae5..0000000000 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/UnauthorizedMetricsTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.stargate.sgv2.jsonapi.api; - -import static io.restassured.RestAssured.given; -import static org.assertj.core.api.Assertions.assertThat; - -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.TestProfile; -import io.restassured.http.ContentType; -import io.stargate.sgv2.common.testprofiles.NoGlobalResourcesTestProfile; -import io.stargate.sgv2.jsonapi.api.v1.CollectionResource; -import io.stargate.sgv2.jsonapi.api.v1.NamespaceResource; -import java.util.List; -import org.junit.jupiter.api.Test; - -@QuarkusTest -@TestProfile(NoGlobalResourcesTestProfile.Impl.class) -public class UnauthorizedMetricsTest { - - @Test - public void namespaceResource() { - String json = - """ - { - "createCollection": { - "name": "whatever" - } - } - """; - - // ensure namespace not in tags when no auth token used - given() - .contentType(ContentType.JSON) - .body(json) - .when() - .post(NamespaceResource.BASE_PATH, "keyspace") - .then() - .statusCode(200); - - String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); - List httpMetrics = - metrics.lines().filter(line -> line.startsWith("http_server_requests_seconds")).toList(); - - assertThat(httpMetrics) - .allSatisfy( - line -> - assertThat(line) - .containsAnyOf( - "uri=\"/v1\"", - "uri=\"/v1/{namespace}\"", - "uri=\"/v1/{namespace}/{collection}\"")); - } - - @Test - public void collectionResource() { - String json = """ - { - "find": { - } - } - """; - - // ensure namespace not in tags when no auth token used - given() - .contentType(ContentType.JSON) - .body(json) - .when() - .post(CollectionResource.BASE_PATH, "keyspace", "collection") - .then() - .statusCode(200); - - String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); - List httpMetrics = - metrics.lines().filter(line -> line.startsWith("http_server_requests_seconds")).toList(); - - assertThat(httpMetrics) - .allSatisfy( - line -> - assertThat(line) - .containsAnyOf( - "uri=\"/v1\"", - "uri=\"/v1/{namespace}\"", - "uri=\"/v1/{namespace}/{collection}\"")); - } -} diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/model/command/serializers/ErrorSerializerTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/model/command/serializers/ErrorSerializerTest.java index 70b3ca2347..b6d1b38f83 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/model/command/serializers/ErrorSerializerTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/model/command/serializers/ErrorSerializerTest.java @@ -8,6 +8,7 @@ import io.quarkus.test.junit.TestProfile; import io.stargate.sgv2.common.testprofiles.NoGlobalResourcesTestProfile; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; +import java.util.Collections; import java.util.Map; import javax.inject.Inject; import javax.ws.rs.core.Response; @@ -37,7 +38,8 @@ public void happyPath() throws Exception { @Test public void withoutProps() throws Exception { - CommandResult.Error error = new CommandResult.Error("My message."); + CommandResult.Error error = + new CommandResult.Error("My message.", Collections.emptyMap(), Response.Status.OK); String result = objectMapper.writeValueAsString(error); From decd0dcac71cdb13c3adbce065283e27583eccbb Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Tue, 23 May 2023 12:49:59 -0400 Subject: [PATCH 03/14] Change the return flag to true --- .../sgv2/jsonapi/api/security/ErrorChallengeSender.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java b/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java index 282409c6d1..d54d8ba8e5 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java @@ -95,7 +95,7 @@ public Uni apply(RoutingContext context, ChallengeData challengeData) { // write and map to true return Uni.createFrom() - .completionStage(context.response().write(response).map(false).toCompletionStage()); + .completionStage(context.response().write(response).map(true).toCompletionStage()); } catch (JsonProcessingException e) { LOG.error("Unable to serialize CommandResult instance {} to JSON.", commandResult, e); From 9668f43638977225e2ad565c9ba70bd176fd070d Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Tue, 23 May 2023 14:15:11 -0400 Subject: [PATCH 04/14] Fixed the command name --- src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java index b38e3c5ff6..5e88f199fa 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java @@ -109,7 +109,6 @@ public void unauthorizedCollectionResource() { // ensure namespace not in tags when no auth token used given() - .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, "bad-auth-token") .contentType(ContentType.JSON) .body(json) .when() @@ -136,7 +135,7 @@ public void unauthorizedCollectionResource() { .filter( line -> line.startsWith("http_server_requests_custom_seconds_count") - && line.contains("command=\"find\"") + && line.contains("command=\"unknown\"") && line.contains("error=\"true\"") && line.contains("statusCode=\"401\"")) .toList(); @@ -193,7 +192,6 @@ public void unauthorizedGeneralResource() { // ensure namespace not in tags when no auth token used given() - .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, "bad-auth-token") .contentType(ContentType.JSON) .body(json) .when() @@ -220,7 +218,7 @@ public void unauthorizedGeneralResource() { .filter( line -> line.startsWith("http_server_requests_custom_seconds_count") - && line.contains("command=\"createNamespace\"") + && line.contains("command=\"unknown\"") && line.contains("error=\"true\"") && line.contains("statusCode=\"401\"")) .toList(); From bab977e4ac74b320c80b4cb4a10b9251746ed6c7 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Tue, 23 May 2023 15:31:18 -0400 Subject: [PATCH 05/14] IT fix to check for 401 for token missing --- .../sgv2/jsonapi/api/v1/CollectionResourceIntegrationTest.java | 2 +- .../sgv2/jsonapi/api/v1/GeneralResourceIntegrationTest.java | 2 +- .../sgv2/jsonapi/api/v1/NamespaceResourceIntegrationTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResourceIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResourceIntegrationTest.java index ae2b3a30b6..80997e8818 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResourceIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResourceIntegrationTest.java @@ -33,7 +33,7 @@ public void tokenMissing() { .when() .post(CollectionResource.BASE_PATH, namespaceName, collectionName) .then() - .statusCode(200) + .statusCode(401) .body( "errors[0].message", is( diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResourceIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResourceIntegrationTest.java index c52e214163..fd0cb9e12f 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResourceIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResourceIntegrationTest.java @@ -37,7 +37,7 @@ public void tokenMissing() { .when() .post(GeneralResource.BASE_PATH) .then() - .statusCode(200) + .statusCode(401) .body( "errors[0].message", is( diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResourceIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResourceIntegrationTest.java index d6b6ce236d..0f0564c9a2 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResourceIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResourceIntegrationTest.java @@ -30,7 +30,7 @@ public void tokenMissing() { .when() .post(NamespaceResource.BASE_PATH, namespaceName) .then() - .statusCode(200) + .statusCode(401) .body( "errors[0].message", is( From 21315bb7c6742204d407d2490dc137cd258993df Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 24 May 2023 18:10:46 -0400 Subject: [PATCH 06/14] Changes for Metrics to capture from CommandProcessor --- .../api/security/ErrorChallengeSender.java | 31 +--- .../api/v1/metrics/MetricsConstants.java | 26 ++++ .../api/v1/metrics/RequestMetricsFilter.java | 99 ------------ .../service/processor/CommandProcessor.java | 128 ++++++++++++---- .../sgv2/jsonapi/api/MetricsTest.java | 144 ------------------ ...AbstractCollectionIntegrationTestBase.java | 25 +++ .../AbstractNamespaceIntegrationTestBase.java | 25 +++ .../jsonapi/api/v1/CountIntegrationTest.java | 6 + .../v1/CreateCollectionIntegrationTest.java | 6 + .../v1/CreateNamespaceIntegrationTest.java | 6 + .../v1/DeleteCollectionIntegrationTest.java | 6 + .../api/v1/DeleteManyIntegrationTest.java | 6 + .../api/v1/DeleteOneIntegrationTest.java | 6 + .../api/v1/DropNamespaceIntegrationTest.java | 6 + .../v1/FindCollectionsIntegrationTest.java | 6 + .../jsonapi/api/v1/FindIntegrationTest.java | 6 + .../api/v1/FindNamespacesIntegrationTest.java | 6 + .../v1/FindOneAndDeleteIntegrationTest.java | 6 + .../v1/FindOneAndReplaceIntegrationTest.java | 6 + .../v1/FindOneAndUpdateIntegrationTest.java | 6 + .../api/v1/FindOneIntegrationTest.java | 6 + .../jsonapi/api/v1/InsertIntegrationTest.java | 7 + .../api/v1/UpdateManyIntegrationTest.java | 6 + .../api/v1/UpdateOneIntegrationTest.java | 6 + 24 files changed, 279 insertions(+), 302 deletions(-) delete mode 100644 src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/RequestMetricsFilter.java diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java b/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java index d54d8ba8e5..e8a6b94b74 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java @@ -2,14 +2,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Tag; -import io.micrometer.core.instrument.Tags; import io.quarkus.vertx.http.runtime.security.ChallengeData; import io.smallrye.mutiny.Uni; import io.stargate.sgv2.api.common.security.challenge.ChallengeSender; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; -import io.stargate.sgv2.jsonapi.api.v1.metrics.MetricsConstants; import io.vertx.ext.web.RoutingContext; import java.util.Collections; import java.util.List; @@ -34,32 +30,12 @@ public class ErrorChallengeSender implements ChallengeSender { /** Result is always constant */ private final CommandResult commandResult; - /** The {@link MeterRegistry} to report to. */ - private final MeterRegistry meterRegistry; - - /** The tag for error being true, created only once. */ - private final Tag errorTag = Tag.of("error", "true"); - - /** Tag that represent the api module. */ - private final Tag apiTag = Tag.of("module", "jsonapi"); - - /** - * The command name tag for auth token missing. Don't have access to request message body, so - * hardcoded to unknown - */ - private final Tag commandTag = Tag.of("command", MetricsConstants.UNKNOWN_VALUE); - - /** The tag for status code, as only 401 will be returned from here */ - private final Tag statusCodeTag = Tag.of("statusCode", String.valueOf(401)); - @Inject public ErrorChallengeSender( @ConfigProperty(name = "stargate.auth.header-based.header-name", defaultValue = "") String headerName, - ObjectMapper objectMapper, - MeterRegistry meterRegistry) { + ObjectMapper objectMapper) { this.objectMapper = objectMapper; - this.meterRegistry = meterRegistry; // create the response String message = "Role unauthorized for operation: Missing token, expecting one in the %s header." @@ -88,11 +64,6 @@ public Uni apply(RoutingContext context, ChallengeData challengeData) { // Return the status code from the challenge data context.response().setStatusCode(challengeData.status); - // Add metrics - Tags tags = Tags.of(apiTag, commandTag, statusCodeTag, errorTag); - // record - meterRegistry.counter("http_server_requests_custom_seconds_count", tags).increment(); - // write and map to true return Uni.createFrom() .completionStage(context.response().write(response).map(true).toCompletionStage()); diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java index 04b1262f9e..1543985eb0 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java @@ -1,7 +1,33 @@ package io.stargate.sgv2.jsonapi.api.v1.metrics; +import io.micrometer.core.instrument.Tag; + public class MetricsConstants { // same as V1 io.stargate.core.metrics.StargateMetricConstants#UNKNOWN public static final String UNKNOWN_VALUE = "unknown"; + + public static final Tag ERROR_TRUE_TAG = Tag.of("error", "true"); + + public static final Tag ERROR_FALSE_TAG = Tag.of("error", "false"); + + public static final Tag MODULE_TAG = Tag.of("module", "jsonapi"); + + public static final String ERROR_CLASS = "errorClass"; + + public static final String ERROR_CODE = "errorCode"; + + public static final String TENANT = "tenant"; + + public static final String COMMAND = "command"; + + public static final String NA = "NA"; + + public static final Tag DEFAULT_ERROR_CLASS_TAG = Tag.of(ERROR_CLASS, NA); + + public static final Tag DEFAULT_ERROR_CODE_TAG = Tag.of(ERROR_CODE, NA); + + public static final String COUNT_METRICS_NAME = "command_processor_count"; + + public static final String TIMER_METRICS_NAME = "command_processor_total"; } diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/RequestMetricsFilter.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/RequestMetricsFilter.java deleted file mode 100644 index 064b2acddd..0000000000 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/RequestMetricsFilter.java +++ /dev/null @@ -1,99 +0,0 @@ -package io.stargate.sgv2.jsonapi.api.v1.metrics; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Tag; -import io.micrometer.core.instrument.Tags; -import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; -import java.io.IOException; -import java.io.InputStream; -import java.util.Iterator; -import java.util.regex.Pattern; -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerResponseContext; -import org.jboss.resteasy.reactive.server.ServerResponseFilter; - -/** The filter for counting HTTP requests per command. = */ -@ApplicationScoped -public class RequestMetricsFilter { - - // split pattern for the user agent, extract only first part of the agent - private static final Pattern USER_AGENT_SPLIT = Pattern.compile("[\\s/]"); - - /** The {@link MeterRegistry} to report to. */ - private final MeterRegistry meterRegistry; - - /** The {@link ObjectMapper} to get command name from request. */ - private final ObjectMapper objectMapper; - - /** The tag for error being true, created only once. */ - private final Tag errorTrue = Tag.of("error", "true"); - - /** Tag that represent the api module. */ - private final Tag apiTag = Tag.of("module", "jsonapi"); - - /** The tag for error being false, created only once. */ - private final Tag errorFalse = Tag.of("error", "false"); - - /** Default constructor. */ - @Inject - public RequestMetricsFilter(MeterRegistry meterRegistry, ObjectMapper objectMapper) { - this.meterRegistry = meterRegistry; - this.objectMapper = objectMapper; - } - - /** - * Filter that this bean produces. - * - * @param requestContext {@link ContainerRequestContext} - * @param responseContext {@link ContainerResponseContext} - */ - @ServerResponseFilter - public void record( - ContainerRequestContext requestContext, ContainerResponseContext responseContext) { - String commandName = getCommandName(requestContext.getEntityStream()); - // resolve error - boolean error = responseContext.getStatus() != 200 || checkForErrors(responseContext); - Tag errorTag = error ? errorTrue : errorFalse; - Tag commandTag = Tag.of("command", commandName); - Tag statusCodeTag = Tag.of("statusCode", String.valueOf(responseContext.getStatus())); - - Tags tags = Tags.of(apiTag, commandTag, statusCodeTag, errorTag); - // record - meterRegistry.counter("http_server_requests_custom_seconds_count", tags).increment(); - } - - /** - * Gets the command name from the request. - * - * @param inputStream {@link InputStream} - * @return the command name - */ - private String getCommandName(InputStream inputStream) { - try { - // reset the stream to fetch from beginning of stream - inputStream.reset(); - // get body from requestContext - final Iterator fieldIterator = objectMapper.readTree(inputStream).fieldNames(); - return fieldIterator.hasNext() ? fieldIterator.next() : MetricsConstants.UNKNOWN_VALUE; - } catch (IOException e) { - return MetricsConstants.UNKNOWN_VALUE; - } - } - - /** - * Checks if the response contains an error. - * - * @param responseContext {@link ContainerResponseContext} - * @return true if the response contains an error, false otherwise - */ - public boolean checkForErrors(ContainerResponseContext responseContext) { - Object entity = responseContext.getEntity(); - if (entity instanceof CommandResult commandResult) { - return commandResult.errors() != null && !commandResult.errors().isEmpty(); - } - return false; - } -} diff --git a/src/main/java/io/stargate/sgv2/jsonapi/service/processor/CommandProcessor.java b/src/main/java/io/stargate/sgv2/jsonapi/service/processor/CommandProcessor.java index 2227fdb093..95c82922c9 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/service/processor/CommandProcessor.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/service/processor/CommandProcessor.java @@ -1,9 +1,14 @@ package io.stargate.sgv2.jsonapi.service.processor; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; import io.smallrye.mutiny.Uni; +import io.stargate.sgv2.api.common.StargateRequestInfo; import io.stargate.sgv2.jsonapi.api.model.command.Command; import io.stargate.sgv2.jsonapi.api.model.command.CommandContext; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; +import io.stargate.sgv2.jsonapi.api.v1.metrics.MetricsConstants; import io.stargate.sgv2.jsonapi.exception.JsonApiException; import io.stargate.sgv2.jsonapi.exception.mappers.ThrowableCommandResultSupplier; import io.stargate.sgv2.jsonapi.service.bridge.executor.QueryExecutor; @@ -25,15 +30,24 @@ @ApplicationScoped public class CommandProcessor { + private final MeterRegistry meterRegistry; + private final QueryExecutor queryExecutor; private final CommandResolverService commandResolverService; + private final StargateRequestInfo stargateRequestInfo; + @Inject public CommandProcessor( - QueryExecutor queryExecutor, CommandResolverService commandResolverService) { + MeterRegistry meterRegistry, + QueryExecutor queryExecutor, + CommandResolverService commandResolverService, + StargateRequestInfo stargateRequestInfo) { + this.meterRegistry = meterRegistry; this.queryExecutor = queryExecutor; this.commandResolverService = commandResolverService; + this.stargateRequestInfo = stargateRequestInfo; } /** @@ -46,36 +60,92 @@ public CommandProcessor( */ public Uni processCommand( CommandContext commandContext, T command) { - // start by resolving the command, get resolver - return commandResolverService - .resolverForCommand(command) - - // resolver can be null, not handled in CommandResolverService for now - .flatMap( - resolver -> { - // if we have resolver, resolve operation and execute - Operation operation = resolver.resolveCommand(commandContext, command); - return operation.execute(queryExecutor); - }) + final Tag commandTag = Tag.of(MetricsConstants.COMMAND, command.getClass().getSimpleName()); + final String tenant = + stargateRequestInfo != null && stargateRequestInfo.getTenantId().isPresent() + ? stargateRequestInfo.getTenantId().get() + : MetricsConstants.UNKNOWN_VALUE; + final Tag tenantTag = Tag.of(MetricsConstants.TENANT, tenant); + final long start = System.currentTimeMillis(); + return Uni.createFrom() + .item(start) + .onItem() + .transformToUni( + startTime -> { + // start by resolving the command, get resolver + return commandResolverService + .resolverForCommand(command) - // handler failures here - .onFailure() - .recoverWithItem( - t -> { - // DocsException is supplier of the CommandResult - // so simply return - if (t instanceof JsonApiException jsonApiException) { - return jsonApiException; - } + // resolver can be null, not handled in CommandResolverService for now + .flatMap( + resolver -> { + // if we have resolver, resolve operation and execute + Operation operation = resolver.resolveCommand(commandContext, command); + return operation.execute(queryExecutor); + }) - // otherwise use generic for now - return new ThrowableCommandResultSupplier(t); - }) + // handler failures here + .onItemOrFailure() + .transform( + (item, failure) -> { + if (failure != null) { + if (failure instanceof JsonApiException jsonApiException) { + return jsonApiException; + } + return new ThrowableCommandResultSupplier(failure); - // if we have a non-null item - // call supplier get to map to the command result - .onItem() - .ifNotNull() - .transform(Supplier::get); + } else { + // return the tags and command result + return item; + } + }) + .onItem() + .ifNotNull() + .transform(Supplier::get) + .onItem() + .transform( + result -> { + Tag errorTag = MetricsConstants.ERROR_FALSE_TAG; + ; + Tag errorClassTag = MetricsConstants.DEFAULT_ERROR_CLASS_TAG; + Tag errorCodeTag = MetricsConstants.DEFAULT_ERROR_CODE_TAG; + if (null != result.errors() && !result.errors().isEmpty()) { + errorTag = MetricsConstants.ERROR_TRUE_TAG; + String errorClass = + (String) + result + .errors() + .get(0) + .fields() + .getOrDefault( + MetricsConstants.ERROR_CLASS, + MetricsConstants.UNKNOWN_VALUE); + errorClassTag = Tag.of(MetricsConstants.ERROR_CLASS, errorClass); + String errorCode = + (String) + result + .errors() + .get(0) + .fields() + .getOrDefault( + MetricsConstants.ERROR_CODE, + MetricsConstants.UNKNOWN_VALUE); + errorCodeTag = Tag.of(MetricsConstants.ERROR_CODE, errorCode); + } + Tags tags = + Tags.of(commandTag, tenantTag, errorTag, errorClassTag, errorCodeTag); + // add metrics + meterRegistry + .counter(MetricsConstants.COUNT_METRICS_NAME, tags) + .increment(); + meterRegistry + .timer(MetricsConstants.TIMER_METRICS_NAME, tags) + .record( + System.currentTimeMillis() - start, + java.util.concurrent.TimeUnit.MILLISECONDS); + // return the command result + return result; + }); + }); } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java index 5e88f199fa..3357e4cabf 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/MetricsTest.java @@ -1,20 +1,14 @@ package io.stargate.sgv2.jsonapi.api; import static io.restassured.RestAssured.given; -import static io.stargate.sgv2.common.IntegrationTestUtils.getAuthToken; import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.blankString; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.TestProfile; import io.restassured.http.ContentType; -import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.common.testprofiles.NoGlobalResourcesTestProfile; import io.stargate.sgv2.jsonapi.api.v1.CollectionResource; import io.stargate.sgv2.jsonapi.api.v1.GeneralResource; -import io.stargate.sgv2.jsonapi.api.v1.NamespaceResource; import java.util.List; import org.junit.jupiter.api.Test; @@ -53,49 +47,6 @@ public void unauthorizedNamespaceResource() { "uri=\"/v1\"", "uri=\"/v1/{namespace}\"", "uri=\"/v1/{namespace}/{collection}\"")); - - httpMetrics = - metrics - .lines() - .filter( - line -> - line.startsWith("http_server_requests_custom_seconds_count") - && line.contains("command=\"unknown\"") - && line.contains("error=\"true\"") - && line.contains("statusCode=\"401\"")) - .toList(); - assertThat(httpMetrics).hasSize(1); - } - - @Test - public void invalidCommandNamespaceResourceMetrics() { - String json = """ - { - "createCollection": { - } - } - """; - given() - .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) - .contentType(ContentType.JSON) - .body(json) - .when() - .post(NamespaceResource.BASE_PATH, "namespaceName") - .then() - .statusCode(200); - - String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); - List httpMetrics = - metrics - .lines() - .filter( - line -> - line.startsWith("http_server_requests_custom_seconds_count") - && line.contains("command=\"createCollection\"") - && line.contains("error=\"true\"") - && line.contains("statusCode=\"200\"")) - .toList(); - assertThat(httpMetrics).hasSize(1); } @Test @@ -128,55 +79,6 @@ public void unauthorizedCollectionResource() { "uri=\"/v1\"", "uri=\"/v1/{namespace}\"", "uri=\"/v1/{namespace}/{collection}\"")); - - httpMetrics = - metrics - .lines() - .filter( - line -> - line.startsWith("http_server_requests_custom_seconds_count") - && line.contains("command=\"unknown\"") - && line.contains("error=\"true\"") - && line.contains("statusCode=\"401\"")) - .toList(); - assertThat(httpMetrics).hasSize(1); - } - - @Test - public void invalidCollectionResourceMetrics() { - String json = """ - { - "bad_command": { - } - } - """; - given() - .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) - .contentType(ContentType.JSON) - .body(json) - .when() - .post(CollectionResource.BASE_PATH, "namespaceName", "collectionName") - .then() - .statusCode(200); - - String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); - List httpMetrics = - metrics - .lines() - .filter( - line -> - line.startsWith("http_server_requests_custom_seconds_count") - && line.contains("command=\"bad_command\"") - && line.contains("error=\"true\"") - && line.contains("statusCode=\"200\"")) - .toList(); - assertThat(httpMetrics) - .allSatisfy( - line -> { - assertThat(line).contains("command=\"bad_command\""); - assertThat(line).contains("statusCode=\"200\""); - assertThat(line).contains("error=\"true\""); - }); } @Test @@ -211,51 +113,5 @@ public void unauthorizedGeneralResource() { "uri=\"/v1\"", "uri=\"/v1/{namespace}\"", "uri=\"/v1/{namespace}/{collection}\"")); - - httpMetrics = - metrics - .lines() - .filter( - line -> - line.startsWith("http_server_requests_custom_seconds_count") - && line.contains("command=\"unknown\"") - && line.contains("error=\"true\"") - && line.contains("statusCode=\"401\"")) - .toList(); - assertThat(httpMetrics).hasSize(1); - } - - @Test - public void invalidCommandGeneralResourceMetrics() { - String json = """ - { - "createNamespace": { - } - } - """; - - given() - .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) - .contentType(ContentType.JSON) - .body(json) - .when() - .post(GeneralResource.BASE_PATH) - .then() - .statusCode(200) - .body("errors[0].message", is(not(blankString()))) - .body("errors[0].exceptionClass", is("ConstraintViolationException")); - - String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); - List httpMetrics = - metrics - .lines() - .filter( - line -> - line.startsWith("http_server_requests_custom_seconds_count") - && line.contains("command=\"createNamespace\"") - && line.contains("error=\"true\"") - && line.contains("statusCode=\"200\"")) - .toList(); - assertThat(httpMetrics).hasSize(1); } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractCollectionIntegrationTestBase.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractCollectionIntegrationTestBase.java index 962df34003..a4c206a846 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractCollectionIntegrationTestBase.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractCollectionIntegrationTestBase.java @@ -2,6 +2,7 @@ import static io.restassured.RestAssured.given; import static io.stargate.sgv2.common.IntegrationTestUtils.getAuthToken; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @@ -9,6 +10,7 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; +import java.util.List; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.BeforeAll; @@ -102,4 +104,27 @@ protected void insertDoc(String docJson) { .body("status.insertedIds[0]", not(emptyString())) .statusCode(200); } + + public void checkMetrics(String commandName) { + String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); + List countMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("command_processor_count") + && line.contains("command=\"" + commandName + "\"")) + .toList(); + assertThat(countMetrics.size()).isGreaterThan(0); + + List totalMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("command_processor_total") + && line.contains("command=\"" + commandName + "\"")) + .toList(); + assertThat(totalMetrics.size()).isGreaterThan(0); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java index 945fed48c6..5cc56dc29c 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java @@ -2,12 +2,14 @@ import static io.restassured.RestAssured.given; import static io.stargate.sgv2.common.IntegrationTestUtils.getAuthToken; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; +import java.util.List; import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.microprofile.config.ConfigProvider; import org.junit.jupiter.api.AfterAll; @@ -91,4 +93,27 @@ protected int getTestPort() { return Integer.parseInt(System.getProperty("quarkus.http.test-port")); } } + + public void checkMetrics(String commandName) { + String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); + List countMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("command_processor_count") + && line.contains("command=\"" + commandName + "\"")) + .toList(); + assertThat(countMetrics.size()).isGreaterThan(0); + + List totalMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("command_processor_total") + && line.contains("command=\"" + commandName + "\"")) + .toList(); + assertThat(totalMetrics.size()).isGreaterThan(0); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CountIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CountIntegrationTest.java index c926a1005f..a829a3f198 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CountIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CountIntegrationTest.java @@ -11,6 +11,7 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Order; @@ -708,4 +709,9 @@ public void byBooleanColumn() { .body("errors", is(nullValue())); } } + + @AfterAll + public void checkMetrics() { + checkMetrics("CountDocumentsCommands"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateCollectionIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateCollectionIntegrationTest.java index 118cea60f5..7e05e6a24a 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateCollectionIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateCollectionIntegrationTest.java @@ -10,6 +10,7 @@ import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import org.apache.commons.lang3.RandomStringUtils; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -43,4 +44,9 @@ public void happyPath() { .body("status.ok", is(1)); } } + + @AfterAll + public void checkMetrics() { + checkMetrics("CreateCollectionCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateNamespaceIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateNamespaceIntegrationTest.java index ab71336a2d..bc44613e38 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateNamespaceIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateNamespaceIntegrationTest.java @@ -12,6 +12,7 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Nested; @@ -151,4 +152,9 @@ public void invalidCommand() { .body("errors[0].exceptionClass", is("ConstraintViolationException")); } } + + @AfterAll + public void checkMetrics() { + checkMetrics("CreateNamespaceCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteCollectionIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteCollectionIntegrationTest.java index c763c17582..5697f2a2d0 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteCollectionIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteCollectionIntegrationTest.java @@ -12,6 +12,7 @@ import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import org.apache.commons.lang3.RandomStringUtils; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -117,4 +118,9 @@ public void invalidCommand() { .body("errors[0].exceptionClass", is("ConstraintViolationException")); } } + + @AfterAll + public void checkMetrics() { + checkMetrics("DeleteCollectionCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java index 85a67bfd9d..8efc1aaa5e 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java @@ -20,6 +20,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.RepeatedTest; @@ -400,4 +401,9 @@ public void concurrentDeletes() throws Exception { public void cleanUpData() { deleteAllDocuments(); } + + @AfterAll + public void checkMetrics() { + checkMetrics("DeleteManyCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java index 06e159a194..9ee4e2e7e3 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java @@ -18,6 +18,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; @@ -516,4 +517,9 @@ public void concurrentDeletes() throws Exception { .body("data.documents", is(empty())); } } + + @AfterAll + public void checkMetrics() { + checkMetrics("DeleteOneCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DropNamespaceIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DropNamespaceIntegrationTest.java index 80adf78fa4..aa66ad7b5b 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DropNamespaceIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DropNamespaceIntegrationTest.java @@ -12,6 +12,7 @@ import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import org.apache.commons.lang3.RandomStringUtils; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -169,4 +170,9 @@ public final void notExisting() { .body("status.ok", is(1)); } } + + @AfterAll + public void checkMetrics() { + checkMetrics("DropNamespaceCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionsIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionsIntegrationTest.java index 718a5da789..0a9c113c80 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionsIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionsIntegrationTest.java @@ -12,6 +12,7 @@ import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import org.apache.commons.lang3.RandomStringUtils; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -179,4 +180,9 @@ public void notExistingNamespace() { is("Unknown namespace should_not_be_there, you must create it first.")); } } + + @AfterAll + public void checkMetrics() { + checkMetrics("FindCollectionsCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java index bbe873f1a8..970b451de4 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java @@ -15,6 +15,7 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Order; @@ -1028,4 +1029,9 @@ public void byDateColumn() { .body("data.documents[0]", jsonEquals(expected)); } } + + @AfterAll + public void checkMetrics() { + checkMetrics("FindCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java index 550bdf5ccd..35ae841a44 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java @@ -11,6 +11,7 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -43,4 +44,9 @@ public final void happyPath() { .body("status.namespaces", hasItem(namespaceName)); } } + + @AfterAll + public void checkMetrics() { + checkMetrics("FindNamespaceCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndDeleteIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndDeleteIntegrationTest.java index 19e49932a1..f2504bcd12 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndDeleteIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndDeleteIntegrationTest.java @@ -12,6 +12,7 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -307,4 +308,9 @@ public void withSortProjection() { public void cleanUpData() { deleteAllDocuments(); } + + @AfterAll + public void checkMetrics() { + checkMetrics("FindOneAndDeleteCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndReplaceIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndReplaceIntegrationTest.java index a218d57dea..9a28ad7daa 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndReplaceIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndReplaceIntegrationTest.java @@ -13,6 +13,7 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -622,4 +623,9 @@ public void byIdProjectionBefore() { public void cleanUpData() { deleteAllDocuments(); } + + @AfterAll + public void checkMetrics() { + checkMetrics("FindOneAndReplaceCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndUpdateIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndUpdateIntegrationTest.java index f0b2b75b44..5259af5946 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndUpdateIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndUpdateIntegrationTest.java @@ -18,6 +18,7 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -1781,4 +1782,9 @@ public void tryCurrentDateWithInvalidArg() { public void cleanUpData() { deleteAllDocuments(); } + + @AfterAll + public void checkMetrics() { + checkMetrics("FindOneAndUpdateCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java index 479fbc5bcd..c88bf7dc7d 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java @@ -15,6 +15,7 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Order; @@ -730,4 +731,9 @@ public void withSizeOperatorNotNumber() { .body("errors[1].errorCode", is("INVALID_FILTER_EXPRESSION")); } } + + @AfterAll + public void checkMetrics() { + checkMetrics("FindOneCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/InsertIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/InsertIntegrationTest.java index 12c145c51e..e103e15f90 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/InsertIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/InsertIntegrationTest.java @@ -22,6 +22,7 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -922,4 +923,10 @@ public void emptyDocuments() { .body("errors", is(nullValue())); } } + + @AfterAll + public void checkMetrics() { + checkMetrics("InsertOneCommand"); + checkMetrics("InsertManyCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateManyIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateManyIntegrationTest.java index aabc1ad725..a36ba1de00 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateManyIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateManyIntegrationTest.java @@ -15,6 +15,7 @@ import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReferenceArray; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.RepeatedTest; @@ -675,4 +676,9 @@ public void invalidCommand() { public void cleanUpData() { deleteAllDocuments(); } + + @AfterAll + public void checkMetrics() { + checkMetrics("UpdateManyCommand"); + } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateOneIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateOneIntegrationTest.java index f1a4f55af0..4800aa3be5 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateOneIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateOneIntegrationTest.java @@ -14,6 +14,7 @@ import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReferenceArray; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.RepeatedTest; @@ -2065,4 +2066,9 @@ public void invalidSetAndUnsetPathConflict() { public void cleanUpData() { deleteAllDocuments(); } + + @AfterAll + public void checkMetrics() { + checkMetrics("UpdateOneCommand"); + } } From 627f7f53bfe31a72122ea92dd88cc373d3af15e9 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 24 May 2023 19:26:16 -0400 Subject: [PATCH 07/14] IT fix --- ...AbstractCollectionIntegrationTestBase.java | 25 ----------------- .../AbstractNamespaceIntegrationTestBase.java | 2 +- .../jsonapi/api/v1/CountIntegrationTest.java | 15 +++++++--- .../v1/CreateCollectionIntegrationTest.java | 16 ++++++++--- .../v1/CreateNamespaceIntegrationTest.java | 15 +++++++--- .../v1/DeleteCollectionIntegrationTest.java | 16 ++++++++--- .../api/v1/DeleteManyIntegrationTest.java | 16 ++++++++--- .../api/v1/DeleteOneIntegrationTest.java | 16 ++++++++--- .../api/v1/DropNamespaceIntegrationTest.java | 16 ++++++++--- .../v1/FindCollectionsIntegrationTest.java | 16 ++++++++--- .../jsonapi/api/v1/FindIntegrationTest.java | 15 +++++++--- .../api/v1/FindNamespacesIntegrationTest.java | 16 ++++++++--- .../v1/FindOneAndDeleteIntegrationTest.java | 16 ++++++++--- .../v1/FindOneAndReplaceIntegrationTest.java | 17 ++++++++--- .../v1/FindOneAndUpdateIntegrationTest.java | 22 ++++++++++++--- .../api/v1/FindOneIntegrationTest.java | 15 +++++++--- .../jsonapi/api/v1/InsertIntegrationTest.java | 19 +++++++++---- .../api/v1/UpdateManyIntegrationTest.java | 18 +++++++++--- .../api/v1/UpdateOneIntegrationTest.java | 28 ++++++++++++++++--- 19 files changed, 224 insertions(+), 95 deletions(-) diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractCollectionIntegrationTestBase.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractCollectionIntegrationTestBase.java index a4c206a846..962df34003 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractCollectionIntegrationTestBase.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractCollectionIntegrationTestBase.java @@ -2,7 +2,6 @@ import static io.restassured.RestAssured.given; import static io.stargate.sgv2.common.IntegrationTestUtils.getAuthToken; -import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @@ -10,7 +9,6 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; -import java.util.List; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.BeforeAll; @@ -104,27 +102,4 @@ protected void insertDoc(String docJson) { .body("status.insertedIds[0]", not(emptyString())) .statusCode(200); } - - public void checkMetrics(String commandName) { - String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); - List countMetrics = - metrics - .lines() - .filter( - line -> - line.startsWith("command_processor_count") - && line.contains("command=\"" + commandName + "\"")) - .toList(); - assertThat(countMetrics.size()).isGreaterThan(0); - - List totalMetrics = - metrics - .lines() - .filter( - line -> - line.startsWith("command_processor_total") - && line.contains("command=\"" + commandName + "\"")) - .toList(); - assertThat(totalMetrics.size()).isGreaterThan(0); - } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java index 5cc56dc29c..b24e1a1392 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java @@ -94,7 +94,7 @@ protected int getTestPort() { } } - public void checkMetrics(String commandName) { + public static void checkMetrics(String commandName) { String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); List countMetrics = metrics diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CountIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CountIntegrationTest.java index a829a3f198..7d5c5d6c72 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CountIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CountIntegrationTest.java @@ -11,18 +11,21 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; import org.junit.jupiter.api.TestMethodOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) public class CountIntegrationTest extends AbstractCollectionIntegrationTestBase { @Nested @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @Order(1) class Count { @Test @@ -710,8 +713,12 @@ public void byBooleanColumn() { } } - @AfterAll - public void checkMetrics() { - checkMetrics("CountDocumentsCommands"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + CountIntegrationTest.super.checkMetrics("CountDocumentsCommands"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateCollectionIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateCollectionIntegrationTest.java index 7e05e6a24a..f028121db3 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateCollectionIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateCollectionIntegrationTest.java @@ -10,15 +10,19 @@ import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import org.apache.commons.lang3.RandomStringUtils; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) class CreateCollectionIntegrationTest extends AbstractNamespaceIntegrationTestBase { @Nested + @Order(1) class CreateCollection { @Test @@ -45,8 +49,12 @@ public void happyPath() { } } - @AfterAll - public void checkMetrics() { - checkMetrics("CreateCollectionCommand"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + CreateCollectionIntegrationTest.super.checkMetrics("CreateCollectionCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateNamespaceIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateNamespaceIntegrationTest.java index bc44613e38..84bc6e4a98 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateNamespaceIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CreateNamespaceIntegrationTest.java @@ -12,14 +12,17 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) class CreateNamespaceIntegrationTest extends AbstractNamespaceIntegrationTestBase { private static final String DB_NAME = "stargate"; @@ -153,8 +156,12 @@ public void invalidCommand() { } } - @AfterAll - public void checkMetrics() { - checkMetrics("CreateNamespaceCommand"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + CreateNamespaceIntegrationTest.super.checkMetrics("CreateNamespaceCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteCollectionIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteCollectionIntegrationTest.java index 5697f2a2d0..70122f7ca2 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteCollectionIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteCollectionIntegrationTest.java @@ -12,15 +12,19 @@ import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import org.apache.commons.lang3.RandomStringUtils; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) class DeleteCollectionIntegrationTest extends AbstractNamespaceIntegrationTestBase { @Nested + @Order(1) class DeleteCollection { @Test @@ -119,8 +123,12 @@ public void invalidCommand() { } } - @AfterAll - public void checkMetrics() { - checkMetrics("DeleteCollectionCommand"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + DeleteCollectionIntegrationTest.super.checkMetrics("DeleteCollectionCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java index 8efc1aaa5e..54ce1c8088 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java @@ -20,16 +20,20 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) public class DeleteManyIntegrationTest extends AbstractCollectionIntegrationTestBase { @Nested + @Order(1) class DeleteMany { private void insert(int countOfDocument) { @@ -402,8 +406,12 @@ public void cleanUpData() { deleteAllDocuments(); } - @AfterAll - public void checkMetrics() { - checkMetrics("DeleteManyCommand"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + DeleteManyIntegrationTest.super.checkMetrics("DeleteManyCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java index 9ee4e2e7e3..75d953aed9 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java @@ -18,15 +18,19 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) public class DeleteOneIntegrationTest extends AbstractCollectionIntegrationTestBase { @Nested + @Order(1) class DeleteOne { @Test public void byId() { @@ -518,8 +522,12 @@ public void concurrentDeletes() throws Exception { } } - @AfterAll - public void checkMetrics() { - checkMetrics("DeleteOneCommand"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + DeleteOneIntegrationTest.super.checkMetrics("DeleteOneCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DropNamespaceIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DropNamespaceIntegrationTest.java index aa66ad7b5b..07f56d6b98 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DropNamespaceIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DropNamespaceIntegrationTest.java @@ -12,15 +12,19 @@ import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import org.apache.commons.lang3.RandomStringUtils; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) class DropNamespaceIntegrationTest extends AbstractNamespaceIntegrationTestBase { @Nested + @Order(1) class DropNamespace { @Test @@ -171,8 +175,12 @@ public final void notExisting() { } } - @AfterAll - public void checkMetrics() { - checkMetrics("DropNamespaceCommand"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + DropNamespaceIntegrationTest.super.checkMetrics("DropNamespaceCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionsIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionsIntegrationTest.java index 0a9c113c80..ca8838815c 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionsIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionsIntegrationTest.java @@ -12,15 +12,19 @@ import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import org.apache.commons.lang3.RandomStringUtils; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) class FindCollectionsIntegrationTest extends AbstractNamespaceIntegrationTestBase { @Nested + @Order(1) class FindCollections { @Test @@ -181,8 +185,12 @@ public void notExistingNamespace() { } } - @AfterAll - public void checkMetrics() { - checkMetrics("FindCollectionsCommand"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + FindCollectionsIntegrationTest.super.checkMetrics("FindCollectionsCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java index 970b451de4..1ebe743ea1 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java @@ -15,15 +15,17 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; import org.junit.jupiter.api.TestMethodOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) public class FindIntegrationTest extends AbstractCollectionIntegrationTestBase { // TODO refactor in https://github.com/stargate/jsonapi/issues/174 @@ -33,6 +35,7 @@ public class FindIntegrationTest extends AbstractCollectionIntegrationTestBase { @Nested @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @Order(1) class Find { @Test @Order(1) @@ -1030,8 +1033,12 @@ public void byDateColumn() { } } - @AfterAll - public void checkMetrics() { - checkMetrics("FindCommand"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + FindIntegrationTest.super.checkMetrics("FindCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java index 35ae841a44..4b24e74de3 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java @@ -11,15 +11,19 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) class FindNamespacesIntegrationTest extends AbstractNamespaceIntegrationTestBase { @Nested + @Order(1) class FindNamespaces { @Test @@ -45,8 +49,12 @@ public final void happyPath() { } } - @AfterAll - public void checkMetrics() { - checkMetrics("FindNamespaceCommand"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + FindNamespacesIntegrationTest.super.checkMetrics("FindNamespaceCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndDeleteIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndDeleteIntegrationTest.java index f2504bcd12..9f78cb82d7 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndDeleteIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndDeleteIntegrationTest.java @@ -12,15 +12,19 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) public class FindOneAndDeleteIntegrationTest extends AbstractCollectionIntegrationTestBase { @Nested + @Order(1) class FindOneAndDelete { @Test public void byId() { @@ -309,8 +313,12 @@ public void cleanUpData() { deleteAllDocuments(); } - @AfterAll - public void checkMetrics() { - checkMetrics("FindOneAndDeleteCommand"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + FindOneAndDeleteIntegrationTest.super.checkMetrics("FindOneAndDeleteCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndReplaceIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndReplaceIntegrationTest.java index 9a28ad7daa..b1d5acdf70 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndReplaceIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndReplaceIntegrationTest.java @@ -13,15 +13,19 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) public class FindOneAndReplaceIntegrationTest extends AbstractCollectionIntegrationTestBase { @Nested + @Order(1) class FindOneAndReplace { @Test public void byId() { @@ -481,6 +485,7 @@ public void byIdWithEmptyDocument() { } @Nested + @Order(2) class FindOneAndReplaceWithProjection { @Test public void byIdProjectionAfter() { @@ -624,8 +629,12 @@ public void cleanUpData() { deleteAllDocuments(); } - @AfterAll - public void checkMetrics() { - checkMetrics("FindOneAndReplaceCommand"); + @Nested + @Order(3) + class Metrics { + @Test + public void checkMetrics() { + FindOneAndReplaceIntegrationTest.super.checkMetrics("FindOneAndReplaceCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndUpdateIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndUpdateIntegrationTest.java index 5259af5946..4abe609d00 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndUpdateIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneAndUpdateIntegrationTest.java @@ -18,16 +18,20 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) public class FindOneAndUpdateIntegrationTest extends AbstractCollectionIntegrationTestBase { @Nested + @Order(1) class FindOneAndUpdate { @Test @@ -689,6 +693,7 @@ public void withSortDescendingReturnDocumentAfter() { } @Nested + @Order(2) class FindOneAndUpdateFailures { @Test @@ -971,6 +976,7 @@ public void byIdTryIncNonNumber() { } @Nested + @Order(3) class FindOneAndUpdateNested { @Test @@ -1148,6 +1154,7 @@ public void byIdAndSetNested() { } @Nested + @Order(4) class FindOneAndUpdateWithSetOnInsert { @Test @@ -1330,6 +1337,7 @@ public void useGivenDocIdOnInsert() { } @Nested + @Order(5) class FindOneAndUpdateWithProjection { @Test public void projectionAfterUpdate() { @@ -1514,6 +1522,7 @@ public void projectionBeforeUpdate() { } @Nested + @Order(6) class FindOneAndUpdateWithDate { @Test public void setWithDateField() { @@ -1672,6 +1681,7 @@ public void trySetWithInvalidDateField() { } @Nested + @Order(7) class FindOneAndUpdateWithCurrentDate { private static final ObjectMapper MAPPER = new ObjectMapper(); @@ -1783,8 +1793,12 @@ public void cleanUpData() { deleteAllDocuments(); } - @AfterAll - public void checkMetrics() { - checkMetrics("FindOneAndUpdateCommand"); + @Nested + @Order(8) + class Metrics { + @Test + public void checkMetrics() { + FindOneAndUpdateIntegrationTest.super.checkMetrics("FindOneAndUpdateCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java index c88bf7dc7d..63006418b6 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java @@ -15,17 +15,20 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; import org.junit.jupiter.api.TestMethodOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) public class FindOneIntegrationTest extends AbstractCollectionIntegrationTestBase { @Nested + @Order(1) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class FindOne { private static final String DOC1_JSON = @@ -732,8 +735,12 @@ public void withSizeOperatorNotNumber() { } } - @AfterAll - public void checkMetrics() { - checkMetrics("FindOneCommand"); + @Nested + @Order(2) + class Metrics { + @Test + public void checkMetrics() { + FindOneIntegrationTest.super.checkMetrics("FindOneCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/InsertIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/InsertIntegrationTest.java index e103e15f90..c9b9ec8ba5 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/InsertIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/InsertIntegrationTest.java @@ -22,13 +22,16 @@ import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import io.stargate.sgv2.jsonapi.testresource.DseTestResource; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) public class InsertIntegrationTest extends AbstractCollectionIntegrationTestBase { @AfterEach public void cleanUpData() { @@ -36,6 +39,7 @@ public void cleanUpData() { } @Nested + @Order(1) class InsertOne { @Test @@ -514,6 +518,7 @@ public void tryInsertTooLongName() { } @Nested + @Order(2) class InsertMany { @Test @@ -924,9 +929,13 @@ public void emptyDocuments() { } } - @AfterAll - public void checkMetrics() { - checkMetrics("InsertOneCommand"); - checkMetrics("InsertManyCommand"); + @Nested + @Order(3) + class Metrics { + @Test + public void checkMetrics() { + InsertIntegrationTest.super.checkMetrics("InsertOneCommand"); + InsertIntegrationTest.super.checkMetrics("InsertManyCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateManyIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateManyIntegrationTest.java index a36ba1de00..6f10f21738 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateManyIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateManyIntegrationTest.java @@ -15,16 +15,20 @@ import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReferenceArray; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) public class UpdateManyIntegrationTest extends AbstractCollectionIntegrationTestBase { @Nested + @Order(1) class UpdateMany { private void insert(int countOfDocument) { @@ -548,6 +552,7 @@ public void upsertAddFilterColumn() { } @Nested + @Order(2) class Concurrency { @RepeatedTest(10) @@ -642,6 +647,7 @@ public void concurrentUpdates() throws Exception { } @Nested + @Order(3) class ClientErrors { @Test @@ -677,8 +683,12 @@ public void cleanUpData() { deleteAllDocuments(); } - @AfterAll - public void checkMetrics() { - checkMetrics("UpdateManyCommand"); + @Nested + @Order(3) + class Metrics { + @Test + public void checkMetrics() { + UpdateManyIntegrationTest.super.checkMetrics("UpdateManyCommand"); + } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateOneIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateOneIntegrationTest.java index 4800aa3be5..5bc8935270 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateOneIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/UpdateOneIntegrationTest.java @@ -14,17 +14,21 @@ import io.stargate.sgv2.jsonapi.testresource.DseTestResource; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReferenceArray; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; @QuarkusIntegrationTest @QuarkusTestResource(DseTestResource.class) +@TestClassOrder(ClassOrderer.OrderAnnotation.class) public class UpdateOneIntegrationTest extends AbstractCollectionIntegrationTestBase { @Nested + @Order(1) class UpdateOneWithSet { @Test public void byIdAndSet() { @@ -670,6 +674,7 @@ public void byColumnAndSetSubDoc() { } @Nested + @Order(2) class UpdateOneWithUnset { @Test public void byIdAndUnset() { @@ -733,6 +738,7 @@ public void byIdAndUnset() { } @Nested + @Order(3) class UpdateOneWithPop { @Test @@ -817,6 +823,7 @@ public void byColumnAndPop() { } @Nested + @Order(4) class UpdateOneWithPush { @Test @@ -1012,6 +1019,7 @@ public void byColumnAndPushWithEachAndPosition() { } @Nested + @Order(5) class UpdateOneWithInc { @Test @@ -1089,6 +1097,7 @@ public void byColumnAndInc() { } @Nested + @Order(6) class UpdateOneWithMul { @Test public void byColumnAndMultiply() { @@ -1163,6 +1172,7 @@ public void byColumnAndMultiply() { } @Nested + @Order(7) class UpdateOneWithAddToSet { @Test @@ -1342,6 +1352,7 @@ public void byColumnAndAddToSetWithEach() { } @Nested + @Order(8) class UpdateOneWithMin { @Test @@ -1548,6 +1559,7 @@ public void byColumnMinMixedTypes() { } @Nested + @Order(9) class UpdateOneWithMax { @Test @@ -1754,6 +1766,7 @@ public void byColumnMaxMixedTypes() { } @Nested + @Order(10) class UpdateOneWithRename { @Test @@ -1831,6 +1844,7 @@ public void byColumnAndRename() { // Tests combining more than update operator, mostly for cross-validation @Nested + @Order(11) class UpdateOneMultipleOperationTypes { @Test public void byColumnUseSetAndUnset() { @@ -1902,6 +1916,7 @@ public void byColumnUseSetAndUnset() { } @Nested + @Order(12) class Concurrency { @RepeatedTest(10) @@ -2001,6 +2016,7 @@ public void concurrentUpdates() throws Exception { } @Nested + @Order(13) class ClientErrors { @Test public void invalidCommand() { @@ -2067,8 +2083,12 @@ public void cleanUpData() { deleteAllDocuments(); } - @AfterAll - public void checkMetrics() { - checkMetrics("UpdateOneCommand"); + @Nested + @Order(14) + class Metrics { + @Test + public void checkMetrics() { + UpdateOneIntegrationTest.super.checkMetrics("UpdateOneCommand"); + } } } From 8b996ac5b99e2931a203d3d62db3a476541405d8 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 24 May 2023 19:38:33 -0400 Subject: [PATCH 08/14] Fixed the FindNamespacesCommand name in the test case --- .../sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java index 4b24e74de3..67cbaf1713 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindNamespacesIntegrationTest.java @@ -54,7 +54,7 @@ public final void happyPath() { class Metrics { @Test public void checkMetrics() { - FindNamespacesIntegrationTest.super.checkMetrics("FindNamespaceCommand"); + FindNamespacesIntegrationTest.super.checkMetrics("FindNamespacesCommand"); } } } From 652f2bbc2f09ada722d8012569b59386d7541d00 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Thu, 25 May 2023 10:22:18 -0400 Subject: [PATCH 09/14] Changes to adapt tag name from config and added MeteredCommandProcessor --- .../jsonapi/api/v1/CollectionResource.java | 10 +- .../sgv2/jsonapi/api/v1/GeneralResource.java | 10 +- .../jsonapi/api/v1/NamespaceResource.java | 10 +- .../api/v1/metrics/JsonApiMetricsConfig.java | 32 +++++ .../api/v1/metrics/MetricsConstants.java | 33 ----- .../service/processor/CommandProcessor.java | 128 ++++-------------- .../processor/MeteredCommandProcessor.java | 116 ++++++++++++++++ .../AbstractNamespaceIntegrationTestBase.java | 12 +- 8 files changed, 193 insertions(+), 158 deletions(-) create mode 100644 src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java delete mode 100644 src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java create mode 100644 src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java index 2603ca48b7..d6e5fc10d2 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java @@ -17,7 +17,7 @@ import io.stargate.sgv2.jsonapi.api.model.command.impl.UpdateManyCommand; import io.stargate.sgv2.jsonapi.api.model.command.impl.UpdateOneCommand; import io.stargate.sgv2.jsonapi.config.constants.OpenApiConstants; -import io.stargate.sgv2.jsonapi.service.processor.CommandProcessor; +import io.stargate.sgv2.jsonapi.service.processor.MeteredCommandProcessor; import javax.inject.Inject; import javax.validation.Valid; import javax.validation.constraints.NotNull; @@ -51,11 +51,11 @@ public class CollectionResource { public static final String BASE_PATH = "/v1/{namespace}/{collection}"; - private final CommandProcessor commandProcessor; + private final MeteredCommandProcessor meteredCommandProcessor; @Inject - public CollectionResource(CommandProcessor commandProcessor) { - this.commandProcessor = commandProcessor; + public CollectionResource(MeteredCommandProcessor meteredCommandProcessor) { + this.meteredCommandProcessor = meteredCommandProcessor; } @Operation( @@ -144,7 +144,7 @@ public Uni> postCommand( CommandContext commandContext = new CommandContext(namespace, collection); // call processor - return commandProcessor + return meteredCommandProcessor .processCommand(commandContext, command) // map to 2xx unless overridden by error .map(commandResult -> commandResult.map()); diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java index 9a059623a4..3f1de328f5 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java @@ -6,7 +6,7 @@ import io.stargate.sgv2.jsonapi.api.model.command.GeneralCommand; import io.stargate.sgv2.jsonapi.api.model.command.impl.CreateNamespaceCommand; import io.stargate.sgv2.jsonapi.config.constants.OpenApiConstants; -import io.stargate.sgv2.jsonapi.service.processor.CommandProcessor; +import io.stargate.sgv2.jsonapi.service.processor.MeteredCommandProcessor; import javax.inject.Inject; import javax.validation.Valid; import javax.validation.constraints.NotNull; @@ -35,11 +35,11 @@ public class GeneralResource { public static final String BASE_PATH = "/v1"; - private final CommandProcessor commandProcessor; + private final MeteredCommandProcessor meteredCommandProcessor; @Inject - public GeneralResource(CommandProcessor commandProcessor) { - this.commandProcessor = commandProcessor; + public GeneralResource(MeteredCommandProcessor meteredCommandProcessor) { + this.meteredCommandProcessor = meteredCommandProcessor; } @Operation(summary = "Execute command", description = "Executes a single general command.") @@ -72,7 +72,7 @@ public GeneralResource(CommandProcessor commandProcessor) { public Uni> postCommand(@NotNull @Valid GeneralCommand command) { // call processor - return commandProcessor + return meteredCommandProcessor .processCommand(CommandContext.empty(), command) // map to 2xx unless overridden by error .map(commandResult -> commandResult.map()); diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResource.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResource.java index 5e2c702573..234c92d33a 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResource.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResource.java @@ -6,7 +6,7 @@ import io.stargate.sgv2.jsonapi.api.model.command.NamespaceCommand; import io.stargate.sgv2.jsonapi.api.model.command.impl.CreateCollectionCommand; import io.stargate.sgv2.jsonapi.config.constants.OpenApiConstants; -import io.stargate.sgv2.jsonapi.service.processor.CommandProcessor; +import io.stargate.sgv2.jsonapi.service.processor.MeteredCommandProcessor; import javax.inject.Inject; import javax.validation.Valid; import javax.validation.constraints.NotNull; @@ -40,11 +40,11 @@ public class NamespaceResource { public static final String BASE_PATH = "/v1/{namespace}"; - private final CommandProcessor commandProcessor; + private final MeteredCommandProcessor meteredCommandProcessor; @Inject - public NamespaceResource(CommandProcessor commandProcessor) { - this.commandProcessor = commandProcessor; + public NamespaceResource(MeteredCommandProcessor meteredCommandProcessor) { + this.meteredCommandProcessor = meteredCommandProcessor; } @Operation( @@ -88,7 +88,7 @@ public Uni> postCommand( CommandContext commandContext = new CommandContext(namespace, null); // call processor - return commandProcessor + return meteredCommandProcessor .processCommand(commandContext, command) // map to 2xx unless overridden by error .map(commandResult -> commandResult.map()); diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java new file mode 100644 index 0000000000..e9b571fcb2 --- /dev/null +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java @@ -0,0 +1,32 @@ +package io.stargate.sgv2.jsonapi.api.v1.metrics; + +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@ConfigMapping(prefix = "stargate.metrics") +public interface JsonApiMetricsConfig { + @NotNull + @Valid + JsonApiMetricsConfig.CustomMetricsConfig customMetricsConfig(); + + public interface CustomMetricsConfig { + @NotBlank + @WithDefault("error.class") + String errorClass(); + + @NotBlank + @WithDefault("error.code") + String errorCode(); + + @NotBlank + @WithDefault("command") + String command(); + + @NotBlank + @WithDefault("command.processor.process") + String metricsName(); + } +} diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java deleted file mode 100644 index 1543985eb0..0000000000 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/MetricsConstants.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.stargate.sgv2.jsonapi.api.v1.metrics; - -import io.micrometer.core.instrument.Tag; - -public class MetricsConstants { - - // same as V1 io.stargate.core.metrics.StargateMetricConstants#UNKNOWN - public static final String UNKNOWN_VALUE = "unknown"; - - public static final Tag ERROR_TRUE_TAG = Tag.of("error", "true"); - - public static final Tag ERROR_FALSE_TAG = Tag.of("error", "false"); - - public static final Tag MODULE_TAG = Tag.of("module", "jsonapi"); - - public static final String ERROR_CLASS = "errorClass"; - - public static final String ERROR_CODE = "errorCode"; - - public static final String TENANT = "tenant"; - - public static final String COMMAND = "command"; - - public static final String NA = "NA"; - - public static final Tag DEFAULT_ERROR_CLASS_TAG = Tag.of(ERROR_CLASS, NA); - - public static final Tag DEFAULT_ERROR_CODE_TAG = Tag.of(ERROR_CODE, NA); - - public static final String COUNT_METRICS_NAME = "command_processor_count"; - - public static final String TIMER_METRICS_NAME = "command_processor_total"; -} diff --git a/src/main/java/io/stargate/sgv2/jsonapi/service/processor/CommandProcessor.java b/src/main/java/io/stargate/sgv2/jsonapi/service/processor/CommandProcessor.java index 95c82922c9..2227fdb093 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/service/processor/CommandProcessor.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/service/processor/CommandProcessor.java @@ -1,14 +1,9 @@ package io.stargate.sgv2.jsonapi.service.processor; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Tag; -import io.micrometer.core.instrument.Tags; import io.smallrye.mutiny.Uni; -import io.stargate.sgv2.api.common.StargateRequestInfo; import io.stargate.sgv2.jsonapi.api.model.command.Command; import io.stargate.sgv2.jsonapi.api.model.command.CommandContext; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; -import io.stargate.sgv2.jsonapi.api.v1.metrics.MetricsConstants; import io.stargate.sgv2.jsonapi.exception.JsonApiException; import io.stargate.sgv2.jsonapi.exception.mappers.ThrowableCommandResultSupplier; import io.stargate.sgv2.jsonapi.service.bridge.executor.QueryExecutor; @@ -30,24 +25,15 @@ @ApplicationScoped public class CommandProcessor { - private final MeterRegistry meterRegistry; - private final QueryExecutor queryExecutor; private final CommandResolverService commandResolverService; - private final StargateRequestInfo stargateRequestInfo; - @Inject public CommandProcessor( - MeterRegistry meterRegistry, - QueryExecutor queryExecutor, - CommandResolverService commandResolverService, - StargateRequestInfo stargateRequestInfo) { - this.meterRegistry = meterRegistry; + QueryExecutor queryExecutor, CommandResolverService commandResolverService) { this.queryExecutor = queryExecutor; this.commandResolverService = commandResolverService; - this.stargateRequestInfo = stargateRequestInfo; } /** @@ -60,92 +46,36 @@ public CommandProcessor( */ public Uni processCommand( CommandContext commandContext, T command) { - final Tag commandTag = Tag.of(MetricsConstants.COMMAND, command.getClass().getSimpleName()); - final String tenant = - stargateRequestInfo != null && stargateRequestInfo.getTenantId().isPresent() - ? stargateRequestInfo.getTenantId().get() - : MetricsConstants.UNKNOWN_VALUE; - final Tag tenantTag = Tag.of(MetricsConstants.TENANT, tenant); - final long start = System.currentTimeMillis(); - return Uni.createFrom() - .item(start) - .onItem() - .transformToUni( - startTime -> { - // start by resolving the command, get resolver - return commandResolverService - .resolverForCommand(command) + // start by resolving the command, get resolver + return commandResolverService + .resolverForCommand(command) - // resolver can be null, not handled in CommandResolverService for now - .flatMap( - resolver -> { - // if we have resolver, resolve operation and execute - Operation operation = resolver.resolveCommand(commandContext, command); - return operation.execute(queryExecutor); - }) + // resolver can be null, not handled in CommandResolverService for now + .flatMap( + resolver -> { + // if we have resolver, resolve operation and execute + Operation operation = resolver.resolveCommand(commandContext, command); + return operation.execute(queryExecutor); + }) - // handler failures here - .onItemOrFailure() - .transform( - (item, failure) -> { - if (failure != null) { - if (failure instanceof JsonApiException jsonApiException) { - return jsonApiException; - } - return new ThrowableCommandResultSupplier(failure); + // handler failures here + .onFailure() + .recoverWithItem( + t -> { + // DocsException is supplier of the CommandResult + // so simply return + if (t instanceof JsonApiException jsonApiException) { + return jsonApiException; + } - } else { - // return the tags and command result - return item; - } - }) - .onItem() - .ifNotNull() - .transform(Supplier::get) - .onItem() - .transform( - result -> { - Tag errorTag = MetricsConstants.ERROR_FALSE_TAG; - ; - Tag errorClassTag = MetricsConstants.DEFAULT_ERROR_CLASS_TAG; - Tag errorCodeTag = MetricsConstants.DEFAULT_ERROR_CODE_TAG; - if (null != result.errors() && !result.errors().isEmpty()) { - errorTag = MetricsConstants.ERROR_TRUE_TAG; - String errorClass = - (String) - result - .errors() - .get(0) - .fields() - .getOrDefault( - MetricsConstants.ERROR_CLASS, - MetricsConstants.UNKNOWN_VALUE); - errorClassTag = Tag.of(MetricsConstants.ERROR_CLASS, errorClass); - String errorCode = - (String) - result - .errors() - .get(0) - .fields() - .getOrDefault( - MetricsConstants.ERROR_CODE, - MetricsConstants.UNKNOWN_VALUE); - errorCodeTag = Tag.of(MetricsConstants.ERROR_CODE, errorCode); - } - Tags tags = - Tags.of(commandTag, tenantTag, errorTag, errorClassTag, errorCodeTag); - // add metrics - meterRegistry - .counter(MetricsConstants.COUNT_METRICS_NAME, tags) - .increment(); - meterRegistry - .timer(MetricsConstants.TIMER_METRICS_NAME, tags) - .record( - System.currentTimeMillis() - start, - java.util.concurrent.TimeUnit.MILLISECONDS); - // return the command result - return result; - }); - }); + // otherwise use generic for now + return new ThrowableCommandResultSupplier(t); + }) + + // if we have a non-null item + // call supplier get to map to the command result + .onItem() + .ifNotNull() + .transform(Supplier::get); } } diff --git a/src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java b/src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java new file mode 100644 index 0000000000..2a5b1b3936 --- /dev/null +++ b/src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java @@ -0,0 +1,116 @@ +package io.stargate.sgv2.jsonapi.service.processor; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; +import io.smallrye.mutiny.Uni; +import io.stargate.sgv2.api.common.StargateRequestInfo; +import io.stargate.sgv2.api.common.config.MetricsConfig; +import io.stargate.sgv2.jsonapi.api.model.command.Command; +import io.stargate.sgv2.jsonapi.api.model.command.CommandContext; +import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; +import io.stargate.sgv2.jsonapi.api.v1.metrics.JsonApiMetricsConfig; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +@ApplicationScoped +public class MeteredCommandProcessor { + + private static final String UNKNOWN_VALUE = "unknown"; + + private static final String NA = "NA"; + + private final CommandProcessor commandProcessor; + + private final MeterRegistry meterRegistry; + + private final StargateRequestInfo stargateRequestInfo; + + private final JsonApiMetricsConfig.CustomMetricsConfig customMetricsConfig; + + private final MetricsConfig.TenantRequestCounterConfig tenantConfig; + + /** The tag for error being true, created only once. */ + private final Tag errorTrue; + + /** The tag for error being false, created only once. */ + private final Tag errorFalse; + + /** The tag for tenant being unknown, created only once. */ + private final Tag tenantUnknown; + + private final Tag defaultErrorCode; + + private final Tag defaultErrorClass; + + @Inject + public MeteredCommandProcessor( + CommandProcessor commandProcessor, + MeterRegistry meterRegistry, + StargateRequestInfo stargateRequestInfo, + JsonApiMetricsConfig jsonApiMetricsConfig, + MetricsConfig metricsConfig) { + this.commandProcessor = commandProcessor; + this.meterRegistry = meterRegistry; + tenantConfig = metricsConfig.tenantRequestCounter(); + customMetricsConfig = jsonApiMetricsConfig.customMetricsConfig(); + this.stargateRequestInfo = stargateRequestInfo; + errorTrue = Tag.of(tenantConfig.errorTag(), "true"); + errorFalse = Tag.of(tenantConfig.errorTag(), "false"); + tenantUnknown = Tag.of(tenantConfig.tenantTag(), UNKNOWN_VALUE); + defaultErrorCode = Tag.of(customMetricsConfig.errorCode(), NA); + defaultErrorClass = Tag.of(customMetricsConfig.errorClass(), NA); + } + + /** + * Processes a single command in a given command context. + * + * @param commandContext {@link CommandContext} + * @param command {@link Command} + * @return Uni emitting the result of the command execution. + * @param Type of the command. + */ + public Uni processCommand( + CommandContext commandContext, T command) { + Timer.Sample sample = Timer.start(meterRegistry); + // start by resolving the command, get resolver + return commandProcessor + .processCommand(commandContext, command) + .onItem() + .invoke( + result -> { + Tags tags = getCustomTags(command, result); + // add metrics + sample.stop(meterRegistry.timer(customMetricsConfig.metricsName(), tags)); + }); + } + + /** + * Generate custom tags based on the command and result. + * + * @param command - request command + * @param result - response command result + * @return + */ + private Tags getCustomTags(Command command, CommandResult result) { + Tag commandTag = Tag.of(customMetricsConfig.command(), command.getClass().getSimpleName()); + String tenant = stargateRequestInfo.getTenantId().orElse(UNKNOWN_VALUE); + Tag tenantTag = Tag.of(tenantConfig.tenantTag(), tenant); + Tag errorTag = errorFalse; + Tag errorClassTag = defaultErrorClass; + Tag errorCodeTag = defaultErrorCode; + // if error is present, add error tags else use defaults + if (null != result.errors() && !result.errors().isEmpty()) { + errorTag = errorTrue; + String errorClass = + (String) result.errors().get(0).fields().getOrDefault("exceptionClass", UNKNOWN_VALUE); + errorClassTag = Tag.of(customMetricsConfig.errorClass(), errorClass); + String errorCode = + (String) result.errors().get(0).fields().getOrDefault("errorCode", UNKNOWN_VALUE); + errorCodeTag = Tag.of(customMetricsConfig.errorCode(), errorCode); + } + Tags tags = Tags.of(commandTag, tenantTag, errorTag, errorClassTag, errorCodeTag); + return tags; + } +} diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java index b24e1a1392..2412b2a1b8 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/AbstractNamespaceIntegrationTestBase.java @@ -101,19 +101,9 @@ public static void checkMetrics(String commandName) { .lines() .filter( line -> - line.startsWith("command_processor_count") + line.startsWith("command_processor_process") && line.contains("command=\"" + commandName + "\"")) .toList(); assertThat(countMetrics.size()).isGreaterThan(0); - - List totalMetrics = - metrics - .lines() - .filter( - line -> - line.startsWith("command_processor_total") - && line.contains("command=\"" + commandName + "\"")) - .toList(); - assertThat(totalMetrics.size()).isGreaterThan(0); } } From eb22e762a4cf6fc8767de36aba8581c4221caa0f Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Thu, 25 May 2023 12:00:11 -0400 Subject: [PATCH 10/14] Added unit test case for MeteredCommandProcessor --- .../MeteredCommandProcessorTest.java | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java new file mode 100644 index 0000000000..9238db6aba --- /dev/null +++ b/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java @@ -0,0 +1,131 @@ +package io.stargate.sgv2.jsonapi.service.processor; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.TestProfile; +import io.quarkus.test.junit.mockito.InjectMock; +import io.smallrye.mutiny.Uni; +import io.stargate.sgv2.api.common.StargateRequestInfo; +import io.stargate.sgv2.common.testprofiles.NoGlobalResourcesTestProfile; +import io.stargate.sgv2.jsonapi.api.model.command.CommandContext; +import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; +import io.stargate.sgv2.jsonapi.api.model.command.impl.CountDocumentsCommands; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import javax.inject.Inject; +import javax.ws.rs.core.Response; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +@QuarkusTest +@TestProfile(NoGlobalResourcesTestProfile.Impl.class) +public class MeteredCommandProcessorTest { + @Inject MeteredCommandProcessor meteredCommandProcessor; + @InjectMock protected CommandProcessor commandProcessor; + @InjectMock protected StargateRequestInfo stargateRequestInfo; + @Inject ObjectMapper objectMapper; + + @Nested + class CustomMetrics { + @Test + public void metrics() throws Exception { + String json = + """ + { + "countDocuments": { + + } + } + """; + + CountDocumentsCommands countCommand = + objectMapper.readValue(json, CountDocumentsCommands.class); + CommandContext commandContext = new CommandContext("namespace", "collection"); + CommandResult commandResult = new CommandResult(Collections.emptyList()); + Mockito.when(commandProcessor.processCommand(commandContext, countCommand)) + .thenReturn(Uni.createFrom().item(commandResult)); + Mockito.when(stargateRequestInfo.getTenantId()).thenReturn(Optional.of("test-tenant")); + meteredCommandProcessor.processCommand(commandContext, countCommand).await().indefinitely(); + String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); + List httpMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("command_processor_process") + && line.contains("error=\"false\"")) + .toList(); + + assertThat(httpMetrics) + .satisfies( + lines -> { + assertThat(lines.size()).isGreaterThan(0); + lines.forEach( + line -> { + assertThat(line).contains("command=\"CountDocumentsCommands\""); + assertThat(line).contains("tenant=\"test-tenant\""); + assertThat(line).contains("error=\"false\""); + assertThat(line).contains("error_code=\"NA\""); + assertThat(line).contains("error_class=\"NA\""); + assertThat(line).contains("module=\"sgv2-jsonapi\""); + }); + }); + } + + @Test + public void errorMetrics() throws Exception { + String json = + """ + { + "countDocuments": { + + } + } + """; + + CountDocumentsCommands countCommand = + objectMapper.readValue(json, CountDocumentsCommands.class); + CommandContext commandContext = new CommandContext("namespace", "collection"); + Map fields = new HashMap<>(); + fields.put("exceptionClass", "TestExceptionClass"); + fields.put("errorCode", "TestErrorCode"); + CommandResult.Error error = new CommandResult.Error("message", fields, Response.Status.OK); + CommandResult commandResult = new CommandResult(Collections.singletonList(error)); + Mockito.when(commandProcessor.processCommand(commandContext, countCommand)) + .thenReturn(Uni.createFrom().item(commandResult)); + Mockito.when(stargateRequestInfo.getTenantId()).thenReturn(Optional.of("test-tenant")); + meteredCommandProcessor.processCommand(commandContext, countCommand).await().indefinitely(); + String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); + List httpMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("command_processor_process") + && line.contains("error=\"true\"")) + .toList(); + + assertThat(httpMetrics) + .satisfies( + lines -> { + assertThat(lines.size()).isGreaterThan(0); + lines.forEach( + line -> { + assertThat(line).contains("command=\"CountDocumentsCommands\""); + assertThat(line).contains("tenant=\"test-tenant\""); + assertThat(line).contains("error=\"true\""); + assertThat(line).contains("error_code=\"TestErrorCode\""); + assertThat(line).contains("error_class=\"TestExceptionClass\""); + assertThat(line).contains("module=\"sgv2-jsonapi\""); + }); + }); + } + } +} From ff66f5e262a3960c373100d9b95ac0eb90c3d702 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Thu, 25 May 2023 12:01:42 -0400 Subject: [PATCH 11/14] Added unit test case for MeteredCommandProcessor --- .../service/processor/MeteredCommandProcessorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java index 9238db6aba..f78a0bc72b 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java @@ -66,7 +66,7 @@ public void metrics() throws Exception { assertThat(httpMetrics) .satisfies( lines -> { - assertThat(lines.size()).isGreaterThan(0); + assertThat(lines.size()).isEqualTo(3); lines.forEach( line -> { assertThat(line).contains("command=\"CountDocumentsCommands\""); @@ -115,7 +115,7 @@ public void errorMetrics() throws Exception { assertThat(httpMetrics) .satisfies( lines -> { - assertThat(lines.size()).isGreaterThan(0); + assertThat(lines.size()).isEqualTo(3); lines.forEach( line -> { assertThat(line).contains("command=\"CountDocumentsCommands\""); From c8ac337594ee2a25d6c30ead3831c82c96730e2a Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Thu, 25 May 2023 12:28:51 -0400 Subject: [PATCH 12/14] Changes for merge conflict --- .../api/model/command/CommandResult.java | 1 - .../api/security/ErrorChallengeSender.java | 10 ++++----- .../jsonapi/api/v1/CollectionResource.java | 22 +++++++++---------- .../sgv2/jsonapi/api/v1/GeneralResource.java | 2 +- .../jsonapi/api/v1/NamespaceResource.java | 22 +++++++++---------- .../api/v1/metrics/JsonApiMetricsConfig.java | 6 ++--- .../processor/MeteredCommandProcessor.java | 4 ++-- .../serializers/ErrorSerializerTest.java | 2 +- .../MeteredCommandProcessorTest.java | 4 ++-- 9 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandResult.java b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandResult.java index f762a3cffa..782d815dd2 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandResult.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandResult.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.databind.JsonNode; import jakarta.validation.constraints.NotNull; import jakarta.ws.rs.core.Response; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java b/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java index e8a6b94b74..8dac25b056 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/security/ErrorChallengeSender.java @@ -7,13 +7,13 @@ import io.stargate.sgv2.api.common.security.challenge.ChallengeSender; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; import io.vertx.ext.web.RoutingContext; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.util.Collections; import java.util.List; -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java index d6e5fc10d2..580ff2f053 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java @@ -18,17 +18,17 @@ import io.stargate.sgv2.jsonapi.api.model.command.impl.UpdateOneCommand; import io.stargate.sgv2.jsonapi.config.constants.OpenApiConstants; import io.stargate.sgv2.jsonapi.service.processor.MeteredCommandProcessor; -import javax.inject.Inject; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; -import javax.ws.rs.Consumes; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.inject.Inject; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.media.Content; import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java index 34be70779b..fc27d1ec56 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java @@ -6,7 +6,7 @@ import io.stargate.sgv2.jsonapi.api.model.command.GeneralCommand; import io.stargate.sgv2.jsonapi.api.model.command.impl.CreateNamespaceCommand; import io.stargate.sgv2.jsonapi.config.constants.OpenApiConstants; -import io.stargate.sgv2.jsonapi.service.processor.CommandProcessor; +import io.stargate.sgv2.jsonapi.service.processor.MeteredCommandProcessor; import jakarta.inject.Inject; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResource.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResource.java index 234c92d33a..526f210f40 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResource.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/NamespaceResource.java @@ -7,17 +7,17 @@ import io.stargate.sgv2.jsonapi.api.model.command.impl.CreateCollectionCommand; import io.stargate.sgv2.jsonapi.config.constants.OpenApiConstants; import io.stargate.sgv2.jsonapi.service.processor.MeteredCommandProcessor; -import javax.inject.Inject; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; -import javax.ws.rs.Consumes; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.inject.Inject; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.media.Content; import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java index e9b571fcb2..b558308945 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java @@ -2,9 +2,9 @@ import io.smallrye.config.ConfigMapping; import io.smallrye.config.WithDefault; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; @ConfigMapping(prefix = "stargate.metrics") public interface JsonApiMetricsConfig { diff --git a/src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java b/src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java index 2a5b1b3936..badf9126ef 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java @@ -11,8 +11,8 @@ import io.stargate.sgv2.jsonapi.api.model.command.CommandContext; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; import io.stargate.sgv2.jsonapi.api.v1.metrics.JsonApiMetricsConfig; -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; @ApplicationScoped public class MeteredCommandProcessor { diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/model/command/serializers/ErrorSerializerTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/model/command/serializers/ErrorSerializerTest.java index 5bf3e1ac63..c5734fa198 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/model/command/serializers/ErrorSerializerTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/model/command/serializers/ErrorSerializerTest.java @@ -8,9 +8,9 @@ import io.quarkus.test.junit.TestProfile; import io.stargate.sgv2.common.testprofiles.NoGlobalResourcesTestProfile; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; -import java.util.Collections; import jakarta.inject.Inject; import jakarta.ws.rs.core.Response; +import java.util.Collections; import java.util.Map; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java index f78a0bc72b..b9df2550fc 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java @@ -13,13 +13,13 @@ import io.stargate.sgv2.jsonapi.api.model.command.CommandContext; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; import io.stargate.sgv2.jsonapi.api.model.command.impl.CountDocumentsCommands; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.Response; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import javax.inject.Inject; -import javax.ws.rs.core.Response; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.Mockito; From 3c5ee8dad578fad0d32637bc6ebefa25248ef8fa Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 31 May 2023 11:48:26 -0400 Subject: [PATCH 13/14] Changes as per review comments --- .../api/v1/metrics/JsonApiMetricsConfig.java | 34 ++++----- .../processor/MeteredCommandProcessor.java | 16 ++-- .../MeteredCommandProcessorTest.java | 75 +++++++++++++++++-- 3 files changed, 88 insertions(+), 37 deletions(-) diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java index b558308945..28e5a93d36 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java @@ -2,31 +2,23 @@ import io.smallrye.config.ConfigMapping; import io.smallrye.config.WithDefault; -import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -@ConfigMapping(prefix = "stargate.metrics") +@ConfigMapping(prefix = "stargate.jsonapi.metric") public interface JsonApiMetricsConfig { - @NotNull - @Valid - JsonApiMetricsConfig.CustomMetricsConfig customMetricsConfig(); + @NotBlank + @WithDefault("error.class") + String errorClass(); - public interface CustomMetricsConfig { - @NotBlank - @WithDefault("error.class") - String errorClass(); + @NotBlank + @WithDefault("error.code") + String errorCode(); - @NotBlank - @WithDefault("error.code") - String errorCode(); + @NotBlank + @WithDefault("command") + String command(); - @NotBlank - @WithDefault("command") - String command(); - - @NotBlank - @WithDefault("command.processor.process") - String metricsName(); - } + @NotBlank + @WithDefault("command.processor.process") + String metricsName(); } diff --git a/src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java b/src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java index badf9126ef..ed74434d8b 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessor.java @@ -27,7 +27,7 @@ public class MeteredCommandProcessor { private final StargateRequestInfo stargateRequestInfo; - private final JsonApiMetricsConfig.CustomMetricsConfig customMetricsConfig; + private final JsonApiMetricsConfig jsonApiMetricsConfig; private final MetricsConfig.TenantRequestCounterConfig tenantConfig; @@ -53,14 +53,14 @@ public MeteredCommandProcessor( MetricsConfig metricsConfig) { this.commandProcessor = commandProcessor; this.meterRegistry = meterRegistry; + this.jsonApiMetricsConfig = jsonApiMetricsConfig; tenantConfig = metricsConfig.tenantRequestCounter(); - customMetricsConfig = jsonApiMetricsConfig.customMetricsConfig(); this.stargateRequestInfo = stargateRequestInfo; errorTrue = Tag.of(tenantConfig.errorTag(), "true"); errorFalse = Tag.of(tenantConfig.errorTag(), "false"); tenantUnknown = Tag.of(tenantConfig.tenantTag(), UNKNOWN_VALUE); - defaultErrorCode = Tag.of(customMetricsConfig.errorCode(), NA); - defaultErrorClass = Tag.of(customMetricsConfig.errorClass(), NA); + defaultErrorCode = Tag.of(jsonApiMetricsConfig.errorCode(), NA); + defaultErrorClass = Tag.of(jsonApiMetricsConfig.errorClass(), NA); } /** @@ -82,7 +82,7 @@ public Uni processCommand( result -> { Tags tags = getCustomTags(command, result); // add metrics - sample.stop(meterRegistry.timer(customMetricsConfig.metricsName(), tags)); + sample.stop(meterRegistry.timer(jsonApiMetricsConfig.metricsName(), tags)); }); } @@ -94,7 +94,7 @@ public Uni processCommand( * @return */ private Tags getCustomTags(Command command, CommandResult result) { - Tag commandTag = Tag.of(customMetricsConfig.command(), command.getClass().getSimpleName()); + Tag commandTag = Tag.of(jsonApiMetricsConfig.command(), command.getClass().getSimpleName()); String tenant = stargateRequestInfo.getTenantId().orElse(UNKNOWN_VALUE); Tag tenantTag = Tag.of(tenantConfig.tenantTag(), tenant); Tag errorTag = errorFalse; @@ -105,10 +105,10 @@ private Tags getCustomTags(Command command, CommandResult result) { errorTag = errorTrue; String errorClass = (String) result.errors().get(0).fields().getOrDefault("exceptionClass", UNKNOWN_VALUE); - errorClassTag = Tag.of(customMetricsConfig.errorClass(), errorClass); + errorClassTag = Tag.of(jsonApiMetricsConfig.errorClass(), errorClass); String errorCode = (String) result.errors().get(0).fields().getOrDefault("errorCode", UNKNOWN_VALUE); - errorCodeTag = Tag.of(customMetricsConfig.errorCode(), errorCode); + errorCodeTag = Tag.of(jsonApiMetricsConfig.errorCode(), errorCode); } Tags tags = Tags.of(commandTag, tenantTag, errorTag, errorClassTag, errorCodeTag); return tags; diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java index b9df2550fc..53f639ff0d 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/service/processor/MeteredCommandProcessorTest.java @@ -13,8 +13,10 @@ import io.stargate.sgv2.jsonapi.api.model.command.CommandContext; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; import io.stargate.sgv2.jsonapi.api.model.command.impl.CountDocumentsCommands; +import io.stargate.sgv2.jsonapi.api.model.command.impl.FindCommand; import jakarta.inject.Inject; import jakarta.ws.rs.core.Response; +import java.time.Duration; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -52,7 +54,10 @@ public void metrics() throws Exception { Mockito.when(commandProcessor.processCommand(commandContext, countCommand)) .thenReturn(Uni.createFrom().item(commandResult)); Mockito.when(stargateRequestInfo.getTenantId()).thenReturn(Optional.of("test-tenant")); - meteredCommandProcessor.processCommand(commandContext, countCommand).await().indefinitely(); + meteredCommandProcessor + .processCommand(commandContext, countCommand) + .await() + .atMost(Duration.ofMinutes(1)); String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); List httpMetrics = metrics @@ -79,16 +84,66 @@ public void metrics() throws Exception { }); } + @Test + public void errorMetricsWithNoErrorCode() throws Exception { + String json = """ + { + "find": { + + } + } + """; + + FindCommand countCommand = objectMapper.readValue(json, FindCommand.class); + CommandContext commandContext = new CommandContext("namespace", "collection"); + Map fields = new HashMap<>(); + fields.put("exceptionClass", "TestExceptionClass"); + CommandResult.Error error = new CommandResult.Error("message", fields, Response.Status.OK); + CommandResult commandResult = new CommandResult(Collections.singletonList(error)); + Mockito.when(commandProcessor.processCommand(commandContext, countCommand)) + .thenReturn(Uni.createFrom().item(commandResult)); + Mockito.when(stargateRequestInfo.getTenantId()).thenReturn(Optional.of("test-tenant")); + meteredCommandProcessor + .processCommand(commandContext, countCommand) + .await() + .atMost(Duration.ofMinutes(1)); + String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); + List httpMetrics = + metrics + .lines() + .filter( + line -> + line.startsWith("command_processor_process") + && line.contains("error=\"true\"") + && line.contains("command=\"FindCommand\"")) + .toList(); + + assertThat(httpMetrics) + .satisfies( + lines -> { + assertThat(lines.size()).isEqualTo(3); + lines.forEach( + line -> { + assertThat(line).contains("command=\"FindCommand\""); + assertThat(line).contains("tenant=\"test-tenant\""); + assertThat(line).contains("error=\"true\""); + assertThat(line).contains("error_code=\"unknown\""); + assertThat(line).contains("error_class=\"TestExceptionClass\""); + assertThat(line).contains("module=\"sgv2-jsonapi\""); + }); + }); + } + @Test public void errorMetrics() throws Exception { String json = """ - { - "countDocuments": { + { + "countDocuments": { - } - } - """; + } + } + """; CountDocumentsCommands countCommand = objectMapper.readValue(json, CountDocumentsCommands.class); @@ -101,7 +156,10 @@ public void errorMetrics() throws Exception { Mockito.when(commandProcessor.processCommand(commandContext, countCommand)) .thenReturn(Uni.createFrom().item(commandResult)); Mockito.when(stargateRequestInfo.getTenantId()).thenReturn(Optional.of("test-tenant")); - meteredCommandProcessor.processCommand(commandContext, countCommand).await().indefinitely(); + meteredCommandProcessor + .processCommand(commandContext, countCommand) + .await() + .atMost(Duration.ofMinutes(1)); String metrics = given().when().get("/metrics").then().statusCode(200).extract().asString(); List httpMetrics = metrics @@ -109,7 +167,8 @@ public void errorMetrics() throws Exception { .filter( line -> line.startsWith("command_processor_process") - && line.contains("error=\"true\"")) + && line.contains("error=\"true\"") + && line.contains("command=\"CountDocumentsCommands\"")) .toList(); assertThat(httpMetrics) From 8fa6bd8cccf6a52b13907a2a1cc67cc6f77a46d2 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 31 May 2023 11:48:55 -0400 Subject: [PATCH 14/14] Updated CONFIGURATION.md --- CONFIGURATION.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CONFIGURATION.md b/CONFIGURATION.md index a18c2afd98..97f30fc5e9 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -37,3 +37,13 @@ Here are some Stargate-relevant property groups that are necessary for correct s | `stargate.jsonapi.operations.max-document-delete-count` | `int` | `20` | The maximum amount of documents that can be deleted in a single operation. In case there are more documents that could be deleted, the operation will set the `moreData` response status to `true`. | | `stargate.jsonapi.operations.max-in-operator-value-size` | `int` | `100` | The maximum number of _id values that can be passed for `$in` operator. | | `stargate.jsonapi.operations.lwt.retries` | `int` | `3` | The amount of client side retries in case of a LWT failure. | + +## Jsonapi metering configuration +*Configuration for jsonapi metering, defined by [JsonApiMetricsConfig.java](io/stargate/sgv2/jsonapi/api/v1/metrics/JsonApiMetricsConfig.java).* + +| Property | Type | Default | Description | +|---------------------------------------|----------|---------------|--------------------------------------------------------------| +| `stargate.jsonapi.metric.error-class` | `string` | `error.class` | Metrics tag that provides information about the error class. | +| `stargate.jsonapi.metric.error-code` | `string` | `error.code` | Metrics tag that provides information about the error code. | +| `stargate.jsonapi.metric.command` | `string` | `command` | Metrics tag that provides information about the command. | +| `stargate.jsonapi.metric.metrics.name`| `string` | `jsonapi` | Metrics name prefix. |