Skip to content

Commit

Permalink
Merge pull request #14278 from phillip-kruger/graphql-npe-no-endpoint
Browse files Browse the repository at this point in the history
Create better response when no GraphQL endpoint has been generated
  • Loading branch information
gsmet authored Jan 13, 2021
2 parents 56d9b6d + 1d553e7 commit 5eff32f
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.quarkus.smallrye.graphql.deployment;

import io.quarkus.builder.item.SimpleBuildItem;
import io.quarkus.runtime.RuntimeValue;

final class SmallRyeGraphQLInitializedBuildItem extends SimpleBuildItem {

private final RuntimeValue<Boolean> initialized;

public SmallRyeGraphQLInitializedBuildItem(RuntimeValue<Boolean> initialized) {
this.initialized = initialized;
}

public RuntimeValue<Boolean> getInitialized() {
return initialized;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import io.quarkus.deployment.util.ServiceUtil;
import io.quarkus.deployment.util.WebJarUtil;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.RuntimeValue;
import io.quarkus.runtime.metrics.MetricsFactory;
import io.quarkus.smallrye.graphql.runtime.SmallRyeGraphQLRecorder;
import io.quarkus.smallrye.graphql.runtime.SmallRyeGraphQLRuntimeConfig;
Expand All @@ -51,7 +52,6 @@
import io.quarkus.vertx.http.deployment.RequireBodyHandlerBuildItem;
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem;
import io.quarkus.vertx.http.runtime.HandlerType;
import io.smallrye.graphql.cdi.config.ConfigKey;
import io.smallrye.graphql.cdi.config.GraphQLConfig;
import io.smallrye.graphql.cdi.producer.GraphQLProducer;
Expand Down Expand Up @@ -145,6 +145,7 @@ void registerNativeImageResources(BuildProducer<ServiceProviderBuildItem> servic
void buildExecutionService(
BuildProducer<ReflectiveClassBuildItem> reflectiveClassProducer,
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchyProducer,
BuildProducer<SmallRyeGraphQLInitializedBuildItem> graphQLInitializedProducer,
SmallRyeGraphQLRecorder recorder,
BeanContainerBuildItem beanContainer,
CombinedIndexBuildItem combinedIndex,
Expand All @@ -154,7 +155,8 @@ void buildExecutionService(

Schema schema = SchemaBuilder.build(index, graphQLConfig.autoNameStrategy);

recorder.createExecutionService(beanContainer.getValue(), schema);
RuntimeValue<Boolean> initialized = recorder.createExecutionService(beanContainer.getValue(), schema);
graphQLInitializedProducer.produce(new SmallRyeGraphQLInitializedBuildItem(initialized));

// Make sure the complex object from the application can work in native mode
reflectiveClassProducer.produce(new ReflectiveClassBuildItem(true, true, getSchemaJavaClasses(schema)));
Expand All @@ -173,19 +175,26 @@ void requireBody(BuildProducer<RequireBodyHandlerBuildItem> requireBodyHandlerPr
@BuildStep
void buildSchemaEndpoint(
BuildProducer<RouteBuildItem> routeProducer,
SmallRyeGraphQLInitializedBuildItem graphQLInitializedBuildItem,
SmallRyeGraphQLRecorder recorder,
SmallRyeGraphQLConfig graphQLConfig) {

Handler<RoutingContext> schemaHandler = recorder.schemaHandler();
routeProducer.produce(
new RouteBuildItem(graphQLConfig.rootPath + SCHEMA_PATH, schemaHandler, HandlerType.BLOCKING));
Handler<RoutingContext> schemaHandler = recorder.schemaHandler(graphQLInitializedBuildItem.getInitialized());

routeProducer.produce(new RouteBuildItem.Builder()
.route(graphQLConfig.rootPath + SCHEMA_PATH)
.handler(schemaHandler)
.blockingRoute()
.build());

}

@Record(ExecutionTime.STATIC_INIT)
@BuildStep
void buildExecutionEndpoint(
BuildProducer<RouteBuildItem> routeProducer,
BuildProducer<NotFoundPageDisplayableEndpointBuildItem> notFoundPageDisplayableEndpointProducer,
SmallRyeGraphQLInitializedBuildItem graphQLInitializedBuildItem,
SmallRyeGraphQLRecorder recorder,
ShutdownContextBuildItem shutdownContext,
LaunchModeBuildItem launchMode,
Expand Down Expand Up @@ -217,7 +226,8 @@ void buildExecutionEndpoint(

Boolean allowGet = ConfigProvider.getConfig().getOptionalValue(ConfigKey.ALLOW_GET, boolean.class).orElse(false);

Handler<RoutingContext> executionHandler = recorder.executionHandler(allowGet);
Handler<RoutingContext> executionHandler = recorder.executionHandler(graphQLInitializedBuildItem.getInitialized(),
allowGet);
routeProducer.produce(new RouteBuildItem.Builder()
.route(graphQLConfig.rootPath)
.handler(executionHandler)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.quarkus.smallrye.graphql.runtime;

import io.vertx.core.Handler;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.RoutingContext;

/**
* Handler that is used when no endpoint is available
*/
public class SmallRyeGraphQLNoEndpointHandler implements Handler<RoutingContext> {
private static final String CONTENT_TYPE = "text/plain; charset=UTF-8";
private static final String MESSAGE = "GraphQL Schema not generated. Make sure you have a GraphQL Endpoint. Go to https://quarkus.io/guides/microprofile-graphql to learn how";

@Override
public void handle(RoutingContext event) {
HttpServerResponse response = event.response();
response.headers().set(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE);
response.setStatusCode(404).end(MESSAGE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.spi.CDI;

import graphql.schema.GraphQLSchema;
import io.quarkus.arc.runtime.BeanContainer;
import io.quarkus.runtime.RuntimeValue;
import io.quarkus.runtime.ShutdownContext;
import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.security.identity.CurrentIdentityAssociation;
Expand All @@ -18,27 +20,36 @@
@Recorder
public class SmallRyeGraphQLRecorder {

public void createExecutionService(BeanContainer beanContainer, Schema schema) {
public RuntimeValue<Boolean> createExecutionService(BeanContainer beanContainer, Schema schema) {
GraphQLProducer graphQLProducer = beanContainer.instance(GraphQLProducer.class);
GraphQLConfig graphQLConfig = beanContainer.instance(GraphQLConfig.class);
graphQLProducer.initialize(schema, graphQLConfig);
GraphQLSchema graphQLSchema = graphQLProducer.initialize(schema, graphQLConfig);
return new RuntimeValue<>(graphQLSchema != null);
}

public Handler<RoutingContext> executionHandler(boolean allowGet) {
Instance<CurrentIdentityAssociation> identityAssociations = CDI.current()
.select(CurrentIdentityAssociation.class);
CurrentIdentityAssociation association;
if (identityAssociations.isResolvable()) {
association = identityAssociations.get();
public Handler<RoutingContext> executionHandler(RuntimeValue<Boolean> initialized, boolean allowGet) {
if (initialized.getValue()) {
Instance<CurrentIdentityAssociation> identityAssociations = CDI.current()
.select(CurrentIdentityAssociation.class);
CurrentIdentityAssociation association;
if (identityAssociations.isResolvable()) {
association = identityAssociations.get();
} else {
association = null;
}
CurrentVertxRequest currentVertxRequest = CDI.current().select(CurrentVertxRequest.class).get();
return new SmallRyeGraphQLExecutionHandler(allowGet, association, currentVertxRequest);
} else {
association = null;
return new SmallRyeGraphQLNoEndpointHandler();
}
CurrentVertxRequest currentVertxRequest = CDI.current().select(CurrentVertxRequest.class).get();
return new SmallRyeGraphQLExecutionHandler(allowGet, association, currentVertxRequest);
}

public Handler<RoutingContext> schemaHandler() {
return new SmallRyeGraphQLSchemaHandler();
public Handler<RoutingContext> schemaHandler(RuntimeValue<Boolean> initialized) {
if (initialized.getValue()) {
return new SmallRyeGraphQLSchemaHandler();
} else {
return new SmallRyeGraphQLNoEndpointHandler();
}
}

public Handler<RoutingContext> uiHandler(String graphqlUiFinalDestination,
Expand Down

0 comments on commit 5eff32f

Please sign in to comment.