From fec6682798484703b1aead37cb6ddca2da5bb629 Mon Sep 17 00:00:00 2001 From: wangyuheng Date: Sat, 23 Jan 2021 16:27:42 +0800 Subject: [PATCH 1/2] multi schema --- .../arc/graphql/GraphQLProvider.java | 60 ++++++++++++------- .../arc/graphql/GraphqlAutoConfiguration.java | 2 +- .../arc/graphql/GraphQLProviderTest.java | 46 ++++++++------ .../resources/graphql/repeat-schema.graphqls | 8 +++ .../resources/graphql/schema-partA.graphqls | 8 +++ .../test/resources/graphql/schema.graphqls | 11 +--- .../src/main/resources/application-dev.yml | 5 +- .../main/resources/graphql/project.graphqls | 54 +++++++++++++++++ .../main/resources/graphql/schema.graphqls | 58 ------------------ .../src/main/resources/graphql/user.graphqls | 3 + 10 files changed, 148 insertions(+), 107 deletions(-) create mode 100644 graphql/src/test/resources/graphql/repeat-schema.graphqls create mode 100644 graphql/src/test/resources/graphql/schema-partA.graphqls create mode 100644 sample/graphql-sample/src/main/resources/graphql/project.graphqls create mode 100644 sample/graphql-sample/src/main/resources/graphql/user.graphqls diff --git a/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphQLProvider.java b/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphQLProvider.java index 649bc6bb..400a187f 100644 --- a/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphQLProvider.java +++ b/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphQLProvider.java @@ -9,10 +9,12 @@ import graphql.schema.idl.SchemaGenerator; import graphql.schema.idl.SchemaParser; import graphql.schema.idl.TypeDefinitionRegistry; +import graphql.schema.idl.errors.SchemaProblem; import org.slf4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import java.io.IOException; @@ -22,13 +24,21 @@ * @author yuheng.wang * @see GraphQL */ -public class GraphQLProvider { +public class GraphQLProvider implements InitializingBean { private static final Logger log = org.slf4j.LoggerFactory.getLogger(GraphQLProvider.class); @Value("${arc.graphql.define:graphql/schema.graphqls}") - private ClassPathResource schema; - @Autowired(required = false) + private String locationPattern; + + private PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + private TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry(); + private DataFetcherInterceptorRegistry dataFetcherInterceptorRegistry; + + public GraphQLProvider(DataFetcherInterceptorRegistry dataFetcherInterceptorRegistry) { + this.dataFetcherInterceptorRegistry = dataFetcherInterceptorRegistry; + } + private GraphQL graphQL; public GraphQL getGraphQL() { @@ -40,28 +50,38 @@ public GraphQL getGraphQL() { private synchronized void refresh() { if (null == graphQL) { - this.initGraphQL(); + GraphQLSchema graphQLSchema = new SchemaGenerator() + .makeExecutableSchema(typeRegistry, RuntimeWiringRegistry.initRuntimeWiring(typeRegistry, dataFetcherInterceptorRegistry)); + this.graphQL = GraphQL + .newGraphQL(graphQLSchema) + .queryExecutionStrategy(new AsyncExecutionStrategy(new CustomDataFetcherExceptionHandler())) + .mutationExecutionStrategy(new AsyncExecutionStrategy(new CustomDataFetcherExceptionHandler())) + .build() + ; } } - private void initGraphQL() { + private void loadSchema() { log.info("init graphql"); - TypeDefinitionRegistry typeRegistry; + SchemaParser schemaParser = new SchemaParser(); try { - typeRegistry = new SchemaParser().parse(schema.getInputStream()); + Resource[] resources = resourcePatternResolver.getResources(locationPattern); + for (Resource resource : resources) { + typeRegistry.merge(schemaParser.parse(resource.getInputStream())); + } + } catch (SchemaProblem schemaProblem) { + log.error("schema defined error! locationPattern:{}", locationPattern, schemaProblem); + throw schemaProblem; } catch (IOException e) { - log.error("read graphql schema fail!", e); - throw new IllegalStateException("read graphql schema fail! path: " + schema.getPath()); + log.warn("read graphql schema fail!", e); + } + if (typeRegistry.types().isEmpty()) { + throw new IllegalStateException("read graphql schema fail! locationPattern: " + locationPattern); } - - GraphQLSchema graphQLSchema = new SchemaGenerator() - .makeExecutableSchema(typeRegistry, RuntimeWiringRegistry.initRuntimeWiring(typeRegistry, dataFetcherInterceptorRegistry)); - this.graphQL = GraphQL - .newGraphQL(graphQLSchema) - .queryExecutionStrategy(new AsyncExecutionStrategy(new CustomDataFetcherExceptionHandler())) - .mutationExecutionStrategy(new AsyncExecutionStrategy(new CustomDataFetcherExceptionHandler())) - .build() - ; } + @Override + public void afterPropertiesSet() throws Exception { + this.loadSchema(); + } } diff --git a/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphqlAutoConfiguration.java b/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphqlAutoConfiguration.java index 979bf9e5..2c8ac859 100644 --- a/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphqlAutoConfiguration.java +++ b/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphqlAutoConfiguration.java @@ -32,7 +32,7 @@ public DirectivePostProcessor directivePostProcessor() { @Bean @ConditionalOnMissingBean public GraphQLProvider graphQLProvider() { - return new GraphQLProvider(); + return new GraphQLProvider(dataFetcherInterceptorRegistry()); } @Bean diff --git a/graphql/src/test/java/com/github/yituhealthcare/arc/graphql/GraphQLProviderTest.java b/graphql/src/test/java/com/github/yituhealthcare/arc/graphql/GraphQLProviderTest.java index 13c64aa9..b6f4ce8b 100644 --- a/graphql/src/test/java/com/github/yituhealthcare/arc/graphql/GraphQLProviderTest.java +++ b/graphql/src/test/java/com/github/yituhealthcare/arc/graphql/GraphQLProviderTest.java @@ -1,6 +1,9 @@ package com.github.yituhealthcare.arc.graphql; +import com.github.yituhealthcare.arc.graphql.interceptor.DataFetcherInterceptorRegistry; import graphql.GraphQL; +import graphql.schema.idl.errors.SchemaProblem; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -9,7 +12,6 @@ import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import org.springframework.core.io.ClassPathResource; import static org.junit.Assert.assertNotNull; @@ -26,35 +28,45 @@ public class GraphQLProviderTest { @Rule public ExpectedException thrown = ExpectedException.none(); - @Test - public void graphQL_must_not_null() throws IllegalAccessException { - GraphQLProvider mock = PowerMockito.spy(new GraphQLProvider()); - PowerMockito.field(GraphQLProvider.class, "schema").set(mock, new ClassPathResource("graphql/schema.graphqls")); - assertNotNull(mock.getGraphQL()); + private GraphQLProvider graphQLProvider; + + @Before + public void setUp() { + graphQLProvider = new GraphQLProvider(new DataFetcherInterceptorRegistry()); } @Test - public void should_not_init_if_graphQL_existed() throws Exception { - GraphQLProvider mock = PowerMockito.spy(new GraphQLProvider()); - PowerMockito.field(GraphQLProvider.class, "schema").set(mock, new ClassPathResource("graphql/schema.graphqls")); - PowerMockito.verifyPrivate(mock, new Times(0)).invoke("initGraphQL"); + public void graphQL_must_not_null() throws Exception { + GraphQLProvider mock = PowerMockito.spy(graphQLProvider); + PowerMockito.field(GraphQLProvider.class, "locationPattern").set(mock, "graphql/s*.graphqls"); + mock.afterPropertiesSet(); + assertNotNull(mock.getGraphQL()); } @Test - public void should_init_once_if_not_graphQL_not_existed() throws Exception { - GraphQLProvider mock = PowerMockito.spy(new GraphQLProvider()); - PowerMockito.field(GraphQLProvider.class, "schema").set(mock, new ClassPathResource("graphql/schema.graphqls")); + public void should_init_once_if_schema_existed() throws Exception { + GraphQLProvider mock = PowerMockito.spy(graphQLProvider); + PowerMockito.field(GraphQLProvider.class, "locationPattern").set(mock, "graphql/schema*.graphqls"); + mock.afterPropertiesSet(); GraphQL graphQL = mock.getGraphQL(); assertNotNull(graphQL); - PowerMockito.verifyPrivate(mock, new Times(1)).invoke("initGraphQL"); + PowerMockito.verifyPrivate(mock, new Times(1)).invoke("loadSchema"); } @Test public void should_throw_illegal_state_if_schema_not_existed() throws Exception { - GraphQLProvider mock = new GraphQLProvider(); - PowerMockito.field(GraphQLProvider.class, "schema").set(mock, new ClassPathResource("graphql/not_existed_schema.graphqls")); + GraphQLProvider mock = PowerMockito.spy(graphQLProvider); + PowerMockito.field(GraphQLProvider.class, "locationPattern").set(mock, "graphql/not_existed_schema.graphqls"); thrown.expect(IllegalStateException.class); - mock.getGraphQL(); + mock.afterPropertiesSet(); + } + + @Test + public void should_throw_illegal_state_if_schema_type_repeat() throws Exception { + GraphQLProvider mock = PowerMockito.spy(graphQLProvider); + PowerMockito.field(GraphQLProvider.class, "locationPattern").set(mock, "graphql/*.graphqls"); + thrown.expect(SchemaProblem.class); + mock.afterPropertiesSet(); } } \ No newline at end of file diff --git a/graphql/src/test/resources/graphql/repeat-schema.graphqls b/graphql/src/test/resources/graphql/repeat-schema.graphqls new file mode 100644 index 00000000..92d1871f --- /dev/null +++ b/graphql/src/test/resources/graphql/repeat-schema.graphqls @@ -0,0 +1,8 @@ +type Mock{ + id: String! + name: String! +} + +input MockInput{ + name: String! +} diff --git a/graphql/src/test/resources/graphql/schema-partA.graphqls b/graphql/src/test/resources/graphql/schema-partA.graphqls new file mode 100644 index 00000000..92d1871f --- /dev/null +++ b/graphql/src/test/resources/graphql/schema-partA.graphqls @@ -0,0 +1,8 @@ +type Mock{ + id: String! + name: String! +} + +input MockInput{ + name: String! +} diff --git a/graphql/src/test/resources/graphql/schema.graphqls b/graphql/src/test/resources/graphql/schema.graphqls index 4f506478..345f9790 100644 --- a/graphql/src/test/resources/graphql/schema.graphqls +++ b/graphql/src/test/resources/graphql/schema.graphqls @@ -13,13 +13,4 @@ type Mutation{ createMock( payload: MockInput ): Mock -} - -type Mock{ - id: String! - name: String! -} - -input MockInput{ - name: String! -} +} \ No newline at end of file diff --git a/sample/graphql-sample/src/main/resources/application-dev.yml b/sample/graphql-sample/src/main/resources/application-dev.yml index 299e9d32..730047bc 100644 --- a/sample/graphql-sample/src/main/resources/application-dev.yml +++ b/sample/graphql-sample/src/main/resources/application-dev.yml @@ -11,4 +11,7 @@ spring: # sampler: # probability: 1 server: - port: 8401 \ No newline at end of file + port: 8401 +arc: + graphql: + define: graphql/*.graphqls \ No newline at end of file diff --git a/sample/graphql-sample/src/main/resources/graphql/project.graphqls b/sample/graphql-sample/src/main/resources/graphql/project.graphqls new file mode 100644 index 00000000..f3d4b02d --- /dev/null +++ b/sample/graphql-sample/src/main/resources/graphql/project.graphqls @@ -0,0 +1,54 @@ + +""" +名称 +为了达到某个产品迭代、产品模块开发、或者科研调研等目的所做的工作. +""" +type Project{ + id: String! + name: String! + description: String! + createTime: DateTime! + milestones( + status: MilestoneStatus + ): [Milestone] +} +""" +里程碑 +表述一个Project的某个时间阶段及阶段性目标. 一个Project可以同时拥有多个处于相同或者不同阶段的Milestone. +""" +type Milestone{ + id: String! + name: String! + description: String! + status: MilestoneStatus + creator: User +} + +""" +里程碑状态 +""" +enum MilestoneStatus{ + """ + 未开始 + """ + NOT_STARTED, + """ + 进行中 + """ + DOING, + """ + 发布 + """ + RELEASE, + """ + 关闭 + """ + CLOSE +} + +input ProjectInput{ + name: String! @Size(min : 3, max : 100) + description: String! + dsl: String! + vendorBranches: [String!]! +} diff --git a/sample/graphql-sample/src/main/resources/graphql/schema.graphqls b/sample/graphql-sample/src/main/resources/graphql/schema.graphqls index 9f9cbf26..b73c96dc 100644 --- a/sample/graphql-sample/src/main/resources/graphql/schema.graphqls +++ b/sample/graphql-sample/src/main/resources/graphql/schema.graphqls @@ -21,61 +21,3 @@ type Mutation{ payload: ProjectInput ): Project @Auth(role : "admin") } - -""" -名称 -为了达到某个产品迭代、产品模块开发、或者科研调研等目的所做的工作. -""" -type Project{ - id: String! - name: String! - description: String! - createTime: DateTime! - milestones( - status: MilestoneStatus - ): [Milestone] -} -""" -里程碑 -表述一个Project的某个时间阶段及阶段性目标. 一个Project可以同时拥有多个处于相同或者不同阶段的Milestone. -""" -type Milestone{ - id: String! - name: String! - description: String! - status: MilestoneStatus - creator: User -} - -type User { - name: String! -} - -""" -里程碑状态 -""" -enum MilestoneStatus{ - """ - 未开始 - """ - NOT_STARTED, - """ - 进行中 - """ - DOING, - """ - 发布 - """ - RELEASE, - """ - 关闭 - """ - CLOSE -} - -input ProjectInput{ - name: String! @Size(min : 3, max : 100) - description: String! - dsl: String! - vendorBranches: [String!]! -} diff --git a/sample/graphql-sample/src/main/resources/graphql/user.graphqls b/sample/graphql-sample/src/main/resources/graphql/user.graphqls new file mode 100644 index 00000000..4c0af5f5 --- /dev/null +++ b/sample/graphql-sample/src/main/resources/graphql/user.graphqls @@ -0,0 +1,3 @@ +type User { + name: String! +} \ No newline at end of file From 6117f134f393534e8f9fc9f33585fb6d9f9b601f Mon Sep 17 00:00:00 2001 From: wangyuheng Date: Mon, 25 Jan 2021 22:07:17 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=98=8E=E7=A1=AE=20final=20=E5=8F=98?= =?UTF-8?q?=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../github/yituhealthcare/arc/graphql/GraphQLProvider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphQLProvider.java b/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphQLProvider.java index 400a187f..22d0ed46 100644 --- a/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphQLProvider.java +++ b/graphql/src/main/java/com/github/yituhealthcare/arc/graphql/GraphQLProvider.java @@ -30,10 +30,10 @@ public class GraphQLProvider implements InitializingBean { @Value("${arc.graphql.define:graphql/schema.graphqls}") private String locationPattern; - private PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); - private TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry(); + private final PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + private final TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry(); - private DataFetcherInterceptorRegistry dataFetcherInterceptorRegistry; + private final DataFetcherInterceptorRegistry dataFetcherInterceptorRegistry; public GraphQLProvider(DataFetcherInterceptorRegistry dataFetcherInterceptorRegistry) { this.dataFetcherInterceptorRegistry = dataFetcherInterceptorRegistry;