From 795876a4dc574218cef9c8b6b7d18c5d31acb236 Mon Sep 17 00:00:00 2001 From: Jan Martiska Date: Fri, 15 Sep 2023 08:00:44 +0200 Subject: [PATCH 1/3] SmallRye GraphQL 2.5.0 and graphql-java 21.1 --- bom/application/pom.xml | 4 ++-- .../resources/overrides/invalidDataTypeValue/output3.json | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index c9ed27dcec944..932fd097fb628 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -38,7 +38,7 @@ 1.11.1 2.1.12 0.22.0 - 20.2 + 21.1 3.0.3 4.0.1 4.0.1 @@ -55,7 +55,7 @@ 4.0.4 4.0.0 3.6.2 - 2.4.0 + 2.5.0 3.0.3 6.2.6 4.3.1 diff --git a/tcks/microprofile-graphql/src/main/resources/overrides/invalidDataTypeValue/output3.json b/tcks/microprofile-graphql/src/main/resources/overrides/invalidDataTypeValue/output3.json index 5f9b23eab9939..336e4049da5e0 100644 --- a/tcks/microprofile-graphql/src/main/resources/overrides/invalidDataTypeValue/output3.json +++ b/tcks/microprofile-graphql/src/main/resources/overrides/invalidDataTypeValue/output3.json @@ -1,7 +1,7 @@ { "errors": [ { - "message": "argument 'powerLevel' with value 'StringValue{value='Unlimited'}' is not a valid 'Int'", + "message": "Validation error (WrongType@[updateItemPowerLevel]) : argument 'powerLevel' with value 'StringValue{value='Unlimited'}' is not a valid 'Int' - SRGQL000022: Can not parse a number from [StringValue{value='Unlimited'}]", "locations": [ { "line": 2, @@ -10,7 +10,5 @@ ] } ], - "data": { - "updateItemPowerLevel": null - } + "data": null } From 9f0437a9cf7ad2375dbdfa2c25d3a9815d1e72b8 Mon Sep 17 00:00:00 2001 From: Robert Pospisil Date: Wed, 13 Sep 2023 10:01:45 +0200 Subject: [PATCH 2/3] add config toggle --- .../graphql/deployment/SmallRyeGraphQLProcessor.java | 10 ++++++++++ .../graphql/runtime/SmallRyeGraphQLConfig.java | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java index 9f991d404a896..56af0e947c50d 100644 --- a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java +++ b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java @@ -652,6 +652,16 @@ void activateEventing(SmallRyeGraphQLConfig graphQLConfig, BuildProducer systemProperties) { + if (graphQLConfig.federationBatchResolvingEnabled.isPresent()) { + String value = graphQLConfig.federationBatchResolvingEnabled.get().toString(); + systemProperties.produce(new SystemPropertyBuildItem(ConfigKey.ENABLE_FEDERATION_BATCH_RESOLVING, value)); + System.setProperty(ConfigKey.ENABLE_FEDERATION_BATCH_RESOLVING, value); + } + } + /* * Decides whether we want to activate GraphQL federation and updates system properties accordingly. * If quarkus.smallrye-graphql.federation.enabled is unspecified, enable federation automatically if we see diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLConfig.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLConfig.java index 79ed56937f73f..1042bcb037f58 100644 --- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLConfig.java +++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLConfig.java @@ -27,6 +27,12 @@ public class SmallRyeGraphQLConfig { @ConfigItem(name = "federation.enabled") public Optional federationEnabled; + /** + * Enable batch resolving for federation. Disabled by default. + */ + @ConfigItem(name = "federation.batch-resolving-enabled") + public Optional federationBatchResolvingEnabled; + /** * Enable metrics. By default, this is false. If set to true, a metrics extension is required. */ From bba3785e72c2416519ce2a5fb1b79d72dd8a614c Mon Sep 17 00:00:00 2001 From: Robert Pospisil Date: Thu, 7 Sep 2023 15:27:08 +0200 Subject: [PATCH 3/3] add tests for federation batch resolving --- .../deployment/federation/batch/Foo.java | 16 ++++ .../deployment/federation/batch/FooApi.java | 22 +++++ .../batch/GraphQLFederationBatchTest.java | 80 ++++++++++++++++ .../federation/batch/uni/FooApiUni.java | 26 +++++ .../uni/GraphQLFederationBatchUniTest.java | 81 ++++++++++++++++ .../deployment/federation/complex/Bar.java | 19 ++++ .../deployment/federation/complex/Foo.java | 16 ++++ .../deployment/federation/complex/FooApi.java | 36 +++++++ .../complex/GraphQLFederationComplexTest.java | 96 +++++++++++++++++++ 9 files changed, 392 insertions(+) create mode 100644 extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/Foo.java create mode 100644 extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/FooApi.java create mode 100644 extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/GraphQLFederationBatchTest.java create mode 100644 extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/uni/FooApiUni.java create mode 100644 extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/uni/GraphQLFederationBatchUniTest.java create mode 100644 extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/Bar.java create mode 100644 extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/Foo.java create mode 100644 extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/FooApi.java create mode 100644 extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/GraphQLFederationComplexTest.java diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/Foo.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/Foo.java new file mode 100644 index 0000000000000..a29b125bf2710 --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/Foo.java @@ -0,0 +1,16 @@ +package io.quarkus.smallrye.graphql.deployment.federation.batch; + +import io.smallrye.graphql.api.federation.Extends; +import io.smallrye.graphql.api.federation.External; +import io.smallrye.graphql.api.federation.Key; + +@Key(fields = "id") +@Extends +public class Foo { + + @External + public int id; + + public String name; + +} diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/FooApi.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/FooApi.java new file mode 100644 index 0000000000000..3e9f498c88d7a --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/FooApi.java @@ -0,0 +1,22 @@ +package io.quarkus.smallrye.graphql.deployment.federation.batch; + +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Query; + +@GraphQLApi +public class FooApi { + @Query + public List foos(List id) { + return id.stream().map(this::foo).collect(Collectors.toList()); + } + + private Foo foo(int id) { + var foo = new Foo(); + foo.id = id; + foo.name = "Name of " + id; + return foo; + } +} diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/GraphQLFederationBatchTest.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/GraphQLFederationBatchTest.java new file mode 100644 index 0000000000000..03b4c608bd6b3 --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/GraphQLFederationBatchTest.java @@ -0,0 +1,80 @@ +package io.quarkus.smallrye.graphql.deployment.federation.batch; + +import static org.hamcrest.Matchers.containsString; + +import org.hamcrest.CoreMatchers; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.smallrye.graphql.deployment.AbstractGraphQLTest; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class GraphQLFederationBatchTest extends AbstractGraphQLTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(FooApi.class, Foo.class) + .addAsResource(new StringAsset("quarkus.smallrye-graphql.schema-include-directives=true\n" + + "quarkus.smallrye-graphql.federation.batch-resolving-enabled=true"), + "application.properties") + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")); + + @Test + public void checkServiceDeclarationInSchema() { + RestAssured.given() + .get("/graphql/schema.graphql") + .then() + .body(containsString("type _Service {")); + } + + @Test + public void checkFederationDirectivesInSchema() { + RestAssured.given() + .get("/graphql/schema.graphql") + .then() + .body(containsString("id: Int! @external")) + .body(containsString("type Foo @extends @key(fields : \"id\")")); + ; + } + + @Test + public void resolvePerFederation() { + String query = "query federation($representations: [_Any!]!) {\n" + + " _entities(representations: $representations) {\n" + + " ... on Foo {\n" + + " id\n" + + " name\n" + + " }\n" + + " }\n" + + "}"; + String variables = "{\n" + + " \"representations\": [\n" + + " {\n" + + " \"__typename\": \"Foo\",\n" + + " \"id\": 1\n" + + " },\n" + + " {\n" + + " \"__typename\": \"Foo\",\n" + + " \"id\": 2\n" + + " }\n" + + " ]\n" + + "}"; + + String request = getPayload(query, variables); + RestAssured.given().when() + .accept(MEDIATYPE_JSON) + .contentType(MEDIATYPE_JSON) + .body(request) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .body(CoreMatchers.is( + "{\"data\":{\"_entities\":[{\"id\":1,\"name\":\"Name of 1\"},{\"id\":2,\"name\":\"Name of 2\"}]}}")); + } +} diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/uni/FooApiUni.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/uni/FooApiUni.java new file mode 100644 index 0000000000000..e20992948b3ba --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/uni/FooApiUni.java @@ -0,0 +1,26 @@ +package io.quarkus.smallrye.graphql.deployment.federation.batch.uni; + +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Query; + +import io.quarkus.smallrye.graphql.deployment.federation.batch.Foo; +import io.smallrye.mutiny.Uni; + +@GraphQLApi +public class FooApiUni { + + @Query + public Uni> foos(List id) { + return Uni.join().all(id.stream().map(this::foo).collect(Collectors.toList())).andFailFast(); + } + + private Uni foo(int id) { + var foo = new Foo(); + foo.id = id; + foo.name = "Name of " + id; + return Uni.createFrom().item(foo); + } +} diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/uni/GraphQLFederationBatchUniTest.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/uni/GraphQLFederationBatchUniTest.java new file mode 100644 index 0000000000000..f91192188a2f3 --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/batch/uni/GraphQLFederationBatchUniTest.java @@ -0,0 +1,81 @@ +package io.quarkus.smallrye.graphql.deployment.federation.batch.uni; + +import static org.hamcrest.Matchers.containsString; + +import org.hamcrest.CoreMatchers; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.smallrye.graphql.deployment.AbstractGraphQLTest; +import io.quarkus.smallrye.graphql.deployment.federation.batch.Foo; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class GraphQLFederationBatchUniTest extends AbstractGraphQLTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(FooApiUni.class, Foo.class) + .addAsResource(new StringAsset("quarkus.smallrye-graphql.schema-include-directives=true\n" + + "quarkus.smallrye-graphql.federation.batch-resolving-enabled=true"), + "application.properties") + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")); + + @Test + public void checkServiceDeclarationInSchema() { + RestAssured.given() + .get("/graphql/schema.graphql") + .then() + .body(containsString("type _Service {")); + } + + @Test + public void checkFederationDirectivesInSchema() { + RestAssured.given() + .get("/graphql/schema.graphql") + .then() + .body(containsString("id: Int! @external")) + .body(containsString("type Foo @extends @key(fields : \"id\")")); + ; + } + + @Test + public void resolvePerFederation() { + String query = "query federation($representations: [_Any!]!) {\n" + + " _entities(representations: $representations) {\n" + + " ... on Foo {\n" + + " id\n" + + " name\n" + + " }\n" + + " }\n" + + "}"; + String variables = "{\n" + + " \"representations\": [\n" + + " {\n" + + " \"__typename\": \"Foo\",\n" + + " \"id\": 1\n" + + " },\n" + + " {\n" + + " \"__typename\": \"Foo\",\n" + + " \"id\": 2\n" + + " }\n" + + " ]\n" + + "}"; + + String request = getPayload(query, variables); + RestAssured.given().when() + .accept(MEDIATYPE_JSON) + .contentType(MEDIATYPE_JSON) + .body(request) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .body(CoreMatchers.is( + "{\"data\":{\"_entities\":[{\"id\":1,\"name\":\"Name of 1\"},{\"id\":2,\"name\":\"Name of 2\"}]}}")); + } +} diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/Bar.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/Bar.java new file mode 100644 index 0000000000000..86c15193df495 --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/Bar.java @@ -0,0 +1,19 @@ +package io.quarkus.smallrye.graphql.deployment.federation.complex; + +import io.smallrye.graphql.api.federation.Extends; +import io.smallrye.graphql.api.federation.External; +import io.smallrye.graphql.api.federation.Key; + +@Key(fields = "id") +@Key(fields = "otherId") +@Extends +public class Bar { + + @External + public int id; + @External + public String otherId; + + public String name; + +} diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/Foo.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/Foo.java new file mode 100644 index 0000000000000..3ebaa016a1129 --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/Foo.java @@ -0,0 +1,16 @@ +package io.quarkus.smallrye.graphql.deployment.federation.complex; + +import io.smallrye.graphql.api.federation.Extends; +import io.smallrye.graphql.api.federation.External; +import io.smallrye.graphql.api.federation.Key; + +@Key(fields = "id") +@Extends +public class Foo { + + @External + public int id; + + public String name; + +} diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/FooApi.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/FooApi.java new file mode 100644 index 0000000000000..24cd2594bd319 --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/FooApi.java @@ -0,0 +1,36 @@ +package io.quarkus.smallrye.graphql.deployment.federation.complex; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Query; + +import io.smallrye.mutiny.Uni; + +@GraphQLApi +public class FooApi { + @Query + public Foo foo(int id) { + var foo = new Foo(); + foo.id = id; + foo.name = "Name of " + id; + return foo; + } + + @Query + public Uni> bars(List id, List otherId) { + return Uni.join().all( + IntStream.range(0, id.size()).boxed().map(i -> bar(id.get(i), otherId.get(i))).collect(Collectors.toList())) + .andFailFast(); + } + + private Uni bar(int id, String otherId) { + var bar = new Bar(); + bar.id = id; + bar.otherId = otherId; + bar.name = id + otherId; + return Uni.createFrom().item(bar); + } +} diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/GraphQLFederationComplexTest.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/GraphQLFederationComplexTest.java new file mode 100644 index 0000000000000..9481279424f6d --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/federation/complex/GraphQLFederationComplexTest.java @@ -0,0 +1,96 @@ +package io.quarkus.smallrye.graphql.deployment.federation.complex; + +import static org.hamcrest.Matchers.containsString; + +import org.hamcrest.CoreMatchers; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.smallrye.graphql.deployment.AbstractGraphQLTest; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class GraphQLFederationComplexTest extends AbstractGraphQLTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(FooApi.class, Foo.class, Bar.class) + .addAsResource(new StringAsset("quarkus.smallrye-graphql.schema-include-directives=true\n" + + "quarkus.smallrye-graphql.federation.batch-resolving-enabled=true"), + "application.properties") + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")); + + @Test + public void checkServiceDeclarationInSchema() { + RestAssured.given() + .get("/graphql/schema.graphql") + .then() + .body(containsString("type _Service {")); + } + + @Test + public void checkFederationDirectivesInSchema() { + RestAssured.given() + .get("/graphql/schema.graphql") + .then() + .body(containsString("id: Int! @external")) + .body(containsString("type Foo @extends @key(fields : \"id\")")); + ; + } + + @Test + public void resolvePerFederation() { + String query = "query federation($representations: [_Any!]!) { \n" + + " _entities(representations: $representations) {\n" + + " ... on Foo {\n" + + " __typename\n" + + " id\n" + + " name\n" + + " }\n" + + " ... on Bar{\n" + + " __typename\n" + + " id\n" + + " otherId\n" + + " name\n" + + " }\n" + + " }\n" + + "}"; + String variables = "{ \n" + + " \"representations\": [ \n" + + " { \n" + + " \"__typename\": \"Bar\", \n" + + " \"id\": \"2\", \n" + + " \"otherId\": \"???\" \n" + + " }, \n" + + " { \n" + + " \"__typename\": \"Foo\", \n" + + " \"id\": 1\n" + + " }, \n" + + " { \n" + + " \"__typename\": \"Bar\", \n" + + " \"id\": \"3\", \n" + + " \"otherId\": \"!!!\" \n" + + " }, \n" + + " { \n" + + " \"__typename\": \"Foo\", \n" + + " \"id\": 0\n" + + " } \n" + + " ] \n" + + "}"; + String request = getPayload(query, variables); + RestAssured.given().when() + .accept(MEDIATYPE_JSON) + .contentType(MEDIATYPE_JSON) + .body(request) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .body(CoreMatchers.is( + "{\"data\":{\"_entities\":[{\"__typename\":\"Bar\",\"id\":2,\"otherId\":\"???\",\"name\":\"2???\"},{\"__typename\":\"Foo\",\"id\":1,\"name\":\"Name of 1\"},{\"__typename\":\"Bar\",\"id\":3,\"otherId\":\"!!!\",\"name\":\"3!!!\"},{\"__typename\":\"Foo\",\"id\":0,\"name\":\"Name of 0\"}]}}")); + } +}