Skip to content

Commit dd44da9

Browse files
committed
Merge branch 'master' into schema-generator-post-process
2 parents c45d058 + b5692e4 commit dd44da9

35 files changed

Lines changed: 1546 additions & 350 deletions

src/main/antlr/Graphql.g4

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ document : definition+;
1111
definition:
1212
operationDefinition |
1313
fragmentDefinition |
14-
typeSystemDefinition
14+
typeSystemDefinition |
15+
typeSystemExtension
1516
;
1617

1718

src/main/antlr/GraphqlSDL.g4

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@ import GraphqlCommon;
44
typeSystemDefinition: description?
55
schemaDefinition |
66
typeDefinition |
7-
typeExtension |
87
directiveDefinition
98
;
109

10+
typeSystemExtension :
11+
schemaExtension |
12+
typeExtension
13+
;
14+
1115
schemaDefinition : description? SCHEMA directives? '{' operationTypeDefinition+ '}';
1216

17+
schemaExtension :
18+
EXTEND SCHEMA directives? '{' operationTypeDefinition+ '}' |
19+
EXTEND SCHEMA directives+
20+
;
21+
1322
operationTypeDefinition : description? operationType ':' typeName;
1423

1524
typeDefinition:

src/main/java/graphql/SerializationError.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import graphql.schema.CoercingSerializeException;
77

88
import java.util.List;
9+
import java.util.Map;
910

1011
import static graphql.Assert.assertNotNull;
1112
import static java.lang.String.format;
@@ -31,15 +32,14 @@ public CoercingSerializeException getException() {
3132
return exception;
3233
}
3334

34-
3535
@Override
3636
public String getMessage() {
3737
return message;
3838
}
3939

4040
@Override
4141
public List<SourceLocation> getLocations() {
42-
return null;
42+
return exception.getLocations();
4343
}
4444

4545
@Override
@@ -52,6 +52,11 @@ public List<Object> getPath() {
5252
return path;
5353
}
5454

