Skip to content

Commit ae541cb

Browse files
authored
feat: add support for per request schema transformation and instrumentation (#163)
* feat: add initial GraphQLJpaExecutorContextFactory implementation * feat: use Optional.empty() in GraphQLJpaExecutorContext default implementation * feat: add GraphQLJpaExecutorContextFactory auto configuration * refactor(executor): extracted context and input factory interfaces * fix(test): corrected failing public class GraphQLJpaQueryAutoConfigurationTest { * fix: GraphQLExecutorTests.queryWithNullVarables - Object required to be not null * feat: Added GraphQLExecutorContextFactory auto-configuration support * fix: Corrected SpringBootTests for GraphQLController auto-configuratino * feat: Added GraphQLContext supplier auto-configuration support * refactor: Added Supplier wrapper for Instrumentation and FieldVisibility * fix: GraphQLJpaQueryAutoConfigurationTest * feat: Added Instrumentation supplier example * fix: Added logger level example for tracing instrumentation
1 parent 36703f0 commit ae541cb

File tree

21 files changed

+507
-79
lines changed

21 files changed

+507
-79
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@ target/
2727
# Scala
2828
.cache-main
2929
.cache-tests
30+
31+
.sts4-cache

graphql-jpa-query-boot-starter/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryAutoConfiguration.java

+47-24
Original file line numberDiff line numberDiff line change
@@ -15,60 +15,83 @@
1515
*/
1616
package com.introproventures.graphql.jpa.query.boot.autoconfigure;
1717

18-
import javax.persistence.EntityManager;
18+
import java.util.function.Supplier;
19+
1920
import javax.persistence.EntityManagerFactory;
2021

22+
import org.springframework.beans.factory.ObjectProvider;
23+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2124
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2225
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2326
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2427
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
28+
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
2529
import org.springframework.context.annotation.Bean;
2630
import org.springframework.context.annotation.Configuration;
2731

2832
import com.introproventures.graphql.jpa.query.autoconfigure.GraphQLSchemaConfigurer;
33+
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutionInputFactory;
2934
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor;
35+
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutorContextFactory;
3036
import com.introproventures.graphql.jpa.query.schema.GraphQLSchemaBuilder;
3137
import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor;
38+
import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutorContextFactory;
3239
import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder;
3340

3441
import graphql.GraphQL;
42+
import graphql.GraphQLContext;
43+
import graphql.execution.instrumentation.Instrumentation;
3544
import graphql.schema.GraphQLSchema;
45+
import graphql.schema.visibility.GraphqlFieldVisibility;
3646

3747
@Configuration
3848
@ConditionalOnClass(GraphQL.class)
3949
@ConditionalOnProperty(name="spring.graphql.jpa.query.enabled", havingValue="true", matchIfMissing=true)
50+
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
4051
public class GraphQLJpaQueryAutoConfiguration {
4152

42-
@Bean
43-
@ConditionalOnMissingBean
44-
@ConditionalOnSingleCandidate(EntityManagerFactory.class)
45-
public GraphQLSchemaBuilder graphQLSchemaBuilder(final EntityManagerFactory entityManagerFactory) {
46-
return new GraphQLJpaSchemaBuilder(entityManagerFactory.createEntityManager());
47-
}
48-
49-
@Bean
50-
@ConditionalOnMissingBean
51-
public GraphQLSchemaConfigurer graphQLJpaQuerySchemaConfigurer(GraphQLSchemaBuilder graphQLSchemaBuilder) {
52-
53-
return (registry) -> {
54-
registry.register(graphQLSchemaBuilder.build());
55-
};
56-
}
57-
5853
@Configuration
5954
public static class DefaultGraphQLJpaQueryConfiguration {
55+
56+
@Bean
57+
@ConditionalOnMissingBean
58+
@ConditionalOnSingleCandidate(EntityManagerFactory.class)
59+
public GraphQLSchemaBuilder graphQLSchemaBuilder(final EntityManagerFactory entityManagerFactory) {
60+
return new GraphQLJpaSchemaBuilder(entityManagerFactory.createEntityManager());
61+
}
6062

6163
@Bean
62-
@ConditionalOnMissingBean(GraphQLExecutor.class)
63-
public GraphQLExecutor graphQLExecutor(GraphQLSchema graphQLSchema) {
64-
return new GraphQLJpaExecutor(graphQLSchema);
64+
@ConditionalOnMissingBean
65+
public GraphQLSchemaConfigurer graphQLJpaQuerySchemaConfigurer(GraphQLSchemaBuilder graphQLSchemaBuilder) {
66+
67+
return (registry) -> {
68+
registry.register(graphQLSchemaBuilder.build());
69+
};
70+
}
71+
72+
@Bean
73+
@ConditionalOnMissingBean
74+
public GraphQLExecutorContextFactory graphQLExecutorContextFactory(ObjectProvider<GraphQLExecutionInputFactory> graphQLExecutionInputFactory,
75+
ObjectProvider<Supplier<GraphqlFieldVisibility>> graphqlFieldVisibility,
76+
ObjectProvider<Supplier<Instrumentation>> instrumentation,
77+
ObjectProvider<Supplier<GraphQLContext>> graphqlContext) {
78+
GraphQLJpaExecutorContextFactory bean = new GraphQLJpaExecutorContextFactory();
79+
80+
graphQLExecutionInputFactory.ifAvailable(bean::withExecutionInputFactory);
81+
graphqlFieldVisibility.ifAvailable(bean::withGraphqlFieldVisibility);
82+
instrumentation.ifAvailable(bean::withInstrumentation);
83+
graphqlContext.ifAvailable(bean::withGraphqlContext);
84+
85+
return bean;
6586
}
6687

6788
@Bean
68-
@ConditionalOnMissingBean(GraphQLSchemaBuilder.class)
69-
public GraphQLSchemaBuilder graphQLSchemaBuilder(final EntityManager entityManager) {
70-
return new GraphQLJpaSchemaBuilder(entityManager);
89+
@ConditionalOnMissingBean
90+
public GraphQLExecutor graphQLExecutor(GraphQLSchema graphQLSchema,
91+
GraphQLExecutorContextFactory graphQLExecutorContextFactory) {
92+
return new GraphQLJpaExecutor(graphQLSchema,
93+
graphQLExecutorContextFactory);
7194
}
72-
95+
7396
}
7497
}
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,36 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.introproventures.graphql.jpa.query.boot.autoconfigure;
16+
package com.introproventures.graphql.jpa.query.boot.test.boot.autoconfigure;
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
1919

20+
import java.util.function.Supplier;
21+
2022
import org.junit.Test;
2123
import org.junit.runner.RunWith;
24+
import org.springframework.beans.factory.ObjectProvider;
2225
import org.springframework.beans.factory.annotation.Autowired;
2326
import org.springframework.boot.autoconfigure.SpringBootApplication;
2427
import org.springframework.boot.autoconfigure.domain.EntityScan;
2528
import org.springframework.boot.test.context.SpringBootTest;
2629
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
30+
import org.springframework.boot.test.mock.mockito.MockBean;
2731
import org.springframework.test.context.junit4.SpringRunner;
2832

33+
import com.introproventures.graphql.jpa.query.boot.test.starter.model.Author;
34+
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutionInputFactory;
2935
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor;
3036
import com.introproventures.graphql.jpa.query.schema.GraphQLSchemaBuilder;
3137
import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor;
38+
import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutorContextFactory;
3239
import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder;
33-
import com.introproventures.graphql.jpa.query.starter.model.Author;
3440

41+
import graphql.GraphQLContext;
42+
import graphql.execution.instrumentation.Instrumentation;
3543
import graphql.schema.GraphQLObjectType;
3644
import graphql.schema.GraphQLSchema;
45+
import graphql.schema.visibility.GraphqlFieldVisibility;
3746

3847
@RunWith(SpringRunner.class)
3948
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@@ -42,14 +51,41 @@ public class GraphQLJpaQueryAutoConfigurationTest {
4251
@SpringBootApplication
4352
@EntityScan(basePackageClasses=Author.class)
4453
static class Application {
54+
@MockBean
55+
private GraphQLExecutionInputFactory mockExecutionInputFactory;
56+
57+
@MockBean
58+
private Supplier<Instrumentation> mockInstrumentation;
59+
60+
@MockBean
61+
private Supplier<GraphqlFieldVisibility> mockGraphqlFieldVisibility;
62+
63+
@MockBean
64+
private Supplier<GraphQLContext> graphqlContext;
65+
4566
}
4667

4768
@Autowired(required=false)
4869
private GraphQLExecutor graphQLExecutor;
4970

5071
@Autowired(required=false)
5172
private GraphQLSchemaBuilder graphQLSchemaBuilder;
73+
74+
@Autowired(required=false)
75+
private GraphQLJpaExecutorContextFactory executorContextFactory;
76+
77+
@Autowired
78+
private ObjectProvider<GraphQLExecutionInputFactory> executionInputFactory;
5279

80+
@Autowired
81+
private ObjectProvider<Supplier<Instrumentation>> instrumentation;
82+
83+
@Autowired
84+
private ObjectProvider<Supplier<GraphqlFieldVisibility>> graphqlFieldVisibility;
85+
86+
@Autowired
87+
private ObjectProvider<Supplier<GraphQLContext>> graphqlContext;
88+
5389
@Autowired
5490
private GraphQLSchema graphQLSchema;
5591

@@ -61,6 +97,18 @@ public void contextIsAutoConfigured() {
6197
assertThat(graphQLSchemaBuilder).isNotNull()
6298
.isInstanceOf(GraphQLJpaSchemaBuilder.class);
6399

100+
assertThat(executorContextFactory).isNotNull()
101+
.isInstanceOf(GraphQLJpaExecutorContextFactory.class);
102+
103+
assertThat(executionInputFactory.getIfAvailable()).isNotNull();
104+
assertThat(instrumentation.getIfAvailable()).isNotNull();
105+
assertThat(graphqlFieldVisibility.getIfAvailable()).isNotNull();
106+
assertThat(graphqlContext.getIfAvailable()).isNotNull();
107+
108+
assertThat(executorContextFactory.getExecutionInputFactory()).isEqualTo(executionInputFactory.getObject());
109+
assertThat(executorContextFactory.getInstrumentation()).isEqualTo(instrumentation.getObject());
110+
assertThat(executorContextFactory.getGraphqlFieldVisibility()).isEqualTo(graphqlFieldVisibility.getObject());
111+
assertThat(executorContextFactory.getGraphqlContext()).isEqualTo(graphqlContext.getObject());
64112

65113
assertThat(graphQLSchema.getQueryType())
66114
.extracting(GraphQLObjectType::getName, GraphQLObjectType::getDescription)
+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.introproventures.graphql.jpa.query.starter;
16+
package com.introproventures.graphql.jpa.query.boot.test.starter;
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
1919

@@ -37,7 +37,7 @@
3737

3838
import com.fasterxml.jackson.core.JsonParseException;
3939
import com.fasterxml.jackson.databind.JsonMappingException;
40-
import com.introproventures.graphql.jpa.query.starter.Result.GraphQLError;
40+
import com.introproventures.graphql.jpa.query.boot.test.starter.Result.GraphQLError;
4141
import com.introproventures.graphql.jpa.query.web.GraphQLController.GraphQLQueryRequest;
4242

4343
@RunWith(SpringRunner.class)

graphql-jpa-query-boot-starter/src/test/java/com/introproventures/graphql/jpa/query/starter/model/Author.java renamed to graphql-jpa-query-boot-starter/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Author.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.introproventures.graphql.jpa.query.starter.model;
17+
package com.introproventures.graphql.jpa.query.boot.test.starter.model;
1818

1919
import java.util.Collection;
2020

graphql-jpa-query-boot-starter/src/test/java/com/introproventures/graphql/jpa/query/starter/model/Book.java renamed to graphql-jpa-query-boot-starter/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Book.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.introproventures.graphql.jpa.query.starter.model;
17+
package com.introproventures.graphql.jpa.query.boot.test.starter.model;
1818

1919
import javax.persistence.Entity;
2020
import javax.persistence.EnumType;

graphql-jpa-query-boot-starter/src/test/java/com/introproventures/graphql/jpa/query/starter/model/Genre.java renamed to graphql-jpa-query-boot-starter/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Genre.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.introproventures.graphql.jpa.query.starter.model;
17+
package com.introproventures.graphql.jpa.query.boot.test.starter.model;
1818

1919
public enum Genre {
2020
NOVEL, PLAY

graphql-jpa-query-example-merge/pom.xml

+2-22
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,12 @@
3232
<groupId>com.introproventures</groupId>
3333
<artifactId>graphql-jpa-query-example-model-starwars</artifactId>
3434
</dependency>
35-
36-
<dependency>
37-
<groupId>com.introproventures</groupId>
38-
<artifactId>graphql-jpa-query-web</artifactId>
39-
</dependency>
40-
41-
<dependency>
42-
<groupId>com.introproventures</groupId>
43-
<artifactId>graphql-jpa-query-schema</artifactId>
44-
</dependency>
4535

4636
<dependency>
4737
<groupId>com.introproventures</groupId>
48-
<artifactId>graphql-jpa-query-autoconfigure</artifactId>
38+
<artifactId>graphql-jpa-query-boot-starter</artifactId>
4939
</dependency>
50-
51-
<dependency>
52-
<groupId>org.springframework.boot</groupId>
53-
<artifactId>spring-boot-starter-web</artifactId>
54-
</dependency>
55-
56-
<dependency>
57-
<groupId>org.springframework.boot</groupId>
58-
<artifactId>spring-boot-starter-data-jpa</artifactId>
59-
</dependency>
60-
40+
6141
<dependency>
6242
<groupId>com.h2database</groupId>
6343
<artifactId>h2</artifactId>

graphql-jpa-query-example-merge/src/main/java/com/introproventures/graphql/jpa/query/example/Application.java

+29-6
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,22 @@
1515
*/
1616
package com.introproventures.graphql.jpa.query.example;
1717

18-
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor;
19-
import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor;
20-
import graphql.schema.GraphQLSchema;
18+
import java.util.function.Supplier;
19+
20+
import javax.servlet.http.HttpServletRequest;
21+
22+
import org.slf4j.Logger;
23+
import org.slf4j.LoggerFactory;
2124
import org.springframework.boot.SpringApplication;
2225
import org.springframework.boot.autoconfigure.SpringBootApplication;
2326
import org.springframework.context.annotation.Bean;
2427
import org.springframework.transaction.annotation.EnableTransactionManagement;
28+
import org.springframework.web.context.annotation.RequestScope;
29+
30+
import graphql.GraphQLContext;
31+
import graphql.execution.instrumentation.Instrumentation;
32+
import graphql.execution.instrumentation.SimpleInstrumentation;
33+
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
2534

2635
/**
2736
* GraphQL JPA Query Example with Spring Boot Autoconfiguration
@@ -34,14 +43,28 @@
3443
@SpringBootApplication
3544
@EnableTransactionManagement
3645
public class Application {
46+
47+
private static final Logger logger = LoggerFactory.getLogger(Application.class);
3748

3849
public static void main(String[] args) {
3950
SpringApplication.run(Application.class, args);
4051
}
41-
52+
53+
@Bean
54+
@RequestScope
55+
public Supplier<GraphQLContext> graphqlContext(HttpServletRequest request) {
56+
return () -> GraphQLContext.newContext()
57+
.of("request", request)
58+
.of("user", request)
59+
.build();
60+
}
61+
4262
@Bean
43-
public GraphQLExecutor graphQLExecutor(GraphQLSchema graphQLSchema) {
44-
return new GraphQLJpaExecutor(graphQLSchema);
63+
@RequestScope
64+
public Supplier<Instrumentation> instrumentation(HttpServletRequest request) {
65+
return () -> logger.isDebugEnabled()
66+
? new TracingInstrumentation()
67+
: SimpleInstrumentation.INSTANCE;
4568
}
4669

4770
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2017 IntroPro Ventures Inc. and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.introproventures.graphql.jpa.query.schema;
18+
19+
import graphql.ExecutionInput;
20+
21+
public interface GraphQLExecutionInputFactory {
22+
23+
default ExecutionInput.Builder create() {
24+
return ExecutionInput.newExecutionInput();
25+
};
26+
27+
}

0 commit comments

Comments
 (0)