From 48374b2682e6f7490153e271cf3667cd63cd5a3c Mon Sep 17 00:00:00 2001 From: "Sachin D. Shinde" Date: Wed, 14 Apr 2021 10:26:01 -0700 Subject: [PATCH 1/7] Upgrade graphql-spring-boot to 11.0.0 and spring-boot to 2.3.6 --- pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fefbc82c..217930c9 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ ${java.version} 2.7 16.1 - 5.10.0 + 11.0.0 1.8 17.0.0 5.4.2 @@ -72,7 +72,7 @@ 2.22.1 1.6.8 1.7.30 - 2.1.6.RELEASE + 2.3.6.RELEASE @@ -86,6 +86,7 @@ com.graphql-java-kickstart graphiql-spring-boot-starter ${graphql-spring-boot.version} + runtime com.graphql-java-kickstart From e69065f4be2b6505afad5a6016cb3788ab69e98b Mon Sep 17 00:00:00 2001 From: "Sachin D. Shinde" Date: Wed, 14 Apr 2021 19:35:35 -0700 Subject: [PATCH 2/7] Refactor Spring Boot example to be more similar to graphql-java-kickstart examples --- .../federation/springexample/App.java | 35 ++++++++++- .../InventorySchemaProvider.java | 61 ------------------- .../federation/springexample/Product.java | 26 ++++++++ 3 files changed, 59 insertions(+), 63 deletions(-) delete mode 100644 spring-example/src/main/java/com/apollographql/federation/springexample/InventorySchemaProvider.java diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/App.java b/spring-example/src/main/java/com/apollographql/federation/springexample/App.java index 0dcc4b29..a17b2a91 100644 --- a/spring-example/src/main/java/com/apollographql/federation/springexample/App.java +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/App.java @@ -1,10 +1,19 @@ package com.apollographql.federation.springexample; +import com.apollographql.federation.graphqljava.Federation; +import com.apollographql.federation.graphqljava._Entity; import com.apollographql.federation.graphqljava.tracing.FederatedTracingInstrumentation; -import graphql.execution.instrumentation.Instrumentation; +import graphql.schema.GraphQLSchema; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import org.springframework.core.io.Resource; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @SpringBootApplication public class App { @@ -13,7 +22,29 @@ public static void main(String[] args) { } @Bean - public Instrumentation addFederatedTracing() { + public GraphQLSchema graphQLSchema(@Value("classpath:schemas/inventory.graphql") Resource sdl) throws IOException { + return Federation.transform(sdl.getFile()) + .fetchEntities(env -> env.>>getArgument(_Entity.argumentName) + .stream() + .map(reference -> { + if ("Product".equals(reference.get("__typename"))) { + return Product.resolveReference(reference); + } + return null; + }) + .collect(Collectors.toList())) + .resolveEntityType(env -> { + final Object src = env.getObject(); + if (src instanceof Product) { + return env.getSchema().getObjectType("Product"); + } + return null; + }) + .build(); + } + + @Bean + public FederatedTracingInstrumentation federatedTracingInstrumentation() { return new FederatedTracingInstrumentation(new FederatedTracingInstrumentation.Options(true)); } } diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/InventorySchemaProvider.java b/spring-example/src/main/java/com/apollographql/federation/springexample/InventorySchemaProvider.java deleted file mode 100644 index d2c4665b..00000000 --- a/spring-example/src/main/java/com/apollographql/federation/springexample/InventorySchemaProvider.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.apollographql.federation.springexample; - -import com.apollographql.federation.graphqljava.Federation; -import com.apollographql.federation.graphqljava._Entity; -import graphql.servlet.config.DefaultGraphQLSchemaProvider; -import graphql.servlet.config.GraphQLSchemaProvider; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.Resource; -import org.springframework.stereotype.Component; - -import java.io.IOException; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -@Component -public class InventorySchemaProvider extends DefaultGraphQLSchemaProvider implements GraphQLSchemaProvider { - public InventorySchemaProvider(@Value("classpath:schemas/inventory.graphql") Resource sdl) throws IOException { - super(Federation.transform(sdl.getFile()) - .fetchEntities(env -> env.>>getArgument(_Entity.argumentName) - .stream() - .map(values -> { - if ("Product".equals(values.get("__typename"))) { - final Object upc = values.get("upc"); - if (upc instanceof String) { - return lookupProduct((String) upc); - } - } - return null; - }) - .collect(Collectors.toList())) - .resolveEntityType(env -> { - final Object src = env.getObject(); - if (src instanceof Product) { - return env.getSchema().getObjectType("Product"); - } - return null; - }) - .build()); - } - - @NotNull - private static Product lookupProduct(@NotNull String upc) { - try { - // Why not? - int quantity = Math.floorMod( - new BigInteger(1, - MessageDigest.getInstance("SHA1").digest(upc.getBytes()) - ).intValue(), - 10_000); - - return new Product(upc, quantity); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } -} diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/Product.java b/spring-example/src/main/java/com/apollographql/federation/springexample/Product.java index 7cb6b65f..18416221 100644 --- a/spring-example/src/main/java/com/apollographql/federation/springexample/Product.java +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/Product.java @@ -1,5 +1,12 @@ package com.apollographql.federation.springexample; +import org.jetbrains.annotations.NotNull; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Map; + public class Product { private final String upc; private final int quantity; @@ -20,4 +27,23 @@ public int getQuantity() { public boolean isInStock() { return this.quantity > 0; } + + public static Product resolveReference(@NotNull Map reference) { + if (!(reference.get("upc") instanceof String)) { + return null; + } + final String upc = (String) reference.get("upc"); + try { + // Why not? + int quantity = Math.floorMod( + new BigInteger(1, + MessageDigest.getInstance("SHA1").digest(upc.getBytes()) + ).intValue(), + 10_000); + + return new Product(upc, quantity); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } } From c996ec5058f3ae45c5c7e00543dc65f31cb87cd9 Mon Sep 17 00:00:00 2001 From: "Sachin D. Shinde" Date: Thu, 15 Apr 2021 00:27:40 -0700 Subject: [PATCH 3/7] Bump junit jupiter version to 5.7.1, and use aggregator artifact to fix issues with maven surefire plugin --- graphql-java-support/pom.xml | 6 +----- pom.xml | 10 ++-------- spring-example/pom.xml | 2 +- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/graphql-java-support/pom.xml b/graphql-java-support/pom.xml index bdca94a2..dd12602a 100644 --- a/graphql-java-support/pom.xml +++ b/graphql-java-support/pom.xml @@ -26,11 +26,7 @@ org.junit.jupiter - junit-jupiter-api - - - org.junit.jupiter - junit-jupiter-engine + junit-jupiter org.slf4j diff --git a/pom.xml b/pom.xml index 217930c9..aa0512f6 100644 --- a/pom.xml +++ b/pom.xml @@ -64,7 +64,7 @@ 11.0.0 1.8 17.0.0 - 5.4.2 + 5.7.1 1.6 3.1.2 3.1.1 @@ -107,13 +107,7 @@ org.junit.jupiter - junit-jupiter-api - ${junit.version} - test - - - org.junit.jupiter - junit-jupiter-engine + junit-jupiter ${junit.version} test diff --git a/spring-example/pom.xml b/spring-example/pom.xml index ea438a69..ca08058c 100644 --- a/spring-example/pom.xml +++ b/spring-example/pom.xml @@ -23,7 +23,7 @@ org.junit.jupiter - junit-jupiter-api + junit-jupiter org.springframework.boot From 12bea62a2c24d47b2130579cf78e0e556984f8f0 Mon Sep 17 00:00:00 2001 From: "Sachin D. Shinde" Date: Thu, 15 Apr 2021 01:10:06 -0700 Subject: [PATCH 4/7] Use @Import in AppTest to import configuration, and additionally check for GraphQL errors and federated tracing in the test --- .../apollographql/federation/springexample/AppTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spring-example/src/test/java/com/apollographql/federation/springexample/AppTest.java b/spring-example/src/test/java/com/apollographql/federation/springexample/AppTest.java index 3453a226..650a083f 100644 --- a/spring-example/src/test/java/com/apollographql/federation/springexample/AppTest.java +++ b/spring-example/src/test/java/com/apollographql/federation/springexample/AppTest.java @@ -6,6 +6,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; import org.springframework.test.context.junit4.SpringRunner; import java.io.IOException; @@ -16,6 +17,7 @@ @RunWith(SpringRunner.class) @GraphQLTest +@Import(App.class) public class AppTest { @Autowired private GraphQLTestTemplate graphqlTestTemplate; @@ -24,8 +26,11 @@ public class AppTest { public void lookupPlanckProduct() throws IOException { final GraphQLResponse response = graphqlTestTemplate.postForResource("queries/LookupPlanckProduct.graphql"); assertNotNull(response, "response should not have been null"); - assertTrue(response.isOk(), "response should have been OK"); + assertTrue(response.isOk(), "response should have been HTTP 200 OK"); + response.assertThatNoErrorsArePresent(); final int quantity = response.get("$.data._entities[0].quantity", Integer.class); assertEquals(8658, quantity, "Inventory contains 8658 Plancks"); + final String federatedTrace = response.get("$.extensions.ftv1", String.class); + assertNotNull(federatedTrace, "response should not have had null federated trace"); } } From fd05024f9b8c4588bf5f1105ff86c1b55350d66b Mon Sep 17 00:00:00 2001 From: "Sachin D. Shinde" Date: Thu, 15 Apr 2021 01:12:24 -0700 Subject: [PATCH 5/7] Add logback.xml to disable logging in spring-example test, as spring boot starter now declares logback and the maven surefire plugin doesn't like it when tests log to STDOUT --- spring-example/src/test/resources/logback.xml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 spring-example/src/test/resources/logback.xml diff --git a/spring-example/src/test/resources/logback.xml b/spring-example/src/test/resources/logback.xml new file mode 100644 index 00000000..215403c6 --- /dev/null +++ b/spring-example/src/test/resources/logback.xml @@ -0,0 +1,5 @@ + + + + + From 430d65c56949efe7d12f3e70a91bb05d2f589870 Mon Sep 17 00:00:00 2001 From: "Sachin D. Shinde" Date: Sat, 17 Apr 2021 23:17:07 -0700 Subject: [PATCH 6/7] Add graphql-java-tools example in spring-example (using separate spring profile) and update tests --- pom.xml | 13 ++-- spring-example/pom.xml | 4 ++ .../federation/springexample/App.java | 39 ------------ .../graphqljava/AppConfiguration.java | 47 +++++++++++++++ .../{ => graphqljava}/Product.java | 2 +- .../graphqljavatools/AppConfiguration.java | 44 ++++++++++++++ .../GraphQLJavaToolsConfiguration.java | 60 +++++++++++++++++++ .../graphqljavatools/Product.java | 19 ++++++ .../ProductReferenceResolver.java | 29 +++++++++ .../graphqljavatools/ProductResolver.java | 11 ++++ .../graphqljavatools/QueryResolver.java | 8 +++ .../src/main/resources/application.yml | 2 + .../graphql-java-tools/inventory.graphqls | 7 +++ .../{ => graphql-java}/inventory.graphql | 0 .../{AppTest.java => BaseAppTest.java} | 17 +----- .../springexample/GraphQLJavaAppTest.java | 24 ++++++++ .../GraphQLJavaToolsAppTest.java | 24 ++++++++ 17 files changed, 291 insertions(+), 59 deletions(-) create mode 100644 spring-example/src/main/java/com/apollographql/federation/springexample/graphqljava/AppConfiguration.java rename spring-example/src/main/java/com/apollographql/federation/springexample/{ => graphqljava}/Product.java (95%) create mode 100644 spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/AppConfiguration.java create mode 100644 spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/GraphQLJavaToolsConfiguration.java create mode 100644 spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/Product.java create mode 100644 spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/ProductReferenceResolver.java create mode 100644 spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/ProductResolver.java create mode 100644 spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/QueryResolver.java create mode 100644 spring-example/src/main/resources/schemas/graphql-java-tools/inventory.graphqls rename spring-example/src/main/resources/schemas/{ => graphql-java}/inventory.graphql (100%) rename spring-example/src/test/java/com/apollographql/federation/springexample/{AppTest.java => BaseAppTest.java} (67%) create mode 100644 spring-example/src/test/java/com/apollographql/federation/springexample/GraphQLJavaAppTest.java create mode 100644 spring-example/src/test/java/com/apollographql/federation/springexample/GraphQLJavaToolsAppTest.java diff --git a/pom.xml b/pom.xml index aa0512f6..f28d516e 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ ${java.version} 2.7 16.1 - 11.0.0 + 11.0.0 1.8 17.0.0 5.7.1 @@ -80,20 +80,25 @@ com.graphql-java-kickstart graphql-spring-boot-starter - ${graphql-spring-boot.version} + ${graphql-java-kickstart.version} com.graphql-java-kickstart graphiql-spring-boot-starter - ${graphql-spring-boot.version} + ${graphql-java-kickstart.version} runtime com.graphql-java-kickstart graphql-spring-boot-starter-test - ${graphql-spring-boot.version} + ${graphql-java-kickstart.version} test + + com.graphql-java-kickstart + graphql-java-tools + ${graphql-java-kickstart.version} + com.graphql-java graphql-java diff --git a/spring-example/pom.xml b/spring-example/pom.xml index ca08058c..51ca159d 100644 --- a/spring-example/pom.xml +++ b/spring-example/pom.xml @@ -49,6 +49,10 @@ com.graphql-java-kickstart graphql-spring-boot-starter-test + + com.graphql-java-kickstart + graphql-java-tools + diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/App.java b/spring-example/src/main/java/com/apollographql/federation/springexample/App.java index a17b2a91..0896b2c7 100644 --- a/spring-example/src/main/java/com/apollographql/federation/springexample/App.java +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/App.java @@ -1,50 +1,11 @@ package com.apollographql.federation.springexample; -import com.apollographql.federation.graphqljava.Federation; -import com.apollographql.federation.graphqljava._Entity; -import com.apollographql.federation.graphqljava.tracing.FederatedTracingInstrumentation; -import graphql.schema.GraphQLSchema; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.core.io.Resource; - -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } - - @Bean - public GraphQLSchema graphQLSchema(@Value("classpath:schemas/inventory.graphql") Resource sdl) throws IOException { - return Federation.transform(sdl.getFile()) - .fetchEntities(env -> env.>>getArgument(_Entity.argumentName) - .stream() - .map(reference -> { - if ("Product".equals(reference.get("__typename"))) { - return Product.resolveReference(reference); - } - return null; - }) - .collect(Collectors.toList())) - .resolveEntityType(env -> { - final Object src = env.getObject(); - if (src instanceof Product) { - return env.getSchema().getObjectType("Product"); - } - return null; - }) - .build(); - } - - @Bean - public FederatedTracingInstrumentation federatedTracingInstrumentation() { - return new FederatedTracingInstrumentation(new FederatedTracingInstrumentation.Options(true)); - } } diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljava/AppConfiguration.java b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljava/AppConfiguration.java new file mode 100644 index 00000000..786855b6 --- /dev/null +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljava/AppConfiguration.java @@ -0,0 +1,47 @@ +package com.apollographql.federation.springexample.graphqljava; + +import com.apollographql.federation.graphqljava.Federation; +import com.apollographql.federation.graphqljava._Entity; +import com.apollographql.federation.graphqljava.tracing.FederatedTracingInstrumentation; +import graphql.schema.GraphQLSchema; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.io.Resource; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Configuration +@Profile("graphql-java") +public class AppConfiguration { + @Bean + public GraphQLSchema graphQLSchema(@Value("classpath:schemas/graphql-java/inventory.graphql") Resource sdl) throws IOException { + return Federation.transform(sdl.getFile()) + .fetchEntities(env -> env.>>getArgument(_Entity.argumentName) + .stream() + .map(reference -> { + if ("Product".equals(reference.get("__typename"))) { + return Product.resolveReference(reference); + } + return null; + }) + .collect(Collectors.toList())) + .resolveEntityType(env -> { + final Object src = env.getObject(); + if (src instanceof Product) { + return env.getSchema().getObjectType("Product"); + } + return null; + }) + .build(); + } + + @Bean + public FederatedTracingInstrumentation federatedTracingInstrumentation() { + return new FederatedTracingInstrumentation(new FederatedTracingInstrumentation.Options(true)); + } +} diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/Product.java b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljava/Product.java similarity index 95% rename from spring-example/src/main/java/com/apollographql/federation/springexample/Product.java rename to spring-example/src/main/java/com/apollographql/federation/springexample/graphqljava/Product.java index 18416221..24dca16a 100644 --- a/spring-example/src/main/java/com/apollographql/federation/springexample/Product.java +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljava/Product.java @@ -1,4 +1,4 @@ -package com.apollographql.federation.springexample; +package com.apollographql.federation.springexample.graphqljava; import org.jetbrains.annotations.NotNull; diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/AppConfiguration.java b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/AppConfiguration.java new file mode 100644 index 00000000..2fca7c14 --- /dev/null +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/AppConfiguration.java @@ -0,0 +1,44 @@ +package com.apollographql.federation.springexample.graphqljavatools; + +import com.apollographql.federation.graphqljava.SchemaTransformer; +import com.apollographql.federation.graphqljava._Entity; +import com.apollographql.federation.graphqljava.tracing.FederatedTracingInstrumentation; +import graphql.schema.GraphQLSchema; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Configuration +@Profile("graphql-java-tools") +public class AppConfiguration { + @Bean + public GraphQLSchema graphQLSchema(SchemaTransformer schemaTransformer) { + return schemaTransformer + .fetchEntities(env -> env.>>getArgument(_Entity.argumentName) + .stream() + .map(reference -> { + if ("Product".equals(reference.get("__typename"))) { + return ProductReferenceResolver.resolveReference(reference); + } + return null; + }) + .collect(Collectors.toList())) + .resolveEntityType(env -> { + final Object src = env.getObject(); + if (src instanceof Product) { + return env.getSchema().getObjectType("Product"); + } + return null; + }) + .build(); + } + + @Bean + public FederatedTracingInstrumentation federatedTracingInstrumentation() { + return new FederatedTracingInstrumentation(new FederatedTracingInstrumentation.Options(true)); + } +} diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/GraphQLJavaToolsConfiguration.java b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/GraphQLJavaToolsConfiguration.java new file mode 100644 index 00000000..5921866f --- /dev/null +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/GraphQLJavaToolsConfiguration.java @@ -0,0 +1,60 @@ +package com.apollographql.federation.springexample.graphqljavatools; + +import com.apollographql.federation.graphqljava.Federation; +import com.apollographql.federation.graphqljava.SchemaTransformer; +import graphql.Scalars; +import graphql.kickstart.tools.SchemaObjects; +import graphql.kickstart.tools.SchemaParser; +import graphql.kickstart.tools.SchemaParserDictionary; +import graphql.kickstart.tools.SchemaParserOptions; +import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLSchema; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class GraphQLJavaToolsConfiguration { + @Bean + public SchemaParserDictionary schemaParserDictionary() { + return new SchemaParserDictionary().add("Product", Product.class); + } + + @Bean + public BeanPostProcessor schemaParserOptionsBuilderPostProcessor() { + return new BeanPostProcessor() { + @Override + public Object postProcessAfterInitialization(@NotNull Object bean, @NotNull String beanName) throws BeansException { + return bean instanceof SchemaParserOptions.Builder + ? ((SchemaParserOptions.Builder) bean).includeUnusedTypes(true) + : bean; + } + }; + } + + @Bean + public SchemaTransformer schemaTransformer(SchemaParser schemaParser) { + final SchemaObjects schemaObjects = schemaParser.parseSchemaObjects(); + final boolean queryTypeIsEmpty = schemaObjects.getQuery().getFieldDefinitions().isEmpty(); + final GraphQLObjectType newQuery = queryTypeIsEmpty + ? schemaObjects.getQuery().transform(graphQLObjectTypeBuilder -> + graphQLObjectTypeBuilder.field(GraphQLFieldDefinition.newFieldDefinition() + .name("_dummy") + .type(Scalars.GraphQLString) + .build() + ) + ) + : schemaObjects.getQuery(); + final GraphQLSchema graphQLSchema = GraphQLSchema.newSchema() + .query(newQuery) + .mutation(schemaObjects.getMutation()) + .subscription(schemaObjects.getSubscription()) + .additionalTypes(schemaObjects.getDictionary()) + .codeRegistry(schemaObjects.getCodeRegistryBuilder().build()) + .build(); + return Federation.transform(graphQLSchema, queryTypeIsEmpty); + } +} diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/Product.java b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/Product.java new file mode 100644 index 00000000..8b9f2c32 --- /dev/null +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/Product.java @@ -0,0 +1,19 @@ +package com.apollographql.federation.springexample.graphqljavatools; + +public class Product { + private final String upc; + private final int quantity; + + public Product(String upc, int quantity) { + this.upc = upc; + this.quantity = quantity; + } + + public String getUpc() { + return upc; + } + + public int getQuantity() { + return quantity; + } +} diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/ProductReferenceResolver.java b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/ProductReferenceResolver.java new file mode 100644 index 00000000..0c44e6a0 --- /dev/null +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/ProductReferenceResolver.java @@ -0,0 +1,29 @@ +package com.apollographql.federation.springexample.graphqljavatools; + +import org.jetbrains.annotations.NotNull; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Map; + +public class ProductReferenceResolver { + public static Product resolveReference(@NotNull Map reference) { + if (!(reference.get("upc") instanceof String)) { + return null; + } + final String upc = (String) reference.get("upc"); + try { + // Why not? + int quantity = Math.floorMod( + new BigInteger(1, + MessageDigest.getInstance("SHA1").digest(upc.getBytes()) + ).intValue(), + 10_000); + + return new Product(upc, quantity); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } +} diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/ProductResolver.java b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/ProductResolver.java new file mode 100644 index 00000000..50200517 --- /dev/null +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/ProductResolver.java @@ -0,0 +1,11 @@ +package com.apollographql.federation.springexample.graphqljavatools; + +import graphql.kickstart.tools.GraphQLResolver; +import org.springframework.stereotype.Component; + +@Component +public class ProductResolver implements GraphQLResolver { + public boolean isInStock(Product product) { + return product.getQuantity() > 0; + } +} diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/QueryResolver.java b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/QueryResolver.java new file mode 100644 index 00000000..a991e9bf --- /dev/null +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/QueryResolver.java @@ -0,0 +1,8 @@ +package com.apollographql.federation.springexample.graphqljavatools; + +import graphql.kickstart.tools.GraphQLQueryResolver; +import org.springframework.stereotype.Component; + +@Component +public class QueryResolver implements GraphQLQueryResolver { +} diff --git a/spring-example/src/main/resources/application.yml b/spring-example/src/main/resources/application.yml index fe41530c..4a54964e 100644 --- a/spring-example/src/main/resources/application.yml +++ b/spring-example/src/main/resources/application.yml @@ -1,5 +1,7 @@ spring: application: name: federation-jvm-spring-example + profiles: + default: graphql-java server: port: 9000 diff --git a/spring-example/src/main/resources/schemas/graphql-java-tools/inventory.graphqls b/spring-example/src/main/resources/schemas/graphql-java-tools/inventory.graphqls new file mode 100644 index 00000000..6e6e616c --- /dev/null +++ b/spring-example/src/main/resources/schemas/graphql-java-tools/inventory.graphqls @@ -0,0 +1,7 @@ +type Query + +type Product @key(fields: "upc") @extends { + upc: String! @external + inStock: Boolean + quantity: Int +} diff --git a/spring-example/src/main/resources/schemas/inventory.graphql b/spring-example/src/main/resources/schemas/graphql-java/inventory.graphql similarity index 100% rename from spring-example/src/main/resources/schemas/inventory.graphql rename to spring-example/src/main/resources/schemas/graphql-java/inventory.graphql diff --git a/spring-example/src/test/java/com/apollographql/federation/springexample/AppTest.java b/spring-example/src/test/java/com/apollographql/federation/springexample/BaseAppTest.java similarity index 67% rename from spring-example/src/test/java/com/apollographql/federation/springexample/AppTest.java rename to spring-example/src/test/java/com/apollographql/federation/springexample/BaseAppTest.java index 650a083f..94432e69 100644 --- a/spring-example/src/test/java/com/apollographql/federation/springexample/AppTest.java +++ b/spring-example/src/test/java/com/apollographql/federation/springexample/BaseAppTest.java @@ -1,13 +1,7 @@ package com.apollographql.federation.springexample; import com.graphql.spring.boot.test.GraphQLResponse; -import com.graphql.spring.boot.test.GraphQLTest; import com.graphql.spring.boot.test.GraphQLTestTemplate; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit4.SpringRunner; import java.io.IOException; @@ -15,15 +9,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -@RunWith(SpringRunner.class) -@GraphQLTest -@Import(App.class) -public class AppTest { - @Autowired - private GraphQLTestTemplate graphqlTestTemplate; - - @Test - public void lookupPlanckProduct() throws IOException { +public class BaseAppTest { + public void lookupPlanckProduct(GraphQLTestTemplate graphqlTestTemplate) throws IOException { final GraphQLResponse response = graphqlTestTemplate.postForResource("queries/LookupPlanckProduct.graphql"); assertNotNull(response, "response should not have been null"); assertTrue(response.isOk(), "response should have been HTTP 200 OK"); diff --git a/spring-example/src/test/java/com/apollographql/federation/springexample/GraphQLJavaAppTest.java b/spring-example/src/test/java/com/apollographql/federation/springexample/GraphQLJavaAppTest.java new file mode 100644 index 00000000..2fb7ea69 --- /dev/null +++ b/spring-example/src/test/java/com/apollographql/federation/springexample/GraphQLJavaAppTest.java @@ -0,0 +1,24 @@ +package com.apollographql.federation.springexample; + +import com.graphql.spring.boot.test.GraphQLTest; +import com.graphql.spring.boot.test.GraphQLTestTemplate; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +@RunWith(SpringRunner.class) +@GraphQLTest(profiles = "graphql-java") +@Import(App.class) +public class GraphQLJavaAppTest extends BaseAppTest { + @Autowired + private GraphQLTestTemplate graphqlTestTemplate; + + @Test + public void lookupPlanckProduct() throws IOException { + lookupPlanckProduct(graphqlTestTemplate); + } +} diff --git a/spring-example/src/test/java/com/apollographql/federation/springexample/GraphQLJavaToolsAppTest.java b/spring-example/src/test/java/com/apollographql/federation/springexample/GraphQLJavaToolsAppTest.java new file mode 100644 index 00000000..82879f07 --- /dev/null +++ b/spring-example/src/test/java/com/apollographql/federation/springexample/GraphQLJavaToolsAppTest.java @@ -0,0 +1,24 @@ +package com.apollographql.federation.springexample; + +import com.graphql.spring.boot.test.GraphQLTest; +import com.graphql.spring.boot.test.GraphQLTestTemplate; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +@RunWith(SpringRunner.class) +@GraphQLTest(profiles = "graphql-java-tools") +@Import(App.class) +public class GraphQLJavaToolsAppTest extends BaseAppTest { + @Autowired + private GraphQLTestTemplate graphqlTestTemplate; + + @Test + public void lookupPlanckProduct() throws IOException { + lookupPlanckProduct(graphqlTestTemplate); + } +} From 9489327dc636afdeeb6da6bc03173db82dc19846 Mon Sep 17 00:00:00 2001 From: "Sachin D. Shinde" Date: Sat, 17 Apr 2021 23:52:17 -0700 Subject: [PATCH 7/7] Update documentation for new graphql-java-tools example --- spring-example/README.md | 7 ++++++- .../GraphQLJavaToolsConfiguration.java | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/spring-example/README.md b/spring-example/README.md index 7d71e38f..33df17af 100644 --- a/spring-example/README.md +++ b/spring-example/README.md @@ -1,11 +1,16 @@ +This Spring Boot application contains examples for both a microservice using standard `graphql-java` and a microservice using `graphql-java-tools`; their code is separated under different subpackages and Spring profiles. -To run the example, cd to the root of this project, then... +To run the standard `graphql-java` example, `cd` to the root of this project, then... ``` ## compile and install project including example and dependencies mvn install -Dgpg.skip ## start local webserver on 9000 mvn -pl spring-example spring-boot:run ``` +To run the `graphql-java-tools` example, for the last step instead run: +``` +mvn -pl spring-example spring-boot:run -Dspring-boot.run.profiles=graphql-java-tools +``` Now you can query your local graph: ``` ## e.g. diff --git a/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/GraphQLJavaToolsConfiguration.java b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/GraphQLJavaToolsConfiguration.java index 5921866f..3893d8af 100644 --- a/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/GraphQLJavaToolsConfiguration.java +++ b/spring-example/src/main/java/com/apollographql/federation/springexample/graphqljavatools/GraphQLJavaToolsConfiguration.java @@ -16,6 +16,25 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +/** + * Some tips for running with graphql-java-tools: + * 1. You may find that graphql-java-tools removes types that are only used by the federation + * _entities field. This is because graphql-java-tools performs these optimizations before + * federation-graphql-java-support can alter the schema to include the _entities field. The + * simplest workaround here is to both (1) add the type to the {@link SchemaParserDictionary} + * bean and to (2) set the {@link SchemaParserOptions.Builder} bean to have includeUnusedTypes + * as true. However, the latter can't be done via properties.xml/yml since the getters/setters + * aren't named according to the JavaBeans API specification (i.e. getFoo()/setFoo()). You can + * work around this by providing the whole bean via code, or using a {@link BeanPostProcessor} + * as shown below to customize the auto-configuration bean. + * 2. If you have an empty query type, you don't need to add a dummy field to your + * {@link graphql.kickstart.tools.GraphQLQueryResolver} or to your schema string. However, you + * do need to (1) declare an empty {@link graphql.kickstart.tools.GraphQLQueryResolver}, (2) + * declare an empty "type Query" in your schema string, and (3) setup {@link SchemaTransformer} + * as shown below. We could potentially modify the federation-graphql-java-support API to accept + * {@link GraphQLSchema.Builder}s, but unfortunately those builders have no public getters, so + * we wouldn't be able to copy their data. + */ @Configuration public class GraphQLJavaToolsConfiguration { @Bean