diff --git a/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java b/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java index 45225b7b1..0b55347dd 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java @@ -125,7 +125,7 @@ public static GraphQLSchema bootstrap(Schema schema) { } public static GraphQLSchema bootstrap(Schema schema, boolean skipInjectionValidation) { - if (schema != null && (schema.hasOperations()) || Config.get().isFederationEnabled()) { + if (schema != null && (schema.hasOperations() || Config.get().isFederationEnabled())) { Bootstrap bootstrap = new Bootstrap(schema, skipInjectionValidation); bootstrap.generateGraphQLSchema(); return bootstrap.graphQLSchema; @@ -186,10 +186,6 @@ private void generateGraphQLSchema() { createGraphQLObjectTypes(); createGraphQLInputObjectTypes(); - GraphQLObjectType resolversType = Config.get().isFederationEnabled() - ? buildResolvers() - : GraphQLObjectType.newObject().name("Resolver").build(); - GraphQLObjectType queryRootType = addQueries(schemaBuilder); addMutations(schemaBuilder); addSubscriptions(schemaBuilder); @@ -227,7 +223,10 @@ private void generateGraphQLSchema() { // hack! For schema build success if queries are empty. // It will be overrides in Federation transformation - addSdlQueryToSchema(schemaBuilder, queryRootType); + addDummySdlQuery(schemaBuilder, queryRootType); + + // Build reference resolvers type, without adding to schema (just for federation) + GraphQLObjectType resolversType = buildResolvers(); GraphQLSchema rawSchema = schemaBuilder.build(); this.graphQLSchema = Federation.transform(rawSchema) @@ -241,7 +240,7 @@ private void generateGraphQLSchema() { } } - private void addSdlQueryToSchema(GraphQLSchema.Builder schemaBuilder, GraphQLObjectType queryRootType) { + private void addDummySdlQuery(GraphQLSchema.Builder schemaBuilder, GraphQLObjectType queryRootType) { GraphQLObjectType type = GraphQLObjectType.newObject() .name("_Service") .field(GraphQLFieldDefinition diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/ResolverTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/ResolverTest.java new file mode 100644 index 000000000..278f0585f --- /dev/null +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/ResolverTest.java @@ -0,0 +1,155 @@ +package io.smallrye.graphql.execution; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.IOException; +import java.io.InputStream; +import java.util.stream.Stream; + +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; + +import org.jboss.jandex.IndexView; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import graphql.schema.GraphQLSchema; +import io.smallrye.graphql.api.Directive; +import io.smallrye.graphql.api.federation.Extends; +import io.smallrye.graphql.api.federation.External; +import io.smallrye.graphql.api.federation.Key; +import io.smallrye.graphql.api.federation.Requires; +import io.smallrye.graphql.bootstrap.Bootstrap; +import io.smallrye.graphql.schema.SchemaBuilder; +import io.smallrye.graphql.schema.model.Schema; +import io.smallrye.graphql.spi.config.Config; +import io.smallrye.graphql.test.resolver.ExtendedApi; +import io.smallrye.graphql.test.resolver.ExtendedType; + +/** + * Test for Federated namespaces + */ +public class ResolverTest { + private static final TestConfig config = (TestConfig) Config.get(); + private static ExecutionService executionService; + + @AfterAll + static void afterAll() { + config.reset(); + config.federationEnabled = false; + System.setProperty("smallrye.graphql.federation.enabled", "false"); + } + + @BeforeAll + static void beforeAll() { + config.federationEnabled = true; + System.setProperty("smallrye.graphql.federation.enabled", "true"); + + IndexView index = buildIndex(Directive.class, Key.class, External.class, Key.Keys.class, + Extends.class, Requires.class, ExtendedType.class, ExtendedApi.class); + + GraphQLSchema graphQLSchema = createGraphQLSchema(index); + Schema schema = SchemaBuilder.build(index); + executionService = new ExecutionService(graphQLSchema, schema); + } + + private static IndexView buildIndex(Class... classes) { + org.jboss.jandex.Indexer indexer = new org.jboss.jandex.Indexer(); + Stream.of(classes).forEach(cls -> index(indexer, cls)); + return indexer.complete(); + } + + private static InputStream getResourceStream(Class type) { + String name = type.getName().replace(".", "/") + ".class"; + return Thread.currentThread().getContextClassLoader().getResourceAsStream(name); + } + + private static void index(org.jboss.jandex.Indexer indexer, Class cls) { + try { + indexer.index(getResourceStream(cls)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static GraphQLSchema createGraphQLSchema(IndexView index) { + Schema schema = SchemaBuilder.build(index); + assertNotNull(schema, "Schema should not be null"); + GraphQLSchema graphQLSchema = Bootstrap.bootstrap(schema, true); + assertNotNull(graphQLSchema, "GraphQLSchema should not be null"); + return graphQLSchema; + } + + private static JsonObject executeAndGetResult(String graphQL) { + JsonObjectResponseWriter jsonObjectResponseWriter = new JsonObjectResponseWriter(graphQL); + jsonObjectResponseWriter.logInput(); + executionService.executeSync(jsonObjectResponseWriter.getInput(), jsonObjectResponseWriter); + jsonObjectResponseWriter.logOutput(); + return jsonObjectResponseWriter.getOutput(); + } + + @Test + public void findByIdTest() { + JsonObject jsonObject = executeAndGetResult(TEST_ID_QUERY); + assertNotNull(jsonObject); + + JsonValue jsonValue = jsonObject.getJsonObject("data") + .getJsonArray("_entities") + .getJsonObject(0) + .get("id"); + assertEquals(((JsonString) jsonValue).getString(), "id"); + + jsonValue = jsonObject.getJsonObject("data") + .getJsonArray("_entities") + .getJsonObject(0) + .get("value"); + assertNull(jsonValue); + } + + @Test + public void extendsTest() { + JsonObject jsonObject = executeAndGetResult(TEST_ID_NAME_KEY_QUERY); + assertNotNull(jsonObject); + + JsonValue jsonValue = jsonObject.getJsonObject("data") + .getJsonArray("_entities") + .getJsonObject(0) + .get("id"); + assertEquals(((JsonString) jsonValue).getString(), "id"); + + jsonValue = jsonObject.getJsonObject("data") + .getJsonArray("_entities") + .getJsonObject(0) + .get("value"); + assertEquals(((JsonString) jsonValue).getString(), "idnamekey"); + } + + private static final String TEST_ID_QUERY = "query {\n" + + "_entities(\n" + + " representations: { id: \"id\", __typename: \"ExtendedType\" }\n" + + ") {\n" + + " __typename\n" + + " ... on ExtendedType {\n" + + " id\n" + + " }\n" + + " }\n" + + "}"; + + private static final String TEST_ID_NAME_KEY_QUERY = "query {\n" + + "_entities(\n" + + " representations: { id: \"id\", name: \"name\", key: \"key\", __typename: \"ExtendedType\" }\n" + + ") {\n" + + " __typename\n" + + " ... on ExtendedType {\n" + + " id\n" + + " name\n" + + " key\n" + + " value\n" + + " }\n" + + " }\n" + + "}"; +} diff --git a/server/implementation/src/test/java/io/smallrye/graphql/test/resolver/ExtendedApi.java b/server/implementation/src/test/java/io/smallrye/graphql/test/resolver/ExtendedApi.java new file mode 100644 index 000000000..e224c570e --- /dev/null +++ b/server/implementation/src/test/java/io/smallrye/graphql/test/resolver/ExtendedApi.java @@ -0,0 +1,25 @@ +package io.smallrye.graphql.test.resolver; + +import org.eclipse.microprofile.graphql.GraphQLApi; + +import io.smallrye.graphql.api.federation.Resolver; + +@GraphQLApi +public class ExtendedApi { + @Resolver + public ExtendedType extendedTypeById(String id) { + ExtendedType extendedType = new ExtendedType(); + extendedType.setId(id); + return extendedType; + } + + @Resolver + public ExtendedType extendedTypeByIdNameKey(String id, String name, String key) { + ExtendedType extendedType = new ExtendedType(); + extendedType.setId(id); + extendedType.setName(name); + extendedType.setKey(key); + extendedType.setValue(id + name + key); + return extendedType; + } +} diff --git a/server/implementation/src/test/java/io/smallrye/graphql/test/resolver/ExtendedType.java b/server/implementation/src/test/java/io/smallrye/graphql/test/resolver/ExtendedType.java new file mode 100644 index 000000000..0d183f209 --- /dev/null +++ b/server/implementation/src/test/java/io/smallrye/graphql/test/resolver/ExtendedType.java @@ -0,0 +1,55 @@ +package io.smallrye.graphql.test.resolver; + +import io.smallrye.graphql.api.federation.Extends; +import io.smallrye.graphql.api.federation.External; +import io.smallrye.graphql.api.federation.FieldSet; +import io.smallrye.graphql.api.federation.Key; +import io.smallrye.graphql.api.federation.Requires; + +@Extends +@Key(fields = @FieldSet("id")) +public class ExtendedType { + @External + private String id; + + @External + private String name; + + @External + private String key; + + @Requires(fields = @FieldSet("name key")) + private String value; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/tools/maven-plugin/src/main/java/io/smallrye/graphql/mavenplugin/GenerateSchemaMojo.java b/tools/maven-plugin/src/main/java/io/smallrye/graphql/mavenplugin/GenerateSchemaMojo.java index f6fb2633a..90d2ad4de 100644 --- a/tools/maven-plugin/src/main/java/io/smallrye/graphql/mavenplugin/GenerateSchemaMojo.java +++ b/tools/maven-plugin/src/main/java/io/smallrye/graphql/mavenplugin/GenerateSchemaMojo.java @@ -254,7 +254,9 @@ private String generateSchema(IndexView index, boolean enableFederation) { GraphQLSchema graphQLSchema = Bootstrap.bootstrap(internalSchema, true); if (graphQLSchema != null && enableFederation) { graphQLSchema = Federation.transform(graphQLSchema) - .fetchEntities(new FederationDataFetcher(graphQLSchema.getQueryType(), graphQLSchema.getCodeRegistry())) + .fetchEntities(new FederationDataFetcher( + GraphQLObjectType.newObject().name("Resolver").build(), + graphQLSchema.getQueryType(), graphQLSchema.getCodeRegistry())) .resolveEntityType(fetchEntityType()) .setFederation2(true) .build();