Revisit GraphQL metrics
Prior to this commit, we introduced a basic implementation for GraphQL metrics with Spring GraphQL. The main problem was that we were tracking the GraphQL queries themselves and those are good candidates for tags cardinality explosion. This commit revisits the metrics arrangement and introduces: * `graphql.request` as the main metric; this tracks the execution of a single request (i.e. multiple queries) * `graphql.datafetcher` for tracking data fetching operations. * `graphql.error` for counting errors. Closes gh-19
This commit is contained in:
29
README.md
29
README.md
@@ -153,14 +153,39 @@ A custom `WebInterceptor` can, for example, change the HTTP request/response hea
|
||||
|
||||
### Metrics
|
||||
|
||||
If the `spring-boot-starter-actuator` dependency is on the classpath, metrics will be collected for GraphQL queries.
|
||||
If the `spring-boot-starter-actuator` dependency is on the classpath, metrics will be collected for GraphQL requests.
|
||||
You can see those metrics by exposing the metrics endpoint with `application.properties`:
|
||||
```properties
|
||||
management.endpoints.web.exposure.include=health,metrics,info
|
||||
```
|
||||
|
||||
#### GraphQL Request (timer)
|
||||
|
||||
You can then check those metrics at `http://localhost:8080/actuator/metrics/graphql.query`.
|
||||
A Request metric timer is available at `/actuator/metrics/graphql.request`.
|
||||
|
||||
| Tag | Description | Sample values |
|
||||
|---------|-----------------|--------------------|
|
||||
| outcome | Request outcome | "SUCCESS", "ERROR" |
|
||||
|
||||
|
||||
#### GraphQL Data Fetcher (timer)
|
||||
|
||||
A Data Fetcher metric timer is available at `/actuator/metrics/graphql.datafetcher`.
|
||||
|
||||
| Tag | Description | Sample values |
|
||||
|---------|-----------------------|--------------------|
|
||||
| path | data fetcher path | "Query.project" |
|
||||
| outcome | data fetching outcome | "SUCCESS", "ERROR" |
|
||||
|
||||
|
||||
#### GraphQL Error (counter)
|
||||
|
||||
A counter metric counter is available at `/actuator/metrics/graphql.error`.
|
||||
|
||||
| Tag | Description | Sample values |
|
||||
|-----------|-----------------|-------------------------|
|
||||
| errorType | error type | "DataFetchingException" |
|
||||
| errorPath | error JSON Path | "$.project" |
|
||||
|
||||
## Sample applications
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ dependencies {
|
||||
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
|
||||
annotationProcessor 'org.springframework.boot:spring-boot-autoconfigure-processor'
|
||||
|
||||
testImplementation project(':spring-graphql-test')
|
||||
testImplementation 'com.fasterxml.jackson.core:jackson-databind'
|
||||
testImplementation 'org.springframework:spring-webflux'
|
||||
testImplementation 'org.springframework:spring-webmvc'
|
||||
@@ -52,6 +53,7 @@ dependencies {
|
||||
testImplementation 'org.apache.tomcat.embed:tomcat-embed-core'
|
||||
testImplementation 'org.apache.tomcat.embed:tomcat-embed-websocket'
|
||||
testImplementation 'org.springframework.boot:spring-boot-actuator-autoconfigure'
|
||||
testImplementation 'io.micrometer:micrometer-core'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,34 +16,48 @@
|
||||
|
||||
package org.springframework.graphql.boot.actuate.metrics;
|
||||
|
||||
import graphql.ErrorClassification;
|
||||
import graphql.ErrorType;
|
||||
import java.util.List;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
|
||||
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
|
||||
import graphql.schema.DataFetcher;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
|
||||
public class DefaultGraphQLTagsProvider implements GraphQLTagsProvider {
|
||||
|
||||
private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS");
|
||||
private final List<GraphQLTagsContributor> contributors;
|
||||
|
||||
private static final Tag OUTCOME_ERROR = Tag.of("outcome", "ERROR");
|
||||
public DefaultGraphQLTagsProvider(List<GraphQLTagsContributor> contributors) {
|
||||
this.contributors = contributors;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Iterable<Tag> getTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception) {
|
||||
Tags tags = Tags.of(Tag.of("query", parameters.getQuery()));
|
||||
if (result.isDataPresent()) {
|
||||
tags = tags.and(OUTCOME_SUCCESS);
|
||||
public Iterable<Tag> getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception) {
|
||||
Tags tags = Tags.of(GraphQLTags.executionOutcome(result, exception));
|
||||
for (GraphQLTagsContributor contributor : this.contributors) {
|
||||
tags = tags.and(contributor.getExecutionTags(parameters, result, exception));
|
||||
}
|
||||
else {
|
||||
tags = tags.and(OUTCOME_ERROR);
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
ErrorClassification errorClassification = result.getErrors().get(0).getErrorType();
|
||||
if (errorClassification instanceof ErrorType) {
|
||||
tags = tags.and(Tag.of("errorType", ((ErrorType) errorClassification).name()));
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Tag> getErrorTags(InstrumentationExecutionParameters parameters, GraphQLError error) {
|
||||
Tags tags = Tags.of(GraphQLTags.errorType(error), GraphQLTags.errorPath(error));
|
||||
for (GraphQLTagsContributor contributor : this.contributors) {
|
||||
tags = tags.and(contributor.getErrorTags(parameters, error));
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Tag> getDataFetchingTags(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable exception) {
|
||||
Tags tags = Tags.of(GraphQLTags.dataFetchingOutcome(exception), GraphQLTags.dataFetchingPath(parameters));
|
||||
for (GraphQLTagsContributor contributor : this.contributors) {
|
||||
tags = tags.and(contributor.getDataFetchingTags(dataFetcher, parameters, exception));
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package org.springframework.graphql.boot.actuate.metrics;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
|
||||
@@ -42,8 +45,8 @@ public class GraphQLMetricsAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(GraphQLTagsProvider.class)
|
||||
public DefaultGraphQLTagsProvider graphQLTagsProvider() {
|
||||
return new DefaultGraphQLTagsProvider();
|
||||
public DefaultGraphQLTagsProvider graphQLTagsProvider(ObjectProvider<GraphQLTagsContributor> contributors) {
|
||||
return new DefaultGraphQLTagsProvider(contributors.orderedStream().collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,12 +16,16 @@
|
||||
|
||||
package org.springframework.graphql.boot.actuate.metrics;
|
||||
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.execution.instrumentation.InstrumentationContext;
|
||||
import graphql.execution.instrumentation.InstrumentationState;
|
||||
import graphql.execution.instrumentation.SimpleInstrumentation;
|
||||
import graphql.execution.instrumentation.SimpleInstrumentationContext;
|
||||
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
|
||||
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
|
||||
import graphql.schema.DataFetcher;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
@@ -44,40 +48,89 @@ public class GraphQLMetricsInstrumentation extends SimpleInstrumentation {
|
||||
|
||||
@Override
|
||||
public InstrumentationState createState() {
|
||||
return new MetricsInstrumentationState(this.registry);
|
||||
return new RequestMetricsInstrumentationState(this.autoTimer, this.registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstrumentationContext<ExecutionResult> beginExecution(InstrumentationExecutionParameters parameters) {
|
||||
MetricsInstrumentationState state = parameters.getInstrumentationState();
|
||||
state.startTimer();
|
||||
if (this.autoTimer.isEnabled()) {
|
||||
RequestMetricsInstrumentationState state = parameters.getInstrumentationState();
|
||||
state.startTimer();
|
||||
return new SimpleInstrumentationContext<ExecutionResult>() {
|
||||
@Override
|
||||
public void onCompleted(ExecutionResult result, Throwable exc) {
|
||||
Iterable<Tag> tags = tagsProvider.getExecutionTags(parameters, result, exc);
|
||||
state.tags(tags).stopTimer();
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
result.getErrors().forEach(error -> {
|
||||
registry.counter("graphql.error", tagsProvider.getErrorTags(parameters, error)).increment();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return super.beginExecution(parameters);
|
||||
}
|
||||
|
||||
return new SimpleInstrumentationContext<ExecutionResult>() {
|
||||
@Override
|
||||
public void onCompleted(ExecutionResult result, Throwable exc) {
|
||||
Iterable<Tag> tags = tagsProvider.getTags(parameters, result, exc);
|
||||
state.stopTimer(autoTimer.builder("graphql.query").tags(tags));
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public DataFetcher<?> instrumentDataFetcher(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters) {
|
||||
if (this.autoTimer.isEnabled() && !parameters.isTrivialDataFetcher()) {
|
||||
return (environment) -> {
|
||||
Timer.Sample sample = Timer.start(this.registry);
|
||||
try {
|
||||
Object value = dataFetcher.get(environment);
|
||||
if (value instanceof CompletionStage<?>) {
|
||||
CompletionStage<?> completion = (CompletionStage<?>) value;
|
||||
return completion.whenComplete((result, error) -> {
|
||||
recordDataFetcherMetric(sample, dataFetcher, parameters, error);
|
||||
});
|
||||
}
|
||||
else {
|
||||
recordDataFetcherMetric(sample, dataFetcher, parameters, null);
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
recordDataFetcherMetric(sample, dataFetcher, parameters, throwable);
|
||||
throw throwable;
|
||||
}
|
||||
};
|
||||
}
|
||||
return super.instrumentDataFetcher(dataFetcher, parameters);
|
||||
}
|
||||
|
||||
private void recordDataFetcherMetric(Timer.Sample sample, DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable throwable) {
|
||||
Timer.Builder timer = this.autoTimer.builder("graphql.datafetcher");
|
||||
timer.tags(this.tagsProvider.getDataFetchingTags(dataFetcher, parameters, throwable));
|
||||
sample.stop(timer.register(this.registry));
|
||||
}
|
||||
|
||||
|
||||
static class MetricsInstrumentationState implements InstrumentationState {
|
||||
static class RequestMetricsInstrumentationState implements InstrumentationState {
|
||||
|
||||
private final MeterRegistry registry;
|
||||
|
||||
private Timer.Sample timerSample;
|
||||
private final Timer.Builder timer;
|
||||
|
||||
MetricsInstrumentationState(MeterRegistry registry) {
|
||||
private Timer.Sample sample;
|
||||
|
||||
RequestMetricsInstrumentationState(AutoTimer autoTimer, MeterRegistry registry) {
|
||||
this.timer = autoTimer.builder("graphql.request");
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
public void startTimer() {
|
||||
this.timerSample = Timer.start(this.registry);
|
||||
public RequestMetricsInstrumentationState tags(Iterable<Tag> tags) {
|
||||
this.timer.tags(tags);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void stopTimer(Timer.Builder timer) {
|
||||
this.timerSample.stop(timer.register(this.registry));
|
||||
public void startTimer() {
|
||||
this.sample = Timer.start(this.registry);
|
||||
}
|
||||
|
||||
public void stopTimer() {
|
||||
this.sample.stop(this.timer.register(this.registry));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.boot.actuate.metrics;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import graphql.ErrorClassification;
|
||||
import graphql.ErrorType;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.execution.ExecutionStepInfo;
|
||||
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
|
||||
import graphql.schema.GraphQLObjectType;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
|
||||
/**
|
||||
* Factory methods for Tags associated with a GraphQL requests.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public final class GraphQLTags {
|
||||
|
||||
private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS");
|
||||
|
||||
private static final Tag OUTCOME_ERROR = Tag.of("outcome", "ERROR");
|
||||
|
||||
private static final Tag UNKNOWN_ERRORTYPE = Tag.of("errorType", "UNKNOWN");
|
||||
|
||||
public static Tag executionOutcome(ExecutionResult result, Throwable exception) {
|
||||
if (exception == null && result.getErrors().isEmpty()) {
|
||||
return OUTCOME_SUCCESS;
|
||||
}
|
||||
else {
|
||||
return OUTCOME_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
public static Tag errorType(GraphQLError error) {
|
||||
ErrorClassification errorType = error.getErrorType();
|
||||
if (errorType instanceof ErrorType) {
|
||||
return Tag.of("errorType", ((ErrorType) errorType).name());
|
||||
}
|
||||
return UNKNOWN_ERRORTYPE;
|
||||
}
|
||||
|
||||
public static Tag errorPath(GraphQLError error) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
List<Object> pathSegments = error.getPath();
|
||||
if (!pathSegments.isEmpty()) {
|
||||
builder.append('$');
|
||||
for (Object segment : pathSegments) {
|
||||
try {
|
||||
int index = Integer.parseUnsignedInt(segment.toString());
|
||||
builder.append("[*]");
|
||||
}
|
||||
catch (NumberFormatException exc) {
|
||||
builder.append('.');
|
||||
builder.append(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Tag.of("errorPath", builder.toString());
|
||||
}
|
||||
|
||||
public static Tag dataFetchingOutcome(Throwable exception) {
|
||||
return (exception == null) ? OUTCOME_SUCCESS : OUTCOME_ERROR;
|
||||
}
|
||||
|
||||
public static Tag dataFetchingPath(InstrumentationFieldFetchParameters parameters) {
|
||||
ExecutionStepInfo executionStepInfo = parameters.getExecutionStepInfo();
|
||||
StringBuilder dataFetchingType = new StringBuilder();
|
||||
if (executionStepInfo.hasParent() &&
|
||||
executionStepInfo.getParent().getType() instanceof GraphQLObjectType) {
|
||||
dataFetchingType.append(((GraphQLObjectType) executionStepInfo.getParent().getType()).getName());
|
||||
dataFetchingType.append('.');
|
||||
}
|
||||
dataFetchingType.append(executionStepInfo.getPath().getSegmentName());
|
||||
return Tag.of("path", dataFetchingType.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.boot.actuate.metrics;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
|
||||
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
|
||||
import graphql.schema.DataFetcher;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
|
||||
public interface GraphQLTagsContributor {
|
||||
|
||||
Iterable<Tag> getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception);
|
||||
|
||||
Iterable<Tag> getErrorTags(InstrumentationExecutionParameters parameters, GraphQLError error);
|
||||
|
||||
Iterable<Tag> getDataFetchingTags(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable exception);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,10 +17,18 @@
|
||||
package org.springframework.graphql.boot.actuate.metrics;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
|
||||
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
|
||||
import graphql.schema.DataFetcher;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
|
||||
public interface GraphQLTagsProvider {
|
||||
|
||||
Iterable<Tag> getTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception);
|
||||
Iterable<Tag> getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception);
|
||||
|
||||
Iterable<Tag> getErrorTags(InstrumentationExecutionParameters parameters, GraphQLError error);
|
||||
|
||||
Iterable<Tag> getDataFetchingTags(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable exception);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.boot.actuate.metrics;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import graphql.ErrorType;
|
||||
import graphql.ExecutionResultImpl;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.GraphqlErrorBuilder;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.graphql.test.tester.TestExecutionResult;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link GraphQLTags}
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class GraphQLTagsTests {
|
||||
|
||||
@Test
|
||||
void executionOutcomeShouldSucceed() {
|
||||
Tag outcomeTag = GraphQLTags.executionOutcome(new TestExecutionResult(), null);
|
||||
assertThat(outcomeTag.getValue()).isEqualTo("SUCCESS");
|
||||
}
|
||||
|
||||
@Test
|
||||
void executionOutcomeShouldErrorWhenExceptionThrown() {
|
||||
Tag outcomeTag = GraphQLTags.executionOutcome(new TestExecutionResult(), new IllegalArgumentException("test error"));
|
||||
assertThat(outcomeTag.getValue()).isEqualTo("ERROR");
|
||||
}
|
||||
|
||||
@Test
|
||||
void executionOutcomeShouldErrorWhenResponseErrors() {
|
||||
ExecutionResultImpl.Builder builder = new ExecutionResultImpl.Builder();
|
||||
builder.addError(GraphqlErrorBuilder.newError().message("Invalid query").build());
|
||||
Tag outcomeTag = GraphQLTags.executionOutcome(builder.build(), null);
|
||||
assertThat(outcomeTag.getValue()).isEqualTo("ERROR");
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorTypeShouldBeDefinedIfPresent() {
|
||||
GraphQLError error = GraphqlErrorBuilder.newError().errorType(ErrorType.DataFetchingException).message("test error").build();
|
||||
Tag errorTypeTag = GraphQLTags.errorType(error);
|
||||
assertThat(errorTypeTag.getValue()).isEqualTo("DataFetchingException");
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorPathShouldUseJsonPathFormat() {
|
||||
GraphQLError error = GraphqlErrorBuilder.newError().path(Arrays.asList("project", "name")).message("test error").build();
|
||||
Tag errorPathTag = GraphQLTags.errorPath(error);
|
||||
assertThat(errorPathTag.getValue()).isEqualTo("$.project.name");
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorPathShouldUseJsonPathFormatForIndices() {
|
||||
GraphQLError error = GraphqlErrorBuilder.newError().path(Arrays.asList("issues", "42", "title")).message("test error").build();
|
||||
Tag errorPathTag = GraphQLTags.errorPath(error);
|
||||
assertThat(errorPathTag.getValue()).isEqualTo("$.issues[*].title");
|
||||
}
|
||||
|
||||
@Test
|
||||
void dataFetchingOutcomeShouldBeSuccessfulIfNoException() {
|
||||
Tag fetchingOutcomeTag = GraphQLTags.dataFetchingOutcome(null);
|
||||
assertThat(fetchingOutcomeTag.getValue()).isEqualTo("SUCCESS");
|
||||
}
|
||||
|
||||
@Test
|
||||
void dataFetchingOutcomeShouldBeErrorIfException() {
|
||||
Tag fetchingOutcomeTag = GraphQLTags.dataFetchingOutcome(new IllegalStateException("error state"));
|
||||
assertThat(fetchingOutcomeTag.getValue()).isEqualTo("ERROR");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -123,8 +123,6 @@ class ReactorDataFetcherAdapter implements DataFetcher<Object> {
|
||||
|
||||
Method method = ClassUtils.getMethod(dataFetcher.getClass(), "get", DataFetchingEnvironment.class);
|
||||
method = ClassUtils.getMostSpecificMethod(method, dataFetcher.getClass());
|
||||
Class<?> returnType = method.getReturnType();
|
||||
System.out.println(returnType.getName());
|
||||
|
||||
dataFetcher = new ReactorDataFetcherAdapter(dataFetcher, parent.getName().equals("Subscription"));
|
||||
codeRegistry.dataFetcher(parent, fieldDefinition, dataFetcher);
|
||||
|
||||
Reference in New Issue
Block a user