Skip to content

Commit 54d061b

Browse files
committed
Extract base GraphQlSource builder
Separate more clearly the SDL builder options from other common options independent of how GraphQLSchema is created. See gh-312
1 parent ddb7408 commit 54d061b

File tree

8 files changed

+437
-321
lines changed

8 files changed

+437
-321
lines changed

spring-graphql-docs/src/docs/asciidoc/index.adoc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,9 @@ The main implementation, `DefaultExecutionGraphQlService`, is configured with a
243243
`graphql.GraphQL` instance to use for request execution. It provides a builder API to
244244
initialize GraphQL Java and build a `GraphQlSource`.
245245

246-
The default `GraphQlSource` builder, accessible via `GraphQlSource.builder()`, enables
247-
support for <<execution-reactive-datafetcher>>, <<execution-context>>, and
248-
<<execution-exceptions>>.
246+
The default `GraphQlSource` builder, accessible via
247+
`GraphQlSource.schemaResourceBuilder()`, enables support for
248+
<<execution-reactive-datafetcher>>, <<execution-context>>, and <<execution-exceptions>>.
249249

250250
The Spring Boot {spring-boot-ref-docs}/web.html#web.graphql[starter] initializes a
251251
`GraphQlSource` instance through the default `GraphQlSource.Builder` and also enables
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
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+
* https://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 org.springframework.graphql.execution;
18+
19+
import java.util.ArrayList;
20+
import java.util.Collections;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.function.Consumer;
24+
25+
import graphql.GraphQL;
26+
import graphql.execution.instrumentation.ChainedInstrumentation;
27+
import graphql.execution.instrumentation.Instrumentation;
28+
import graphql.schema.GraphQLCodeRegistry;
29+
import graphql.schema.GraphQLSchema;
30+
import graphql.schema.GraphQLTypeVisitor;
31+
import graphql.schema.SchemaTraverser;
32+
33+
34+
/**
35+
* Implementation of {@link GraphQlSource.Builder} that leaves it to subclasses
36+
* to initialize {@link GraphQLSchema}.
37+
*
38+
* @author Rossen Stoyanchev
39+
* @author Brian Clozel
40+
* @since 1.0.0
41+
*/
42+
abstract class AbstractGraphQlSourceBuilder<B extends GraphQlSource.Builder<B>> implements GraphQlSource.Builder<B> {
43+
44+
private final List<DataFetcherExceptionResolver> exceptionResolvers = new ArrayList<>();
45+
46+
private final List<GraphQLTypeVisitor> typeVisitors = new ArrayList<>();
47+
48+
private final List<Instrumentation> instrumentations = new ArrayList<>();
49+
50+
private Consumer<GraphQL.Builder> graphQlConfigurers = (builder) -> {
51+
};
52+
53+
54+
@Override
55+
public B exceptionResolvers(List<DataFetcherExceptionResolver> resolvers) {
56+
this.exceptionResolvers.addAll(resolvers);
57+
return self();
58+
}
59+
60+
@Override
61+
public B typeVisitors(List<GraphQLTypeVisitor> typeVisitors) {
62+
this.typeVisitors.addAll(typeVisitors);
63+
return self();
64+
}
65+
66+
@Override
67+
public B instrumentation(List<Instrumentation> instrumentations) {
68+
this.instrumentations.addAll(instrumentations);
69+
return self();
70+
}
71+
72+
@Override
73+
public B configureGraphQl(Consumer<GraphQL.Builder> configurer) {
74+
this.graphQlConfigurers = this.graphQlConfigurers.andThen(configurer);
75+
return self();
76+
}
77+
78+
@SuppressWarnings("unchecked")
79+
private <T extends B> T self() {
80+
return (T) this;
81+
}
82+
83+
@Override
84+
public GraphQlSource build() {
85+
GraphQLSchema schema = initGraphQlSchema();
86+
87+
schema = applyTypeVisitors(schema);
88+
89+
GraphQL.Builder builder = GraphQL.newGraphQL(schema);
90+
builder.defaultDataFetcherExceptionHandler(new ExceptionResolversExceptionHandler(this.exceptionResolvers));
91+
92+
if (!this.instrumentations.isEmpty()) {
93+
builder = builder.instrumentation(new ChainedInstrumentation(this.instrumentations));
94+
}
95+
96+
this.graphQlConfigurers.accept(builder);
97+
98+
return new FixedGraphQlSource(builder.build(), schema);
99+
}
100+
101+
/**
102+
* Subclasses must implement this method to provide the
103+
* {@link GraphQLSchema} instance.
104+
*/
105+
protected abstract GraphQLSchema initGraphQlSchema();
106+
107+
private GraphQLSchema applyTypeVisitors(GraphQLSchema schema) {
108+
List<GraphQLTypeVisitor> visitors = new ArrayList<>(this.typeVisitors);
109+
visitors.add(ContextDataFetcherDecorator.TYPE_VISITOR);
110+
111+
GraphQLCodeRegistry.Builder codeRegistry = GraphQLCodeRegistry.newCodeRegistry(schema.getCodeRegistry());
112+
Map<Class<?>, Object> vars = Collections.singletonMap(GraphQLCodeRegistry.Builder.class, codeRegistry);
113+
114+
SchemaTraverser traverser = new SchemaTraverser();
115+
traverser.depthFirstFullSchema(visitors, schema, vars);
116+
117+
return schema.transformWithoutTypes(builder -> builder.codeRegistry(codeRegistry));
118+
}
119+
120+
121+
/**
122+
* {@link GraphQlSource} with fixed {@link GraphQL} and {@link GraphQLSchema} instances.
123+
*/
124+
private static class FixedGraphQlSource implements GraphQlSource {
125+
126+
private final GraphQL graphQl;
127+
128+
private final GraphQLSchema schema;
129+
130+
FixedGraphQlSource(GraphQL graphQl, GraphQLSchema schema) {
131+
this.graphQl = graphQl;
132+
this.schema = schema;
133+
}
134+
135+
@Override
136+
public GraphQL graphQl() {
137+
return this.graphQl;
138+
}
139+
140+
@Override
141+
public GraphQLSchema schema() {
142+
return this.schema;
143+
}
144+
145+
}
146+
147+
}

0 commit comments

Comments
 (0)