Skip to content

Commit e60eb50

Browse files
authored
fix(GH-239): Added GraphQLFieldVisibility supplier support in GraphQL request (#240)
* fix(GH-239): Added GraphQLFieldVisibility supplier support in GraphQL request * feat(tests): Added GraphQLJpaExecutorContextFactoryTest * refactor: polished GraphQLJpaExecutorContextFactoryTest * fix(tests): Added more GraphQLJpaExecutorContextFactoryTest coverage
1 parent 510c5bf commit e60eb50

File tree

4 files changed

+221
-1
lines changed

4 files changed

+221
-1
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package com.introproventures.graphql.jpa.query.example;
1717

18+
import static graphql.schema.visibility.DefaultGraphqlFieldVisibility.DEFAULT_FIELD_VISIBILITY;
19+
1820
import java.util.function.Supplier;
1921

2022
import javax.servlet.http.HttpServletRequest;
@@ -31,6 +33,8 @@
3133
import graphql.execution.instrumentation.Instrumentation;
3234
import graphql.execution.instrumentation.SimpleInstrumentation;
3335
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
36+
import graphql.schema.visibility.BlockedFields;
37+
import graphql.schema.visibility.GraphqlFieldVisibility;
3438

3539
/**
3640
* GraphQL JPA Query Example with Spring Boot Autoconfiguration
@@ -50,6 +54,15 @@ public static void main(String[] args) {
5054
SpringApplication.run(Application.class, args);
5155
}
5256

57+
@Bean
58+
@RequestScope
59+
public Supplier<GraphqlFieldVisibility> graphqlFieldVisibility(HttpServletRequest request) {
60+
return () -> !request.isSecure() ? BlockedFields.newBlock()
61+
.addPattern("Book.price")
62+
.build()
63+
: DEFAULT_FIELD_VISIBILITY;
64+
}
65+
5366
@Bean
5467
@RequestScope
5568
public Supplier<GraphQLContext> graphqlContext(HttpServletRequest request) {

graphql-jpa-query-example-model-books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Book.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ public class Book {
5050
@GraphQLIgnoreFilter
5151
String description;
5252

53+
@GraphQLDescription("The price of the book visible only by authorized users")
54+
Double price;
55+
5356
@ElementCollection(fetch = FetchType.LAZY)
5457
@GraphQLDescription("A set of user-defined tags")
5558
private Set<String> tags = new LinkedHashSet<>();

graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaExecutorContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public ExecutionInput.Builder newExecutionInput() {
5353

5454
@Override
5555
public GraphQL.Builder newGraphQL() {
56-
return GraphQL.newGraphQL(graphQLSchema)
56+
return GraphQL.newGraphQL(getGraphQLSchema())
5757
.instrumentation(instrumentation.get());
5858
}
5959

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
package com.introproventures.graphql.jpa.query.schema.impl;
2+
3+
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
4+
import static graphql.schema.visibility.DefaultGraphqlFieldVisibility.DEFAULT_FIELD_VISIBILITY;
5+
import static org.assertj.core.api.Assertions.assertThat;
6+
import static org.assertj.core.api.Assertions.tuple;
7+
import static org.assertj.core.util.Lists.list;
8+
9+
import java.util.Collections;
10+
import java.util.function.Supplier;
11+
12+
import org.junit.Test;
13+
import org.junit.runner.RunWith;
14+
import org.springframework.beans.factory.annotation.Autowired;
15+
import org.springframework.boot.autoconfigure.SpringBootApplication;
16+
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
17+
import org.springframework.boot.test.context.SpringBootTest;
18+
import org.springframework.context.annotation.Bean;
19+
import org.springframework.test.context.junit4.SpringRunner;
20+
21+
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor;
22+
import com.introproventures.graphql.jpa.query.schema.GraphQLExecutorContextFactory;
23+
24+
import graphql.ExecutionResult;
25+
import graphql.GraphQLContext;
26+
import graphql.Scalars;
27+
import graphql.execution.instrumentation.Instrumentation;
28+
import graphql.execution.instrumentation.SimpleInstrumentation;
29+
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
30+
import graphql.schema.GraphQLObjectType;
31+
import graphql.schema.GraphQLSchema;
32+
import graphql.schema.StaticDataFetcher;
33+
import graphql.schema.visibility.BlockedFields;
34+
import graphql.schema.visibility.GraphqlFieldVisibility;
35+
import graphql.validation.ValidationErrorType;
36+
37+
38+
@RunWith(SpringRunner.class)
39+
@SpringBootTest
40+
public class GraphQLJpaExecutorContextFactoryTest {
41+
42+
@Autowired
43+
private GraphQLExecutor executor;
44+
45+
private static boolean isBlockFields;
46+
47+
private static boolean isTracingInstrumentation;
48+
49+
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
50+
static class Application {
51+
52+
@Bean
53+
public Supplier<GraphqlFieldVisibility> graphqlFieldVisibility() {
54+
return () -> isBlockFields ? BlockedFields.newBlock()
55+
.addPattern("Book.price")
56+
.build()
57+
: DEFAULT_FIELD_VISIBILITY;
58+
}
59+
60+
@Bean
61+
public Supplier<GraphQLContext> graphqlContext() {
62+
return () -> GraphQLContext.newContext()
63+
.of("username", "john")
64+
.build();
65+
}
66+
67+
@Bean
68+
public Supplier<Instrumentation> instrumentation() {
69+
return () -> isTracingInstrumentation ? new TracingInstrumentation()
70+
: SimpleInstrumentation.INSTANCE;
71+
}
72+
73+
@Bean
74+
public GraphQLExecutorContextFactory graphQLJpaExecutorContextFactory(Supplier<GraphQLContext> graphQLContext,
75+
Supplier<GraphqlFieldVisibility> graphqlFieldVisibility,
76+
Supplier<Instrumentation> instrumentation) {
77+
return new GraphQLJpaExecutorContextFactory().withGraphqlContext(graphQLContext)
78+
.withGraphqlFieldVisibility(graphqlFieldVisibility)
79+
.withInstrumentation(instrumentation);
80+
}
81+
82+
@Bean
83+
public GraphQLExecutor graphQLExecutor(final GraphQLSchema graphQLSchema,
84+
final GraphQLExecutorContextFactory graphQLJpaExecutorContextFactory) {
85+
return new GraphQLJpaExecutor(graphQLSchema,
86+
graphQLJpaExecutorContextFactory);
87+
}
88+
89+
@SuppressWarnings("deprecation")
90+
@Bean
91+
public GraphQLSchema graphQLSchema() {
92+
93+
GraphQLObjectType query = GraphQLObjectType.newObject()
94+
.name("query")
95+
.field(newFieldDefinition().name("hello")
96+
.type(Scalars.GraphQLString)
97+
.dataFetcher(env -> {
98+
GraphQLContext context = env.getContext();
99+
100+
return context.get("username");
101+
})
102+
)
103+
.field(newFieldDefinition().name("Books")
104+
.dataFetcher(new StaticDataFetcher(Collections.singletonMap("price", 10.0)))
105+
.type(GraphQLObjectType.newObject()
106+
.name("Book")
107+
.field(newFieldDefinition().name("price")
108+
.type(Scalars.GraphQLFloat)))
109+
)
110+
.build();
111+
112+
return GraphQLSchema.newSchema()
113+
.query(query)
114+
.build();
115+
}
116+
}
117+
118+
@Test
119+
public void contextLoads() {
120+
// success
121+
}
122+
123+
@Test
124+
public void testGraphqlFieldVisibility() {
125+
//given
126+
isBlockFields = true;
127+
String query = "{ Books { price } }";
128+
129+
//when
130+
ExecutionResult result = executor.execute(query);
131+
132+
// then
133+
assertThat(result.getErrors()).isNotEmpty()
134+
.extracting("validationErrorType", "queryPath")
135+
.containsOnly(tuple(ValidationErrorType.FieldUndefined,
136+
list("Books", "price")));
137+
}
138+
139+
@Test
140+
public void testGraphqlFieldVisibilityDisabled() {
141+
//given
142+
isBlockFields = false;
143+
String query = "{ Books { price } }";
144+
145+
//when
146+
ExecutionResult result = executor.execute(query);
147+
148+
// then
149+
assertThat(result.getErrors()).isEmpty();
150+
151+
String data = result.getData()
152+
.toString();
153+
154+
assertThat(data).isEqualTo("{Books={price=10.0}}");
155+
}
156+
157+
@Test
158+
public void testGraphQLContext() {
159+
//given
160+
String query = "{ hello }";
161+
162+
//when
163+
ExecutionResult result = executor.execute(query);
164+
165+
// then
166+
assertThat(result.getErrors()).isEmpty();
167+
168+
String data = result.getData().toString();
169+
170+
assertThat(data).isEqualTo("{hello=john}");
171+
}
172+
173+
@Test
174+
public void testInstrumentation() {
175+
//given
176+
isTracingInstrumentation = true;
177+
178+
String query = "{ hello }";
179+
180+
//when
181+
ExecutionResult result = executor.execute(query);
182+
183+
// then
184+
assertThat(result.getExtensions()).isNotEmpty();
185+
186+
assertThat(result.getExtensions()
187+
.get("tracing")).isNotNull();
188+
}
189+
190+
@Test
191+
public void testInstrumentationDisabled() {
192+
//given
193+
isTracingInstrumentation = false;
194+
195+
String query = "{ hello }";
196+
197+
//when
198+
ExecutionResult result = executor.execute(query);
199+
200+
// then
201+
assertThat(result.getExtensions()).isNull();
202+
}
203+
204+
}

0 commit comments

Comments
 (0)