From 3f0c7d1b436cae0a584663e336396f6a61508871 Mon Sep 17 00:00:00 2001 From: Gurel Erceis Date: Mon, 3 Sep 2018 21:05:12 +0100 Subject: [PATCH] Handle InvocationException so that i doesn't hide the original exception from GraphQL --- .../api/graphql/rejoiner/SchemaModule.java | 6 ++ .../rejoiner/RejoinerIntegrationTest.java | 89 ++++++++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/rejoiner/src/main/java/com/google/api/graphql/rejoiner/SchemaModule.java b/rejoiner/src/main/java/com/google/api/graphql/rejoiner/SchemaModule.java index 1a5ba02..40904a1 100644 --- a/rejoiner/src/main/java/com/google/api/graphql/rejoiner/SchemaModule.java +++ b/rejoiner/src/main/java/com/google/api/graphql/rejoiner/SchemaModule.java @@ -394,6 +394,12 @@ private GraphQLFieldDefinition methodToFieldDefinition( } try { return method.invoke(this, methodParameterValues); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } + throw new RuntimeException(cause); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/rejoiner/src/test/java/com/google/api/graphql/rejoiner/RejoinerIntegrationTest.java b/rejoiner/src/test/java/com/google/api/graphql/rejoiner/RejoinerIntegrationTest.java index 194b714..6e56d3a 100644 --- a/rejoiner/src/test/java/com/google/api/graphql/rejoiner/RejoinerIntegrationTest.java +++ b/rejoiner/src/test/java/com/google/api/graphql/rejoiner/RejoinerIntegrationTest.java @@ -16,20 +16,32 @@ import static com.google.common.truth.Truth.assertThat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import com.google.api.graphql.rejoiner.Greetings.ExtraProto; import com.google.api.graphql.rejoiner.Greetings.GreetingsRequest; import com.google.api.graphql.rejoiner.Greetings.GreetingsResponse; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.inject.Guice; import com.google.inject.Key; +import graphql.ErrorType; +import graphql.ExecutionInput; +import graphql.ExecutionResult; +import graphql.GraphQL; +import graphql.GraphQLError; +import graphql.language.SourceLocation; import graphql.schema.DataFetchingEnvironment; import graphql.schema.GraphQLList; import graphql.schema.GraphQLNonNull; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLOutputType; import graphql.schema.GraphQLSchema; +import io.grpc.Status; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -38,9 +50,33 @@ @RunWith(JUnit4.class) public final class RejoinerIntegrationTest { + static class SampleGraphQLException extends RuntimeException implements GraphQLError { + @Override + public String getMessage() { + return "Test GraphQLError"; + } + + @Override + public List getLocations() { + return null; + } + + @Override + public ErrorType getErrorType() { + return ErrorType.DataFetchingException; + } + + @Override + public Map getExtensions() { + HashMap extensions = Maps.newHashMap(); + extensions.put("error", "message"); + return extensions; + } + } + static class GreetingsSchemaModule extends SchemaModule { - @Query("grettingXL") + @Query("greetingXL") ListenableFuture greetingXl( GreetingsRequest req, DataFetchingEnvironment env) { return Futures.immediateFuture(GreetingsResponse.newBuilder().setId(req.getId()).build()); @@ -67,6 +103,18 @@ ListenableFuture greetingsResponseToExtraProto( ExtraProto request, GreetingsResponse source) { return Futures.immediateFuture(request.toBuilder().setSomeValue(source.getId()).build()); } + + @Query("greetingWithException") + ListenableFuture greetingsWithException(GreetingsRequest request) { + throw Status.UNIMPLEMENTED + .withDescription("message from service") + .asRuntimeException(); + } + + @Query("greetingWithGraphQLError") + ListenableFuture greetingsWithGraphqlError(GreetingsRequest request) { + throw new SampleGraphQLException(); + } } static class GreetingsAddonSchemaModule extends SchemaModule { @@ -84,7 +132,7 @@ static class GreetingsAddonSchemaModule extends SchemaModule { @Test public void schemaShouldHaveOneQuery() { - assertThat(schema.getQueryType().getFieldDefinitions()).hasSize(4); + assertThat(schema.getQueryType().getFieldDefinitions()).hasSize(6); } @Test @@ -116,4 +164,41 @@ public void schemaModificationsShouldBeApplied() { assertThat(obj.getFieldDefinition("extraField")).isNotNull(); assertThat(obj.getFieldDefinition("greeting")).isNull(); } + + @Test + public void handlesRuntimeExceptionMessage() { + GraphQL graphQL = GraphQL.newGraphQL(schema) + .build(); + + ExecutionInput executionInput = ExecutionInput.newExecutionInput() + .query("query { greetingWithException { id } }") + .build(); + + ExecutionResult executionResult = graphQL.execute(executionInput); + + assertThat(executionResult.getErrors()).hasSize(1); + GraphQLError graphQLError = executionResult.getErrors().get(0); + assertThat(graphQLError.getMessage()).isEqualTo("Exception while fetching data (/greetingWithException) : UNIMPLEMENTED: message from service"); + assertThat(graphQLError.getPath()).hasSize(1); + assertThat(graphQLError.getPath().get(0)).isEqualTo("greetingWithException"); + } + + @Test + public void handlesGraphQLError() { + GraphQL graphQL = GraphQL.newGraphQL(schema) + .build(); + + ExecutionInput executionInput = ExecutionInput.newExecutionInput() + .query("query { greetingWithGraphQLError { id } }") + .build(); + + ExecutionResult executionResult = graphQL.execute(executionInput); + + assertThat(executionResult.getErrors()).hasSize(1); + GraphQLError graphQLError = executionResult.getErrors().get(0); + assertThat(graphQLError.getMessage()).isEqualTo("Exception while fetching data (/greetingWithGraphQLError) : Test GraphQLError"); + assertThat(graphQLError.getExtensions()).containsEntry("error", "message"); + assertThat(graphQLError.getPath()).hasSize(1); + assertThat(graphQLError.getPath().get(0)).isEqualTo("greetingWithGraphQLError"); + } }