55+
@Override
56+
public Map<String, Object> getExtensions() {
57+
return exception.getExtensions();
58+
}
59+
5560
@Override
5661
public String toString() {
5762
return "SerializationError{" +

src/main/java/graphql/execution/ValuesResolver.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import static graphql.schema.GraphQLTypeUtil.unwrapOne;
3737
import static graphql.schema.visibility.DefaultGraphqlFieldVisibility.DEFAULT_FIELD_VISIBILITY;
3838

39+
@SuppressWarnings("rawtypes")
3940
@Internal
4041
public class ValuesResolver {
4142

@@ -47,11 +48,10 @@ public class ValuesResolver {
4748
* @param schema the schema
4849
* @param variableDefinitions the variable definitions
4950
* @param variableValues the supplied variables
50-
*
5151
* @return coerced variable values as a map
5252
*/
5353
public Map<String, Object> coerceVariableValues(GraphQLSchema schema, List<VariableDefinition> variableDefinitions, Map<String, Object> variableValues) {
54-
GraphqlFieldVisibility fieldVisibility = schema.getFieldVisibility();
54+
GraphqlFieldVisibility fieldVisibility = schema.getCodeRegistry().getFieldVisibility();
5555
Map<String, Object> coercedValues = new LinkedHashMap<>();
5656
for (VariableDefinition variableDefinition : variableDefinitions) {
5757
String variableName = variableDefinition.getName();
@@ -154,10 +154,10 @@ private Object coerceValue(GraphqlFieldVisibility fieldVisibility, VariableDefin
154154
if (value instanceof Map) {
155155
return coerceValueForInputObjectType(fieldVisibility, variableDefinition, (GraphQLInputObjectType) graphQLType, (Map<String, Object>) value);
156156
} else {
157-
throw new CoercingParseValueException(
158-
"Expected type 'Map' but was '" + value.getClass().getSimpleName() +
159-
"'. Variables for input objects must be an instance of type 'Map'."
160-
);
157+
throw CoercingParseValueException.newCoercingParseValueException()
158+
.message("Expected type 'Map' but was '" + value.getClass().getSimpleName() +
159+
"'. Variables for input objects must be an instance of type 'Map'.")
160+
.build();
161161
}
162162
} else {
163163
return assertShouldNeverHappen("unhandled type %s", graphQLType);
@@ -166,13 +166,14 @@ private Object coerceValue(GraphqlFieldVisibility fieldVisibility, VariableDefin
166166
if (e.getLocations() != null) {
167167
throw e;
168168
}
169-
170-
throw new CoercingParseValueException(
171-
"Variable '" + inputName + "' has an invalid value. " + e.getMessage(),
172-
e.getCause(),
173-
variableDefinition.getSourceLocation()
174-
);
169+
throw CoercingParseValueException.newCoercingParseValueException()
170+
.message("Variable '" + inputName + "' has an invalid value : " + e.getMessage())
171+
.extensions(e.getExtensions())
172+
.cause(e.getCause())
173+
.sourceLocation(variableDefinition.getSourceLocation())
174+
.build();
175175
}
176+
176177
}
177178

178179
private Object coerceValueForInputObjectType(GraphqlFieldVisibility fieldVisibility, VariableDefinition variableDefinition, GraphQLInputObjectType inputObjectType, Map<String, Object> input) {

src/main/java/graphql/execution/instrumentation/dataloader/DataLoaderDispatcherInstrumentation.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,14 @@ private void immediatelyDispatch(DataLoaderDispatcherInstrumentationState state)
9595

9696
@Override
9797
public InstrumentationContext<ExecutionResult> beginExecuteOperation(InstrumentationExecuteOperationParameters parameters) {
98+
DataLoaderDispatcherInstrumentationState state = parameters.getInstrumentationState();
99+
//
100+
// during #instrumentExecutionInput they could have enhanced the data loader registry
101+
// so we grab it now just before the query operation gets started
102+
//
103+
DataLoaderRegistry finalRegistry = parameters.getExecutionContext().getDataLoaderRegistry();
104+
state.setDataLoaderRegistry(finalRegistry);
98105
if (!isDataLoaderCompatibleExecution(parameters.getExecutionContext())) {
99-
DataLoaderDispatcherInstrumentationState state = parameters.getInstrumentationState();
100106
state.setAggressivelyBatching(false);
101107
}
102108
return new SimpleInstrumentationContext<>();

src/main/java/graphql/execution/instrumentation/dataloader/DataLoaderDispatcherInstrumentationState.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import org.dataloader.DataLoaderRegistry;
88
import org.slf4j.Logger;
99

10+
import java.util.concurrent.atomic.AtomicReference;
11+
1012
/**
1113
* A base class that keeps track of whether aggressive batching can be used
1214
*/
@@ -21,21 +23,24 @@ public DataLoaderRegistry register(String key, DataLoader<?, ?> dataLoader) {
2123
};
2224

2325
private final FieldLevelTrackingApproach approach;
24-
private final DataLoaderRegistry dataLoaderRegistry;
26+
private final AtomicReference<DataLoaderRegistry> dataLoaderRegistry;
2527
private final InstrumentationState state;
26-
private final boolean hasNoDataLoaders;
27-
private boolean aggressivelyBatching = true;
28+
private volatile boolean aggressivelyBatching = true;
29+
private volatile boolean hasNoDataLoaders;
2830

2931
public DataLoaderDispatcherInstrumentationState(Logger log, DataLoaderRegistry dataLoaderRegistry) {
30-
31-
this.dataLoaderRegistry = dataLoaderRegistry;
32-
this.approach = new FieldLevelTrackingApproach(log, dataLoaderRegistry);
32+
this.dataLoaderRegistry = new AtomicReference<>(dataLoaderRegistry);
33+
this.approach = new FieldLevelTrackingApproach(log, this::getDataLoaderRegistry);
3334
this.state = approach.createState();
35+
hasNoDataLoaders = checkForNoDataLoader(dataLoaderRegistry);
36+
}
37+
38+
private boolean checkForNoDataLoader(DataLoaderRegistry dataLoaderRegistry) {
3439
//
3540
// if they have never set a dataloader into the execution input then we can optimize
3641
// away the tracking code
3742
//
38-
hasNoDataLoaders = dataLoaderRegistry == EMPTY_DATALOADER_REGISTRY;
43+
return dataLoaderRegistry == EMPTY_DATALOADER_REGISTRY;
3944
}
4045

4146
boolean isAggressivelyBatching() {
@@ -51,7 +56,12 @@ FieldLevelTrackingApproach getApproach() {
5156
}
5257

5358
DataLoaderRegistry getDataLoaderRegistry() {
54-
return dataLoaderRegistry;
59+
return dataLoaderRegistry.get();
60+
}
61+
62+
void setDataLoaderRegistry(DataLoaderRegistry newRegistry) {
63+
dataLoaderRegistry.set(newRegistry);
64+
hasNoDataLoaders = checkForNoDataLoader(newRegistry);
5565
}
5666

5767
boolean hasNoDataLoaders() {

src/main/java/graphql/execution/instrumentation/dataloader/FieldLevelTrackingApproach.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@
2424
import java.util.Objects;
2525
import java.util.Set;
2626
import java.util.concurrent.CompletableFuture;
27+
import java.util.function.Supplier;
2728

2829
/**
2930
* This approach uses field level tracking to achieve its aims of making the data loader more efficient
3031
*/
3132
@Internal
3233
public class FieldLevelTrackingApproach {
33-
private final DataLoaderRegistry dataLoaderRegistry;
34+
private final Supplier<DataLoaderRegistry> dataLoaderRegistrySupplier;
3435
private final Logger log;
3536

3637
private static class CallStack implements InstrumentationState {
@@ -118,8 +119,8 @@ public void clearAndMarkCurrentLevelAsReady(int level) {
118119
}
119120
}
120121

121-
public FieldLevelTrackingApproach(Logger log, DataLoaderRegistry dataLoaderRegistry) {
122-
this.dataLoaderRegistry = dataLoaderRegistry;
122+
public FieldLevelTrackingApproach(Logger log, Supplier<DataLoaderRegistry> dataLoaderRegistrySupplier) {
123+
this.dataLoaderRegistrySupplier = dataLoaderRegistrySupplier;
123124
this.log = log;
124125
}
125126

@@ -286,7 +287,12 @@ private boolean levelReady(CallStack callStack, int level) {
286287
}
287288

288289
void dispatch() {
290+
DataLoaderRegistry dataLoaderRegistry = getDataLoaderRegistry();
289291
log.debug("Dispatching data loaders ({})", dataLoaderRegistry.getKeys());
290292
dataLoaderRegistry.dispatchAll();
291293
}
294+
295+
private DataLoaderRegistry getDataLoaderRegistry() {
296+
return dataLoaderRegistrySupplier.get();
297+
}
292298
}

src/main/java/graphql/language/SchemaDefinition.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,10 @@ public static final class Builder implements NodeBuilder {
126126
private IgnoredChars ignoredChars = IgnoredChars.EMPTY;
127127
private Map<String, String> additionalData = new LinkedHashMap<>();
128128

129-
private Builder() {
129+
protected Builder() {
130130
}
131131

132-
private Builder(SchemaDefinition existing) {
132+
protected Builder(SchemaDefinition existing) {
133133
this.sourceLocation = existing.getSourceLocation();
134134
this.comments = existing.getComments();
135135
this.directives = existing.getDirectives();
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package graphql.language;
2+
3+
import graphql.PublicApi;
4+
5+
import java.util.ArrayList;
6+
import java.util.LinkedHashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.function.Consumer;
10+
11+
import static graphql.Assert.assertNotNull;
12+
13+
@PublicApi
14+
public class SchemaExtensionDefinition extends SchemaDefinition {
15+
16+
protected SchemaExtensionDefinition(List<Directive> directives, List<OperationTypeDefinition> operationTypeDefinitions, SourceLocation sourceLocation, List<Comment> comments, IgnoredChars ignoredChars, Map<String, String> additionalData) {
17+
super(directives, operationTypeDefinitions, sourceLocation, comments, ignoredChars, additionalData);
18+
}
19+
20+
@Override
21+
public SchemaExtensionDefinition withNewChildren(NodeChildrenContainer newChildren) {
22+
return transformExtension(builder -> builder
23+
.directives(newChildren.getChildren(CHILD_DIRECTIVES))
24+
.operationTypeDefinitions(newChildren.getChildren(CHILD_OPERATION_TYPE_DEFINITIONS))
25+
);
26+
}
27+
28+
@Override
29+
public SchemaExtensionDefinition deepCopy() {
30+
return new SchemaExtensionDefinition(deepCopy(getDirectives()), deepCopy(getOperationTypeDefinitions()), getSourceLocation(), getComments(),
31+
getIgnoredChars(), getAdditionalData());
32+
}
33+
34+
@Override
35+
public String toString() {
36+
return "SchemaExtensionDefinition{" +
37+
"directives=" + getDirectives() +
38+
", operationTypeDefinitions=" + getOperationTypeDefinitions() +
39+
"}";
40+
}
41+
42+
public SchemaExtensionDefinition transformExtension(Consumer<Builder> builderConsumer) {
43+
Builder builder = new Builder(this);
44+
builderConsumer.accept(builder);
45+
return builder.build();
46+
}
47+
48+
public static Builder newSchemaExtensionDefinition() {
49+
return new Builder();
50+
}
51+
52+
public static final class Builder implements NodeBuilder {
53+
private SourceLocation sourceLocation;
54+
private List<Comment> comments = new ArrayList<>();
55+
private List<Directive> directives = new ArrayList<>();
56+
private List<OperationTypeDefinition> operationTypeDefinitions = new ArrayList<>();
57+
private IgnoredChars ignoredChars = IgnoredChars.EMPTY;
58+
private Map<String, String> additionalData = new LinkedHashMap<>();
59+
60+
protected Builder() {
61+
}
62+
63+
protected Builder(SchemaDefinition existing) {
64+
this.sourceLocation = existing.getSourceLocation();
65+
this.comments = existing.getComments();
66+
this.directives = existing.getDirectives();
67+
this.operationTypeDefinitions = existing.getOperationTypeDefinitions();
68+
this.ignoredChars = existing.getIgnoredChars();
69+
this.additionalData = new LinkedHashMap<>(existing.getAdditionalData());
70+
}
71+
72+
73+
public Builder sourceLocation(SourceLocation sourceLocation) {
74+
this.sourceLocation = sourceLocation;
75+
return this;
76+
}
77+
78+
public Builder comments(List<Comment> comments) {
79+
this.comments = comments;
80+
return this;
81+
}
82+
83+
public Builder directives(List<Directive> directives) {
84+
this.directives = directives;
85+
return this;
86+
}
87+
88+
public Builder directive(Directive directive) {
89+
this.directives.add(directive);
90+
return this;
91+
}
92+
93+
public Builder operationTypeDefinitions(List<OperationTypeDefinition> operationTypeDefinitions) {
94+
this.operationTypeDefinitions = operationTypeDefinitions;
95+
return this;
96+
}
97+
98+
public Builder operationTypeDefinition(OperationTypeDefinition operationTypeDefinitions) {
99+
this.operationTypeDefinitions.add(operationTypeDefinitions);
100+
return this;
101+
}
102+
103+
public Builder ignoredChars(IgnoredChars ignoredChars) {
104+
this.ignoredChars = ignoredChars;
105+
return this;
106+
}
107+
108+
public Builder additionalData(Map<String, String> additionalData) {
109+
this.additionalData = assertNotNull(additionalData);
110+
return this;
111+
}
112+
113+
public Builder additionalData(String key, String value) {
114+
this.additionalData.put(key, value);
115+
return this;
116+
}
117+
118+
public SchemaExtensionDefinition build() {
119+
return new SchemaExtensionDefinition(directives,
120+
operationTypeDefinitions,
121+
sourceLocation,
122+
comments,
123+
ignoredChars,
124+
additionalData);
125+
}
126+
}
127+
128+
}

0 commit comments

Comments
 (0)