From f7b3582f71ca2c06933f44922716f609c5d11907 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Fri, 25 Jan 2019 22:47:17 -0800 Subject: [PATCH] feat: initial GraphQL Schema Autoconfiguration with merge --- graphql-jpa-query-autoconfigure/.classpath | 55 +++++++++++ graphql-jpa-query-autoconfigure/pom.xml | 25 +++++ .../GraphQLSchemaAutoConfiguration.java | 42 +++++++++ .../GraphQLSchemaConfigurer.java | 7 ++ .../GraphQLSchemaFactoryBean.java | 65 +++++++++++++ .../GraphQLShemaRegistration.java | 20 ++++ .../main/resources/META-INF/spring.factories | 2 + .../GraphQLSchemaAutoConfigurationTest.java | 94 +++++++++++++++++++ graphql-jpa-query-boot-starter/pom.xml | 5 + .../GraphQLJpaQueryAutoConfiguration.java | 37 ++++++-- graphql-jpa-query-dependencies/pom.xml | 5 + pom.xml | 1 + 12 files changed, 348 insertions(+), 10 deletions(-) create mode 100644 graphql-jpa-query-autoconfigure/.classpath create mode 100644 graphql-jpa-query-autoconfigure/pom.xml create mode 100644 graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfiguration.java create mode 100644 graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaConfigurer.java create mode 100644 graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaFactoryBean.java create mode 100644 graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLShemaRegistration.java create mode 100644 graphql-jpa-query-autoconfigure/src/main/resources/META-INF/spring.factories create mode 100644 graphql-jpa-query-autoconfigure/src/test/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfigurationTest.java diff --git a/graphql-jpa-query-autoconfigure/.classpath b/graphql-jpa-query-autoconfigure/.classpath new file mode 100644 index 000000000..0ca1374d8 --- /dev/null +++ b/graphql-jpa-query-autoconfigure/.classpath @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/graphql-jpa-query-autoconfigure/pom.xml b/graphql-jpa-query-autoconfigure/pom.xml new file mode 100644 index 000000000..a6b7e5412 --- /dev/null +++ b/graphql-jpa-query-autoconfigure/pom.xml @@ -0,0 +1,25 @@ + + 4.0.0 + + com.introproventures + graphql-jpa-query-build + 0.3.12-SNAPSHOT + ../graphql-jpa-query-build + + graphql-jpa-query-autoconfigure + + + + com.graphql-java + graphql-java + true + + + + org.springframework.boot + spring-boot-autoconfigure + + + + + \ No newline at end of file diff --git a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfiguration.java b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfiguration.java new file mode 100644 index 000000000..2842940b3 --- /dev/null +++ b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfiguration.java @@ -0,0 +1,42 @@ +package com.introproventures.graphql.jpa.query.autoconfigure; + +import java.util.ArrayList; +import java.util.List; + +import graphql.GraphQL; +import graphql.schema.GraphQLSchema; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.CollectionUtils; + +@Configuration +@ConditionalOnClass(GraphQL.class) +public class GraphQLSchemaAutoConfiguration { + + private final List graphQLSchemaConfigurers = new ArrayList<>(); + + @Autowired(required = true) + public void setGraphQLSchemaConfigurers(List configurers) { + if (!CollectionUtils.isEmpty(configurers)) { + graphQLSchemaConfigurers.addAll(configurers); + } + } + + @Bean + @ConditionalOnMissingBean(GraphQLSchema.class) + public GraphQLSchemaFactoryBean graphQLSchemaFactoryBean() { + GraphQLShemaRegistration graphQLShemaRegistration = new GraphQLShemaRegistration(); + + for (GraphQLSchemaConfigurer configurer : graphQLSchemaConfigurers) { + configurer.configure(graphQLShemaRegistration); + } + + return new GraphQLSchemaFactoryBean(graphQLShemaRegistration.getManagedGraphQLSchemas()); + + }; + + +} diff --git a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaConfigurer.java b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaConfigurer.java new file mode 100644 index 000000000..88226dd1c --- /dev/null +++ b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaConfigurer.java @@ -0,0 +1,7 @@ +package com.introproventures.graphql.jpa.query.autoconfigure; + +public interface GraphQLSchemaConfigurer { + + void configure(GraphQLShemaRegistration registry); + +} diff --git a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaFactoryBean.java b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaFactoryBean.java new file mode 100644 index 000000000..02499123b --- /dev/null +++ b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaFactoryBean.java @@ -0,0 +1,65 @@ +package com.introproventures.graphql.jpa.query.autoconfigure; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLSchema; +import org.springframework.beans.factory.config.AbstractFactoryBean; + +public class GraphQLSchemaFactoryBean extends AbstractFactoryBean{ + + private final GraphQLSchema[] managedGraphQLSchemas; + + public GraphQLSchemaFactoryBean(GraphQLSchema[] managedGraphQLSchemas) { + this.managedGraphQLSchemas = managedGraphQLSchemas; + } + + @Override + protected GraphQLSchema createInstance() throws Exception { + + GraphQLSchema.Builder schemaBuilder = GraphQLSchema.newSchema(); + + List mutations = Stream.of(managedGraphQLSchemas) + .map(GraphQLSchema::getMutationType) + .filter(Objects::nonNull) + .map(GraphQLObjectType::getFieldDefinitions) + .flatMap(children -> children.stream()) + .collect(Collectors.toList()); + + List queries = Stream.of(managedGraphQLSchemas) + .map(GraphQLSchema::getQueryType) + .filter(Objects::nonNull) + .filter(it -> !it.getName().equals("null")) // filter out null placeholders + .map(GraphQLObjectType::getFieldDefinitions) + .flatMap(children -> children.stream()) + .collect(Collectors.toList()); + + List subscriptions = Stream.of(managedGraphQLSchemas) + .map(GraphQLSchema::getSubscriptionType) + .filter(Objects::nonNull) + .map(GraphQLObjectType::getFieldDefinitions) + .flatMap(children -> children.stream()) + .collect(Collectors.toList()); + + if(!mutations.isEmpty()) + schemaBuilder.mutation(GraphQLObjectType.newObject().name("Mutation").fields(mutations)); + + if(!queries.isEmpty()) + schemaBuilder.query(GraphQLObjectType.newObject().name("Query").fields(queries)); + + if(!subscriptions.isEmpty()) + schemaBuilder.subscription(GraphQLObjectType.newObject().name("Subscription").fields(subscriptions)); + + return schemaBuilder.build(); + } + + @Override + public Class getObjectType() { + return GraphQLSchema.class; + } + +} diff --git a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLShemaRegistration.java b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLShemaRegistration.java new file mode 100644 index 000000000..b91e17632 --- /dev/null +++ b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLShemaRegistration.java @@ -0,0 +1,20 @@ +package com.introproventures.graphql.jpa.query.autoconfigure; + +import java.util.LinkedHashSet; +import java.util.Set; + +import graphql.schema.GraphQLSchema; + +public class GraphQLShemaRegistration { + + Set managedGraphQLSchemas = new LinkedHashSet(); + + public void register(GraphQLSchema graphQLSchema) { + managedGraphQLSchemas.add(graphQLSchema); + } + + public GraphQLSchema[] getManagedGraphQLSchemas() { + return managedGraphQLSchemas.toArray(new GraphQLSchema[] {}); + } + +} diff --git a/graphql-jpa-query-autoconfigure/src/main/resources/META-INF/spring.factories b/graphql-jpa-query-autoconfigure/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..ab8188c15 --- /dev/null +++ b/graphql-jpa-query-autoconfigure/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.introproventures.graphql.jpa.query.autoconfigure.GraphQLSchemaAutoConfiguration \ No newline at end of file diff --git a/graphql-jpa-query-autoconfigure/src/test/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfigurationTest.java b/graphql-jpa-query-autoconfigure/src/test/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfigurationTest.java new file mode 100644 index 000000000..09129b729 --- /dev/null +++ b/graphql-jpa-query-autoconfigure/src/test/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfigurationTest.java @@ -0,0 +1,94 @@ +package com.introproventures.graphql.jpa.query.autoconfigure; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; + +import graphql.GraphQL; +import graphql.Scalars; +import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLSchema; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.stereotype.Component; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment=WebEnvironment.NONE) +public class GraphQLSchemaAutoConfigurationTest { + + @Autowired + private GraphQLSchema graphQLSchema; + + @SpringBootApplication + static class Application { + + @Component + static class MutationGraphQLSchemaConfigurer implements GraphQLSchemaConfigurer { + + @Override + public void configure(GraphQLShemaRegistration registry) { + GraphQLObjectType mutation = GraphQLObjectType.newObject() + .name("mutation") + .field(GraphQLFieldDefinition.newFieldDefinition() + .name("greet") + .type(Scalars.GraphQLString) + .dataFetcher(environment -> { + return "hello world"; + })) + .build(); + + GraphQLSchema graphQLSchema = GraphQLSchema.newSchema() + .query(GraphQLObjectType.newObject().name("null")) + .mutation(mutation) + .build(); + + registry.register(graphQLSchema); + } + } + @Component + static class QueryGraphQLSchemaConfigurer implements GraphQLSchemaConfigurer { + + @Override + public void configure(GraphQLShemaRegistration registry) { + GraphQLObjectType query = GraphQLObjectType.newObject() + .name("query") + .field(GraphQLFieldDefinition.newFieldDefinition() + .name("hello") + .type(Scalars.GraphQLString) + .dataFetcher(environment -> { + return "world"; + })) + .build(); + + GraphQLSchema graphQLSchema = GraphQLSchema.newSchema() + .query(query) + .build(); + + registry.register(graphQLSchema); + } + } + } + + @Test + public void contextLoads() { + // given + GraphQL graphQL = GraphQL.newGraphQL(graphQLSchema).build(); + + // when + Map result = graphQL.execute("query {hello}").getData(); + Map result2 = graphQL.execute("mutation {greet}").getData(); + + // then + assertThat(result.toString()).isEqualTo("{hello=world}"); + assertThat(result2.toString()).isEqualTo("{greet=hello world}"); + } + + + +} diff --git a/graphql-jpa-query-boot-starter/pom.xml b/graphql-jpa-query-boot-starter/pom.xml index 73c76539b..1d8531915 100644 --- a/graphql-jpa-query-boot-starter/pom.xml +++ b/graphql-jpa-query-boot-starter/pom.xml @@ -18,6 +18,11 @@ graphql-jpa-query-schema + + com.introproventures + graphql-jpa-query-autoconfigure + + org.springframework.boot spring-boot-starter diff --git a/graphql-jpa-query-boot-starter/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryAutoConfiguration.java b/graphql-jpa-query-boot-starter/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryAutoConfiguration.java index 4169111ba..cdcdfac7d 100644 --- a/graphql-jpa-query-boot-starter/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryAutoConfiguration.java +++ b/graphql-jpa-query-boot-starter/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryAutoConfiguration.java @@ -17,6 +17,15 @@ import javax.persistence.EntityManager; +import com.introproventures.graphql.jpa.query.autoconfigure.GraphQLSchemaConfigurer; +import com.introproventures.graphql.jpa.query.autoconfigure.GraphQLShemaRegistration; +import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor; +import com.introproventures.graphql.jpa.query.schema.GraphQLSchemaBuilder; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; +import com.introproventures.graphql.jpa.query.web.GraphQLController; +import graphql.GraphQL; +import graphql.schema.GraphQLSchema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -30,19 +39,27 @@ import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.Assert; -import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor; -import com.introproventures.graphql.jpa.query.schema.GraphQLSchemaBuilder; -import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; -import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; -import com.introproventures.graphql.jpa.query.web.GraphQLController; - -import graphql.GraphQL; - @Configuration @PropertySource("classpath:/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties") @ConditionalOnClass(GraphQL.class) @ConditionalOnProperty(name="spring.graphql.jpa.query.enabled", havingValue="true", matchIfMissing=false) public class GraphQLJpaQueryAutoConfiguration { + + @Configuration + public static class GraphQLJpaQuerySchemaConfigurer implements GraphQLSchemaConfigurer { + + private final GraphQLSchemaBuilder graphQLSchemaBuilder; + + public GraphQLJpaQuerySchemaConfigurer(GraphQLSchemaBuilder graphQLSchemaBuilder) { + this.graphQLSchemaBuilder = graphQLSchemaBuilder; + } + + @Override + public void configure(GraphQLShemaRegistration registry) { + + registry.register(graphQLSchemaBuilder.build()); + } + } @Configuration @Import(GraphQLController.class) @@ -54,8 +71,8 @@ public static class DefaultActivitiGraphQLJpaConfiguration implements ImportAwar @Bean @ConditionalOnMissingBean(GraphQLExecutor.class) - public GraphQLExecutor graphQLExecutor(final GraphQLSchemaBuilder graphQLSchemaBuilder) { - return new GraphQLJpaExecutor(graphQLSchemaBuilder.build()); + public GraphQLExecutor graphQLExecutor(GraphQLSchema graphQLSchema) { + return new GraphQLJpaExecutor(graphQLSchema); } @Bean diff --git a/graphql-jpa-query-dependencies/pom.xml b/graphql-jpa-query-dependencies/pom.xml index 72d339143..800c254b0 100644 --- a/graphql-jpa-query-dependencies/pom.xml +++ b/graphql-jpa-query-dependencies/pom.xml @@ -54,6 +54,11 @@ graphql-jpa-query-boot-starter ${project.version} + + com.introproventures + graphql-jpa-query-autoconfigure + ${project.version} + diff --git a/pom.xml b/pom.xml index c75e83c62..082b7402f 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ graphql-jpa-query-example graphql-jpa-query-dependencies graphql-jpa-query-build + graphql-jpa-query-autoconfigure