Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

Commit 7c7ac22

Browse files
committed
Merge remote-tracking branch 'origin/master'
2 parents 9f68c80 + a6511f2 commit 7c7ac22

8 files changed

Lines changed: 221 additions & 22 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ graphiql:
119119
variables: variables.graphql
120120
variables:
121121
editorTheme: "solarized light"
122+
headers:
123+
Authorization: "Bearer <your-token>"
122124
```
123125
By default GraphiQL is served from within the package. This can be configured to be served from CDN instead,
124126
by setting the property `graphiql.cdn.enabled` to `true`.
@@ -128,6 +130,8 @@ as defined at [GraphiQL Usage](https://github.com/graphql/graphiql#usage). Since
128130
properties like this isn't very readable, you can use the properties in the `graphiql.props.resources` group
129131
to set the classpath resources that should be loaded.
130132

133+
Headers that are used when sending the GraphiQL queries can be set by defining them in the `graphiql.headers` group.
134+
131135
# Supported GraphQL-Java Libraries
132136

133137
The following libraries have auto-configuration classes for creating a `GraphQLSchema`.

graphiql-spring-boot-autoconfigure/src/main/java/com/oembedler/moon/graphiql/boot/GraphiQLController.java

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
@Controller
3232
public class GraphiQLController {
3333

34+
private static final String CDNJS_CLOUDFLARE_COM_AJAX_LIBS_GRAPHIQL = "//cdnjs.cloudflare.com/ajax/libs/graphiql/";
3435
@Value("${graphiql.endpoint:/graphql}")
3536
private String graphqlEndpoint;
3637

@@ -85,45 +86,50 @@ private void addIfAbsent(Properties headerProperties, String header) {
8586
public void graphiql(HttpServletRequest request, HttpServletResponse response, @PathVariable Map<String, String> params) throws IOException {
8687
response.setContentType("text/html; charset=UTF-8");
8788

89+
String endpoint = constructGraphQlEndpoint(request, params);
90+
Map<String, String> replacements = getReplacements(endpoint);
8891

89-
String graphiqlCssUrl = "/vendor/graphiql.min.css";
90-
String graphiqlJsUrl = "/vendor/graphiql.min.js";
91-
92-
if (graphiqlCdnEnabled && StringUtils.isNotBlank(graphiqlCdnVersion)) {
93-
graphiqlCssUrl = "//cdnjs.cloudflare.com/ajax/libs/graphiql/" + graphiqlCdnVersion + "/graphiql.min.css";
94-
graphiqlJsUrl = "//cdnjs.cloudflare.com/ajax/libs/graphiql/" + graphiqlCdnVersion + "/graphiql.min.js";
95-
}
96-
97-
String endpoint = constructGraphQlEndpoint(params);
98-
if (StringUtils.isNotBlank(request.getContextPath()) && !endpoint.startsWith(request.getContextPath())) {
99-
endpoint = request.getContextPath() + endpoint;
100-
}
92+
String populatedTemplate = StrSubstitutor.replace(template, replacements);
93+
populatedTemplate = addContextPathIfEnabled(request, populatedTemplate);
94+
response.getOutputStream().write(populatedTemplate.getBytes(Charset.defaultCharset()));
95+
}
10196

97+
private Map<String, String> getReplacements(String endpoint) {
10298
Map<String, String> replacements = new HashMap<>();
10399
replacements.put("graphqlEndpoint", endpoint);
104100
replacements.put("pageTitle", pageTitle);
105-
replacements.put("graphiqlCssUrl", graphiqlCssUrl);
106-
replacements.put("graphiqlJsUrl", graphiqlJsUrl);
101+
replacements.put("graphiqlCssUrl", graphiqlUrl("graphiql.min.css"));
102+
replacements.put("graphiqlJsUrl", graphiqlUrl("graphiql.min.js"));
107103
replacements.put("props", props);
108104
replacements.put("headers", headers);
105+
return replacements;
106+
}
109107

110-
String populatedTemplate = StrSubstitutor.replace(template, replacements);
108+
private String graphiqlUrl(String filename) {
109+
if (graphiqlCdnEnabled && StringUtils.isNotBlank(graphiqlCdnVersion)) {
110+
return CDNJS_CLOUDFLARE_COM_AJAX_LIBS_GRAPHIQL + graphiqlCdnVersion + "/" + filename;
111+
}
112+
return "/vendor/" + filename;
113+
}
111114

115+
private String addContextPathIfEnabled(HttpServletRequest request, String populatedTemplate) {
112116
if (StringUtils.isNotBlank(request.getContextPath())) {
113117
String vendorPathWithContext = String.format("%s/vendor", request.getContextPath());
114118
populatedTemplate = populatedTemplate
115119
.replaceAll("src=\"/vendor", "src=\"" + vendorPathWithContext)
116120
.replaceAll("href=\"/vendor", "href=\"" + vendorPathWithContext);
117121
}
118-
119-
response.getOutputStream().write(populatedTemplate.getBytes(Charset.defaultCharset()));
122+
return populatedTemplate;
120123
}
121124

122-
private String constructGraphQlEndpoint(@RequestParam Map<String, String> params) {
125+
private String constructGraphQlEndpoint(HttpServletRequest request, @RequestParam Map<String, String> params) {
123126
String endpoint = graphqlEndpoint;
124127
for (Map.Entry<String, String> param : params.entrySet()) {
125128
endpoint = endpoint.replaceAll("\\{" + param.getKey() + "}", param.getValue());
126129
}
130+
if (StringUtils.isNotBlank(request.getContextPath()) && !endpoint.startsWith(request.getContextPath())) {
131+
return request.getContextPath() + endpoint;
132+
}
127133
return endpoint;
128134
}
129135

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.oembedler.moon.graphql.boot;
2+
3+
import graphql.analysis.MaxQueryComplexityInstrumentation;
4+
import graphql.analysis.MaxQueryDepthInstrumentation;
5+
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
8+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
9+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.context.annotation.Configuration;
12+
13+
/**
14+
* @author Marcel Overdijk
15+
*/
16+
@Configuration
17+
@ConditionalOnProperty(value = "graphql.servlet.enabled", havingValue = "true", matchIfMissing = true)
18+
@EnableConfigurationProperties({GraphQLServletProperties.class})
19+
public class GraphQLInstrumentationAutoConfiguration {
20+
21+
@Value("${graphql.servlet.maxQueryComplexity:#{null}}")
22+
private Integer maxQueryComplexity;
23+
24+
@Value("${graphql.servlet.maxQueryDepth:#{null}}")
25+
private Integer maxQueryDepth;
26+
27+
@Bean
28+
@ConditionalOnMissingBean
29+
@ConditionalOnProperty(value = "graphql.servlet.tracing-enabled", havingValue = "true")
30+
public TracingInstrumentation tracingInstrumentation() {
31+
return new TracingInstrumentation();
32+
}
33+
34+
@Bean
35+
@ConditionalOnMissingBean
36+
@ConditionalOnProperty(value = "graphql.servlet.max-query-complexity")
37+
public MaxQueryComplexityInstrumentation maxQueryComplexityInstrumentation() {
38+
return new MaxQueryComplexityInstrumentation(maxQueryComplexity);
39+
}
40+
41+
@Bean
42+
@ConditionalOnMissingBean
43+
@ConditionalOnProperty(value = "graphql.servlet.max-query-depth")
44+
public MaxQueryDepthInstrumentation maxQueryDepthInstrumentation() {
45+
return new MaxQueryDepthInstrumentation(maxQueryDepth);
46+
}
47+
}

graphql-spring-boot-autoconfigure/src/main/java/com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import graphql.execution.AsyncExecutionStrategy;
2323
import graphql.execution.ExecutionStrategy;
2424
import graphql.execution.SubscriptionExecutionStrategy;
25+
import graphql.execution.instrumentation.ChainedInstrumentation;
2526
import graphql.execution.instrumentation.Instrumentation;
2627
import graphql.execution.preparsed.PreparsedDocumentProvider;
2728
import graphql.schema.GraphQLSchema;
@@ -91,7 +92,7 @@ public class GraphQLWebAutoConfiguration {
9192
private List<GraphQLServletListener> listeners;
9293

9394
@Autowired(required = false)
94-
private Instrumentation instrumentation;
95+
private List<Instrumentation> instrumentations;
9596

9697
@Autowired(required = false)
9798
private GraphQLErrorHandler errorHandler;
@@ -186,8 +187,13 @@ public GraphQLQueryInvoker queryInvoker(ExecutionStrategyProvider executionStrat
186187
GraphQLQueryInvoker.Builder builder = GraphQLQueryInvoker.newBuilder()
187188
.withExecutionStrategyProvider(executionStrategyProvider);
188189

189-
if (instrumentation != null) {
190-
builder.withInstrumentation(instrumentation);
190+
if (instrumentations != null && !instrumentations.isEmpty()) {
191+
if (instrumentations.size() == 1) {
192+
builder.withInstrumentation(instrumentations.get(0));
193+
} else {
194+
Instrumentation instrumentation = new ChainedInstrumentation(instrumentations);
195+
builder.withInstrumentation(instrumentation);
196+
}
191197
}
192198

193199
if (preparsedDocumentProvider != null) {

graphql-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,21 @@
2424
"name": "graphql.servlet.subscriptions.websocket.path",
2525
"defaultValue": "/subscriptions",
2626
"type": "java.lang.String"
27+
},
28+
{
29+
"name": "graphql.servlet.tracingEnabled",
30+
"defaultValue": false,
31+
"type": "java.lang.Boolean"
32+
},
33+
{
34+
"name": "graphql.servlet.maxQueryComplexity",
35+
"defaultValue": null,
36+
"type": "java.lang.Integer"
37+
},
38+
{
39+
"name": "graphql.servlet.maxQueryDepth",
40+
"defaultValue": null,
41+
"type": "java.lang.Integer"
2742
}
2843
]
2944
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
22
com.oembedler.moon.graphql.boot.GraphQLWebAutoConfiguration,\
3-
com.oembedler.moon.graphql.boot.GraphQLJavaToolsAutoConfiguration
3+
com.oembedler.moon.graphql.boot.GraphQLJavaToolsAutoConfiguration,\
4+
com.oembedler.moon.graphql.boot.GraphQLInstrumentationAutoConfiguration
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.oembedler.moon.graphql.boot.test.instrumentation;
2+
3+
import com.oembedler.moon.graphql.boot.GraphQLInstrumentationAutoConfiguration;
4+
import com.oembedler.moon.graphql.boot.GraphQLWebAutoConfiguration;
5+
import com.oembedler.moon.graphql.boot.test.AbstractAutoConfigurationTest;
6+
import graphql.analysis.MaxQueryComplexityInstrumentation;
7+
import graphql.analysis.MaxQueryDepthInstrumentation;
8+
import graphql.execution.AsyncExecutionStrategy;
9+
import graphql.execution.ExecutionStrategy;
10+
import graphql.execution.instrumentation.Instrumentation;
11+
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
12+
import graphql.schema.GraphQLObjectType;
13+
import graphql.schema.GraphQLSchema;
14+
import graphql.servlet.AbstractGraphQLHttpServlet;
15+
import org.junit.Assert;
16+
import org.junit.Test;
17+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
18+
import org.springframework.context.annotation.Bean;
19+
import org.springframework.context.annotation.Configuration;
20+
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
21+
22+
/**
23+
* @author Marcel Overdijk
24+
*/
25+
public class GraphQLInstrumentationAutoConfigurationTest extends AbstractAutoConfigurationTest {
26+
27+
public GraphQLInstrumentationAutoConfigurationTest() {
28+
super(AnnotationConfigWebApplicationContext.class, GraphQLInstrumentationAutoConfiguration.class);
29+
}
30+
31+
@Configuration
32+
static class DefaultConfiguration {
33+
34+
@Bean
35+
GraphQLSchema schema() {
36+
return GraphQLSchema.newSchema().query(GraphQLObjectType.newObject().name("Query").build()).build();
37+
}
38+
39+
}
40+
41+
@Test(expected = NoSuchBeanDefinitionException.class)
42+
public void noDefaultInstrumentations() {
43+
load(DefaultConfiguration.class);
44+
45+
this.getContext().getBean(Instrumentation.class);
46+
}
47+
48+
@Test(expected = NoSuchBeanDefinitionException.class)
49+
public void tracingInstrumentationDisabled() {
50+
load(DefaultConfiguration.class, "graphql.servlet.tracingEnabled=false");
51+
52+
this.getContext().getBean(TracingInstrumentation.class);
53+
}
54+
55+
@Test
56+
public void tracingInstrumentationEnabled() {
57+
load(DefaultConfiguration.class, "graphql.servlet.tracingEnabled=true");
58+
59+
Assert.assertNotNull(this.getContext().getBean(TracingInstrumentation.class));
60+
}
61+
62+
@Test
63+
public void maxQueryComplexityEnabled() {
64+
load(DefaultConfiguration.class, "graphql.servlet.maxQueryComplexity=10");
65+
66+
Assert.assertNotNull(this.getContext().getBean(MaxQueryComplexityInstrumentation.class));
67+
}
68+
69+
@Test
70+
public void maxQueryDepthEnabled() {
71+
load(DefaultConfiguration.class, "graphql.servlet.maxQueryDepth=10");
72+
73+
Assert.assertNotNull(this.getContext().getBean(MaxQueryDepthInstrumentation.class));
74+
}
75+
}

graphql-spring-boot-autoconfigure/src/test/java/com/oembedler/moon/graphql/boot/test/web/GraphQLWebAutoConfigurationTest.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
import com.oembedler.moon.graphql.boot.GraphQLWebAutoConfiguration;
44
import com.oembedler.moon.graphql.boot.test.AbstractAutoConfigurationTest;
5+
import graphql.analysis.MaxQueryComplexityInstrumentation;
6+
import graphql.analysis.MaxQueryDepthInstrumentation;
57
import graphql.execution.AsyncExecutionStrategy;
68
import graphql.execution.ExecutionStrategy;
9+
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
710
import graphql.schema.GraphQLObjectType;
811
import graphql.schema.GraphQLSchema;
912
import graphql.servlet.AbstractGraphQLHttpServlet;
@@ -98,4 +101,46 @@ public void appContextLoadsWithThreeExecutionStrategies() {
98101

99102
Assert.assertNotNull(this.getContext().getBean(AbstractGraphQLHttpServlet.class));
100103
}
104+
105+
@Configuration
106+
static class OneInstrumentationConfiguration extends SimpleConfiguration {
107+
@Bean
108+
public TracingInstrumentation tracingInstrumentation() {
109+
return new TracingInstrumentation();
110+
}
111+
}
112+
113+
@Configuration
114+
static class MultipleInstrumentationsConfiguration extends OneInstrumentationConfiguration {
115+
@Bean
116+
public MaxQueryComplexityInstrumentation maxQueryComplexityInstrumentation() {
117+
return new MaxQueryComplexityInstrumentation(10);
118+
}
119+
120+
@Bean
121+
public MaxQueryDepthInstrumentation maxQueryDepthInstrumentation() {
122+
return new MaxQueryDepthInstrumentation(10);
123+
}
124+
}
125+
126+
@Test
127+
public void appContextLoadsWithNoInstrumentation() {
128+
load(SimpleConfiguration.class);
129+
130+
Assert.assertNotNull(this.getContext().getBean(AbstractGraphQLHttpServlet.class));
131+
}
132+
133+
@Test
134+
public void appContextLoadsWithOneInstrumentation() {
135+
load(OneInstrumentationConfiguration.class);
136+
137+
Assert.assertNotNull(this.getContext().getBean(AbstractGraphQLHttpServlet.class));
138+
}
139+
140+
@Test
141+
public void appContextLoadsWithMultipleInstrumentations() {
142+
load(MultipleInstrumentationsConfiguration.class);
143+
144+
Assert.assertNotNull(this.getContext().getBean(AbstractGraphQLHttpServlet.class));
145+
}
101146
}

0 commit comments

Comments
 (0)