Skip to content

Commit 4d9310a

Browse files
committed
Upgrade to graphql-java 15 (#341)
Fixes: - Allow new nullable attributes on type fields that implement interface fields (#338) Adds language tooling for: - New specifiedBy scalar directive - Allow directives on variable definitions Other changes: - No longer need to filter Java scalars as these have been removed in 15 - No longer need to escape schema descriptions as this is now handled by graphql-java
1 parent 8d6c466 commit 4d9310a

File tree

12 files changed

+47
-128
lines changed

12 files changed

+47
-128
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ repositories {
1717
}
1818

1919
dependencies {
20-
compile ("com.graphql-java:graphql-java:14.0") {
20+
compile ("com.graphql-java:graphql-java:15.0") {
2121
exclude group: "org.reactivestreams", module: "reactive-streams"
2222
exclude group: "com.graphql-java", module: "java-dataloader"
2323
exclude group: "org.slf4j", module: "slf4j-api"

gen/com/intellij/lang/jsgraphql/GraphQLParser.java

Lines changed: 10 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/com/intellij/lang/jsgraphql/psi/GraphQLVariableDefinition.java

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/com/intellij/lang/jsgraphql/psi/GraphQLVisitor.java

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/com/intellij/lang/jsgraphql/psi/impl/GraphQLVariableDefinitionImpl.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/META-INF/graphql specification schema.graphql

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ Deprecations include a reason for why it is deprecated, which is formatted using
5959
"""
6060
directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE
6161

62+
"""
63+
The @specifiedBy directive is used within the type system definition language
64+
to provide a URL for specifying the behavior of custom scalar definitions.
65+
"""
66+
directive @specifiedBy(url: String!) on SCALAR
6267

6368
# Introspection --------------------------------------------------------------------------------------------------- ###
6469

@@ -101,6 +106,9 @@ type __Type {
101106

102107
"NON_NULL and LIST only"
103108
ofType: __Type
109+
110+
"May be non-null for SCALAR only, must be null for the others"
111+
specifiedBy: String
104112
}
105113

106114
type __Field {
@@ -162,5 +170,6 @@ enum __DirectiveLocation {
162170
ENUM,
163171
ENUM_VALUE,
164172
INPUT_OBJECT,
165-
INPUT_FIELD_DEFINITION
173+
INPUT_FIELD_DEFINITION,
174+
VARIABLE_DEFINITION
166175
}

src/grammars/GraphQLParser.bnf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ typedOperationDefinition ::= operationType identifier? variableDefinitions? dire
178178

179179
variableDefinitions ::= '(' variableDefinition+ ')' {pin=1 methods=[variableDefinitions="variableDefinition"]}
180180

181-
variableDefinition ::= variable ':' type defaultValue? {pin=1 recoverWhile=variableDefinition_recover}
181+
variableDefinition ::= variable ':' type defaultValue? directives? {pin=1 recoverWhile=variableDefinition_recover methods=[directives="directive"] implements="com.intellij.lang.jsgraphql.psi.impl.GraphQLDirectivesAware"}
182182

183183
private variableDefinition_recover ::= !(')' | root_tokens | variableDefinition)
184184

src/main/com/intellij/lang/jsgraphql/ide/completion/GraphQLCompletionContributor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,8 @@ private boolean isValidDirectiveLocation(GraphQLDirectivesAware directivesAware,
11101110
return directivesAware instanceof GraphQLInputObjectTypeDefinition || directivesAware instanceof GraphQLInputObjectTypeExtensionDefinition;
11111111
case INPUT_FIELD_DEFINITION:
11121112
return directivesAware instanceof GraphQLInputValueDefinition && !(directivesAware.getParent() instanceof GraphQLArgumentsDefinition);
1113+
case VARIABLE_DEFINITION:
1114+
return directivesAware instanceof GraphQLVariableDefinition;
11131115
}
11141116
return false;
11151117
}

src/main/com/intellij/lang/jsgraphql/ide/editor/GraphQLIntrospectionHelper.java

Lines changed: 2 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import com.intellij.util.Consumer;
4747
import com.intellij.util.ExceptionUtil;
4848
import graphql.GraphQLException;
49-
import graphql.Scalars;
5049
import graphql.introspection.IntrospectionQuery;
5150
import graphql.language.*;
5251
import graphql.schema.Coercing;
@@ -62,19 +61,17 @@
6261
import org.jetbrains.annotations.NotNull;
6362

6463
import java.io.IOException;
65-
import java.io.StringWriter;
6664
import java.io.UnsupportedEncodingException;
6765
import java.util.*;
6866

6967
import static com.intellij.lang.jsgraphql.v1.ide.project.JSGraphQLLanguageUIProjectService.setHeadersFromOptions;
70-
import static graphql.schema.idl.ScalarInfo.STANDARD_SCALARS;
7168

7269
public class GraphQLIntrospectionHelper {
7370

7471
private GraphQLIntrospectionTask latestIntrospection = null;
7572
private Project myProject;
7673

77-
private static Set<String> DEFAULT_DIRECTIVES = Sets.newHashSet("deprecated", "skip", "include");
74+
private static Set<String> DEFAULT_DIRECTIVES = Sets.newHashSet("deprecated", "skip", "include", "specifiedBy");
7875

7976
public static GraphQLIntrospectionHelper getService(@NotNull Project project) {
8077
return ServiceManager.getService(project, GraphQLIntrospectionHelper.class);
@@ -86,15 +83,6 @@ public GraphQLIntrospectionHelper(Project project) {
8683
project.getMessageBus().connect().subscribe(GraphQLConfigManager.TOPIC, () -> latestIntrospection = null);
8784
}
8885

89-
// remove the non-spec "graphql-java" standard scalars since we're building a registry based on the actual types present in introspection results
90-
STANDARD_SCALARS.remove(Scalars.GraphQLBigDecimal);
91-
STANDARD_SCALARS.remove(Scalars.GraphQLBigInteger);
92-
STANDARD_SCALARS.remove(Scalars.GraphQLByte);
93-
STANDARD_SCALARS.remove(Scalars.GraphQLChar);
94-
STANDARD_SCALARS.remove(Scalars.GraphQLShort);
95-
STANDARD_SCALARS.remove(Scalars.GraphQLLong);
96-
97-
9886
}
9987

10088
public void performIntrospectionQueryAndUpdateSchemaPathFile(Project project, GraphQLConfigEndpoint endpoint) {
@@ -241,23 +229,6 @@ public String printIntrospectionJsonAsGraphQL(String introspectionJson) {
241229
}
242230
}
243231

244-
// escape descriptions here as the graphql-java SchemaPrinter doesn't at this time
245-
Ref<Consumer<Object>> escapeDescriptionsVisitJson = Ref.create();
246-
escapeDescriptionsVisitJson.set((value) -> {
247-
if (value instanceof Collection) {
248-
((Collection) value).forEach(colValue -> escapeDescriptionsVisitJson.get().consume(colValue));
249-
} else if (value instanceof Map) {
250-
Map map = (Map) value;
251-
String description = (String) map.get("description");
252-
if (description != null) {
253-
description = escapeGraphQLStyleString(description, false); // allow descriptions to appear in multi-line block strings
254-
map.put("description", description);
255-
}
256-
map.values().forEach(mapValue -> escapeDescriptionsVisitJson.get().consume(mapValue));
257-
}
258-
});
259-
escapeDescriptionsVisitJson.get().consume(introspectionAsMap);
260-
261232
if (!GraphQLSettings.getSettings(myProject).isEnableIntrospectionDefaultValues()) {
262233
// strip out the defaultValues that are potentially non-spec compliant
263234
Ref<Consumer<Object>> defaultValueVisitJson = Ref.create();
@@ -309,7 +280,7 @@ private GraphQLSchema buildIntrospectionSchema(TypeDefinitionRegistry registry)
309280
final RuntimeWiring runtimeWiring = EchoingWiringFactory.newEchoingWiring(wiring -> {
310281
Map<String, ScalarTypeDefinition> scalars = registry.scalars();
311282
scalars.forEach((name, v) -> {
312-
if (!ScalarInfo.isStandardScalar(name)) {
283+
if (!ScalarInfo.isGraphqlSpecifiedScalar(name)) {
313284
wiring.scalar(createCustomIntrospectionScalar(name));
314285
}
315286
});
@@ -348,77 +319,6 @@ public Object parseLiteral(Object input) {
348319
});
349320
}
350321

351-
private static String hex(char ch) {
352-
return Integer.toHexString(ch).toUpperCase();
353-
}
354-
355-
/**
356-
* Escapes a raw string description for display inside a block or single line GraphQL string.
357-
* Note that we ignore '/' since it's in the allowed range of source characters even though it's allowed as an escape, e.g. \/
358-
*
359-
* @param str the string to escape
360-
* @return the escaped string as per https://facebook.github.io/graphql/June2018/#sec-String-Value
361-
*/
362-
private static String escapeGraphQLStyleString(String str, Boolean escapeNewLine) {
363-
364-
final int sz = str.length();
365-
final StringWriter out = new StringWriter(sz);
366-
367-
for (int i = 0; i < sz; ++i) {
368-
char ch = str.charAt(i);
369-
if (ch < ' ') {
370-
switch (ch) {
371-
case '\b':
372-
out.write('\\');
373-
out.write('b');
374-
break;
375-
case '\t':
376-
out.write('\\');
377-
out.write('t');
378-
break;
379-
case '\n':
380-
if (escapeNewLine) {
381-
out.write('\\');
382-
out.write('n');
383-
} else {
384-
out.write(ch);
385-
}
386-
break;
387-
case '\f':
388-
out.write('\\');
389-
out.write('f');
390-
break;
391-
case '\r':
392-
out.write('\\');
393-
out.write('r');
394-
break;
395-
case '\u000b':
396-
default:
397-
if (ch > 15) {
398-
out.write("\\u00" + hex(ch));
399-
} else {
400-
out.write("\\u000" + hex(ch));
401-
}
402-
break;
403-
}
404-
} else {
405-
switch (ch) {
406-
case '"':
407-
out.write('\\');
408-
out.write('"');
409-
break;
410-
case '\\':
411-
out.write('\\');
412-
out.write('\\');
413-
break;
414-
default:
415-
out.write(ch);
416-
}
417-
}
418-
}
419-
return out.toString();
420-
}
421-
422322
public GraphQLIntrospectionTask getLatestIntrospection() {
423323
return latestIntrospection;
424324
}

src/main/com/intellij/lang/jsgraphql/ide/editor/GraphQLIntrospectionResultToSchema.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ public class GraphQLIntrospectionResultToSchema {
5151
*/
5252
@SuppressWarnings("unchecked")
5353
public Document createSchemaDefinition(Map<String, Object> introspectionResult) {
54-
assertTrue(introspectionResult.get("__schema") != null, "__schema expected");
54+
assertTrue(introspectionResult.get("__schema") != null, () -> "__schema expected");
5555
Map<String, Object> schema = (Map<String, Object>) introspectionResult.get("__schema");
5656

5757

5858
Map<String, Object> queryType = (Map<String, Object>) schema.get("queryType");
59-
assertNotNull(queryType, "queryType expected");
59+
assertNotNull(queryType, () -> "queryType expected");
6060
TypeName query = TypeName.newTypeName().name((String) queryType.get("name")).build();
6161
boolean nonDefaultQueryName = !"Query".equals(query.getName());
6262

@@ -118,7 +118,7 @@ private TypeDefinition createTypeDefinition(Map<String, Object> type) {
118118

119119
private TypeDefinition createScalar(Map<String, Object> input) {
120120
String name = (String) input.get("name");
121-
if (ScalarInfo.isStandardScalar(name)) {
121+
if (ScalarInfo.isGraphqlSpecifiedScalar(name)) {
122122
return null;
123123
}
124124
return ScalarTypeDefinition.newScalarTypeDefinition().name(name).description(getDescription(input)).build();
@@ -127,7 +127,7 @@ private TypeDefinition createScalar(Map<String, Object> input) {
127127

128128
@SuppressWarnings("unchecked")
129129
UnionTypeDefinition createUnion(Map<String, Object> input) {
130-
assertTrue(input.get("kind").equals("UNION"), "wrong input");
130+
assertTrue(input.get("kind").equals("UNION"), () -> "wrong input");
131131

132132
final List<Map<String, Object>> possibleTypes = (List<Map<String, Object>>) input.get("possibleTypes");
133133
final List<Type> memberTypes = Lists.newArrayList();
@@ -145,7 +145,7 @@ UnionTypeDefinition createUnion(Map<String, Object> input) {
145145

146146
@SuppressWarnings("unchecked")
147147
EnumTypeDefinition createEnum(Map<String, Object> input) {
148-
assertTrue(input.get("kind").equals("ENUM"), "wrong input");
148+
assertTrue(input.get("kind").equals("ENUM"), () -> "wrong input");
149149

150150
final List<Map<String, Object>> enumValues = (List<Map<String, Object>>) input.get("enumValues");
151151
final List<EnumValueDefinition> enumValueDefinitions = Lists.newArrayList();
@@ -169,7 +169,7 @@ EnumTypeDefinition createEnum(Map<String, Object> input) {
169169

170170
@SuppressWarnings("unchecked")
171171
InterfaceTypeDefinition createInterface(Map<String, Object> input) {
172-
assertTrue(input.get("kind").equals("INTERFACE"), "wrong input");
172+
assertTrue(input.get("kind").equals("INTERFACE"), () -> "wrong input");
173173

174174
final List<Map<String, Object>> fields = (List<Map<String, Object>>) input.get("fields");
175175
final InterfaceTypeDefinition interfaceTypeDefinition = InterfaceTypeDefinition.newInterfaceTypeDefinition()
@@ -184,7 +184,7 @@ InterfaceTypeDefinition createInterface(Map<String, Object> input) {
184184

185185
@SuppressWarnings("unchecked")
186186
InputObjectTypeDefinition createInputObject(Map<String, Object> input) {
187-
assertTrue(input.get("kind").equals("INPUT_OBJECT"), "wrong input");
187+
assertTrue(input.get("kind").equals("INPUT_OBJECT"), () -> "wrong input");
188188
final List<Map<String, Object>> fields = (List<Map<String, Object>>) input.get("inputFields");
189189
final InputObjectTypeDefinition inputObjectTypeDefinition = InputObjectTypeDefinition.newInputObjectDefinition()
190190
.name((String) input.get("name"))
@@ -197,7 +197,7 @@ InputObjectTypeDefinition createInputObject(Map<String, Object> input) {
197197

198198
@SuppressWarnings("unchecked")
199199
ObjectTypeDefinition createObject(Map<String, Object> input) {
200-
assertTrue(input.get("kind").equals("OBJECT"), "wrong input");
200+
assertTrue(input.get("kind").equals("OBJECT"), () -> "wrong input");
201201

202202
ObjectTypeDefinition.Builder builder = ObjectTypeDefinition.newObjectTypeDefinition()
203203
.name((String) input.get("name"))

0 commit comments

Comments
 (0)