Skip to content

Commit 3b1736f

Browse files
authored
Merge pull request graphql-java#3287 from vadimofd/schema-printer-comments
Add SchemaPrinter AST comments support
2 parents 980d7f0 + 2fb4e10 commit 3b1736f

File tree

2 files changed

+497
-29
lines changed

2 files changed

+497
-29
lines changed

src/main/java/graphql/schema/idl/SchemaPrinter.java

Lines changed: 133 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import graphql.PublicApi;
77
import graphql.execution.ValuesResolver;
88
import graphql.language.AstPrinter;
9+
import graphql.language.Comment;
910
import graphql.language.Description;
11+
import graphql.language.DirectiveDefinition;
1012
import graphql.language.Document;
1113
import graphql.language.EnumTypeDefinition;
1214
import graphql.language.EnumValueDefinition;
@@ -109,6 +111,8 @@ public static class Options {
109111

110112
private final GraphqlTypeComparatorRegistry comparatorRegistry;
111113

114+
private final boolean includeAstDefinitionComments;
115+
112116
private Options(boolean includeIntrospectionTypes,
113117
boolean includeScalars,
114118
boolean includeSchemaDefinition,
@@ -117,7 +121,8 @@ private Options(boolean includeIntrospectionTypes,
117121
boolean descriptionsAsHashComments,
118122
Predicate<String> includeDirective,
119123
Predicate<GraphQLSchemaElement> includeSchemaElement,
120-
GraphqlTypeComparatorRegistry comparatorRegistry) {
124+
GraphqlTypeComparatorRegistry comparatorRegistry,
125+
boolean includeAstDefinitionComments) {
121126
this.includeIntrospectionTypes = includeIntrospectionTypes;
122127
this.includeScalars = includeScalars;
123128
this.includeSchemaDefinition = includeSchemaDefinition;
@@ -127,6 +132,7 @@ private Options(boolean includeIntrospectionTypes,
127132
this.descriptionsAsHashComments = descriptionsAsHashComments;
128133
this.comparatorRegistry = comparatorRegistry;
129134
this.includeSchemaElement = includeSchemaElement;
135+
this.includeAstDefinitionComments = includeAstDefinitionComments;
130136
}
131137

132138
public boolean isIncludeIntrospectionTypes() {
@@ -165,6 +171,8 @@ public boolean isUseAstDefinitions() {
165171
return useAstDefinitions;
166172
}
167173

174+
public boolean isIncludeAstDefinitionComments() { return includeAstDefinitionComments; }
175+
168176
public static Options defaultOptions() {
169177
return new Options(false,
170178
true,
@@ -174,7 +182,8 @@ public static Options defaultOptions() {
174182
false,
175183
directive -> true,
176184
element -> true,
177-
DefaultGraphqlTypeComparatorRegistry.defaultComparators());
185+
DefaultGraphqlTypeComparatorRegistry.defaultComparators(),
186+
false);
178187
}
179188

180189
/**
@@ -193,7 +202,8 @@ public Options includeIntrospectionTypes(boolean flag) {
193202
this.descriptionsAsHashComments,
194203
this.includeDirective,
195204
this.includeSchemaElement,
196-
this.comparatorRegistry);
205+
this.comparatorRegistry,
206+
this.includeAstDefinitionComments);
197207
}
198208

199209
/**
@@ -212,7 +222,8 @@ public Options includeScalarTypes(boolean flag) {
212222
this.descriptionsAsHashComments,
213223
this.includeDirective,
214224
this.includeSchemaElement,
215-
this.comparatorRegistry);
225+
this.comparatorRegistry,
226+
this.includeAstDefinitionComments);
216227
}
217228

218229
/**
@@ -234,7 +245,8 @@ public Options includeSchemaDefinition(boolean flag) {
234245
this.descriptionsAsHashComments,
235246
this.includeDirective,
236247
this.includeSchemaElement,
237-
this.comparatorRegistry);
248+
this.comparatorRegistry,
249+
this.includeAstDefinitionComments);
238250
}
239251

240252
/**
@@ -258,7 +270,8 @@ public Options includeDirectiveDefinitions(boolean flag) {
258270
this.descriptionsAsHashComments,
259271
this.includeDirective,
260272
this.includeSchemaElement,
261-
this.comparatorRegistry);
273+
this.comparatorRegistry,
274+
this.includeAstDefinitionComments);
262275
}
263276

264277
/**
@@ -278,7 +291,8 @@ public Options includeDirectives(boolean flag) {
278291
this.descriptionsAsHashComments,
279292
directive -> flag,
280293
this.includeSchemaElement,
281-
this.comparatorRegistry);
294+
this.comparatorRegistry,
295+
this.includeAstDefinitionComments);
282296
}
283297

284298
/**
@@ -297,7 +311,8 @@ public Options includeDirectives(Predicate<String> includeDirective) {
297311
this.descriptionsAsHashComments,
298312
includeDirective,
299313
this.includeSchemaElement,
300-
this.comparatorRegistry);
314+
this.comparatorRegistry,
315+
this.includeAstDefinitionComments);
301316
}
302317

303318
/**
@@ -317,7 +332,8 @@ public Options includeSchemaElement(Predicate<GraphQLSchemaElement> includeSchem
317332
this.descriptionsAsHashComments,
318333
this.includeDirective,
319334
includeSchemaElement,
320-
this.comparatorRegistry);
335+
this.comparatorRegistry,
336+
this.includeAstDefinitionComments);
321337
}
322338

323339
/**
@@ -337,7 +353,8 @@ public Options useAstDefinitions(boolean flag) {
337353
this.descriptionsAsHashComments,
338354
this.includeDirective,
339355
this.includeSchemaElement,
340-
this.comparatorRegistry);
356+
this.comparatorRegistry,
357+
this.includeAstDefinitionComments);
341358
}
342359

343360
/**
@@ -359,7 +376,8 @@ public Options descriptionsAsHashComments(boolean flag) {
359376
flag,
360377
this.includeDirective,
361378
this.includeSchemaElement,
362-
this.comparatorRegistry);
379+
this.comparatorRegistry,
380+
this.includeAstDefinitionComments);
363381
}
364382

365383
/**
@@ -380,7 +398,30 @@ public Options setComparators(GraphqlTypeComparatorRegistry comparatorRegistry)
380398
this.descriptionsAsHashComments,
381399
this.includeDirective,
382400
this.includeSchemaElement,
383-
comparatorRegistry);
401+
comparatorRegistry,
402+
this.includeAstDefinitionComments);
403+
}
404+
405+
/**
406+
* Sometimes it is useful to allow printing schema comments. This can be achieved by providing comments in the AST definitions.
407+
* <p>
408+
* The default is to ignore these for backward compatibility and due to this being relatively uncommon need.
409+
*
410+
* @param flag whether to include AST definition comments.
411+
*
412+
* @return new instance of Options
413+
*/
414+
public Options includeAstDefinitionComments(boolean flag) {
415+
return new Options(this.includeIntrospectionTypes,
416+
this.includeScalars,
417+
this.includeSchemaDefinition,
418+
this.includeDirectiveDefinitions,
419+
this.useAstDefinitions,
420+
this.descriptionsAsHashComments,
421+
this.includeDirective,
422+
this.includeSchemaElement,
423+
comparatorRegistry,
424+
flag);
384425
}
385426
}
386427

@@ -741,7 +782,7 @@ private SchemaElementPrinter<GraphQLSchema> schemaPrinter() {
741782
}
742783

743784
if (needsSchemaPrinted) {
744-
if (hasDescription(schema)) {
785+
if (hasAstDefinitionComments(schema) || hasDescription(schema)) {
745786
out.print(printComments(schema, ""));
746787
}
747788
List<GraphQLAppliedDirective> directives = DirectivesUtil.toAppliedDirectives(schema.getSchemaAppliedDirectives(), schema.getSchemaDirectives());
@@ -777,9 +818,10 @@ String argsString(List<GraphQLArgument> arguments) {
777818
}
778819

779820
String argsString(Class<? extends GraphQLSchemaElement> parent, List<GraphQLArgument> arguments) {
821+
boolean hasAstDefinitionComments = arguments.stream().anyMatch(this::hasAstDefinitionComments);
780822
boolean hasDescriptions = arguments.stream().anyMatch(this::hasDescription);
781-
String halfPrefix = hasDescriptions ? " " : "";
782-
String prefix = hasDescriptions ? " " : "";
823+
String halfPrefix = hasAstDefinitionComments || hasDescriptions ? " " : "";
824+
String prefix = hasAstDefinitionComments || hasDescriptions ? " " : "";
783825
int count = 0;
784826
StringBuilder sb = new StringBuilder();
785827

@@ -795,11 +837,11 @@ String argsString(Class<? extends GraphQLSchemaElement> parent, List<GraphQLArgu
795837
sb.append("(");
796838
} else {
797839
sb.append(",");
798-
if (!hasDescriptions) {
840+
if (!hasAstDefinitionComments && !hasDescriptions) {
799841
sb.append(" ");
800842
}
801843
}
802-
if (hasDescriptions) {
844+
if (hasAstDefinitionComments || hasDescriptions) {
803845
sb.append("\n");
804846
}
805847
sb.append(printComments(argument, prefix));
@@ -820,7 +862,7 @@ String argsString(Class<? extends GraphQLSchemaElement> parent, List<GraphQLArgu
820862
count++;
821863
}
822864
if (count > 0) {
823-
if (hasDescriptions) {
865+
if (hasAstDefinitionComments || hasDescriptions) {
824866
sb.append("\n");
825867
}
826868
sb.append(halfPrefix).append(")");
@@ -1027,18 +1069,26 @@ private String printComments(Object graphQLType, String prefix) {
10271069

10281070
private void printComments(PrintWriter out, Object graphQLType, String prefix) {
10291071
String descriptionText = getDescription(graphQLType);
1030-
if (isNullOrEmpty(descriptionText)) {
1031-
return;
1072+
if (!isNullOrEmpty(descriptionText)) {
1073+
List<String> lines = Arrays.asList(descriptionText.split("\n"));
1074+
if (options.isDescriptionsAsHashComments()) {
1075+
printMultiLineHashDescription(out, prefix, lines);
1076+
} else if (!lines.isEmpty()) {
1077+
if (lines.size() > 1) {
1078+
printMultiLineDescription(out, prefix, lines);
1079+
} else {
1080+
printSingleLineDescription(out, prefix, lines.get(0));
1081+
}
1082+
}
10321083
}
10331084

1034-
List<String> lines = Arrays.asList(descriptionText.split("\n"));
1035-
if (options.isDescriptionsAsHashComments()) {
1036-
printMultiLineHashDescription(out, prefix, lines);
1037-
} else if (!lines.isEmpty()) {
1038-
if (lines.size() > 1) {
1039-
printMultiLineDescription(out, prefix, lines);
1040-
} else {
1041-
printSingleLineDescription(out, prefix, lines.get(0));
1085+
if (options.isIncludeAstDefinitionComments()) {
1086+
String commentsText = getAstDefinitionComments(graphQLType);
1087+
if (!isNullOrEmpty(commentsText)) {
1088+
List<String> lines = Arrays.asList(commentsText.split("\n") );
1089+
if (!lines.isEmpty()) {
1090+
printMultiLineHashDescription(out, prefix, lines);
1091+
}
10421092
}
10431093
}
10441094
}
@@ -1062,6 +1112,61 @@ private void printSingleLineDescription(PrintWriter out, String prefix, String s
10621112
out.printf("%s\"%s\"\n", prefix, desc);
10631113
}
10641114

1115+
private boolean hasAstDefinitionComments(Object commentHolder) {
1116+
String comments = getAstDefinitionComments(commentHolder);
1117+
return !isNullOrEmpty(comments);
1118+
}
1119+
1120+
private String getAstDefinitionComments(Object commentHolder) {
1121+
if (commentHolder instanceof GraphQLObjectType) {
1122+
GraphQLObjectType type = (GraphQLObjectType) commentHolder;
1123+
return comments(ofNullable(type.getDefinition()).map(ObjectTypeDefinition::getComments).orElse(null));
1124+
} else if (commentHolder instanceof GraphQLEnumType) {
1125+
GraphQLEnumType type = (GraphQLEnumType) commentHolder;
1126+
return comments(ofNullable(type.getDefinition()).map(EnumTypeDefinition::getComments).orElse(null));
1127+
} else if (commentHolder instanceof GraphQLFieldDefinition) {
1128+
GraphQLFieldDefinition type = (GraphQLFieldDefinition) commentHolder;
1129+
return comments(ofNullable(type.getDefinition()).map(FieldDefinition::getComments).orElse(null));
1130+
} else if (commentHolder instanceof GraphQLEnumValueDefinition) {
1131+
GraphQLEnumValueDefinition type = (GraphQLEnumValueDefinition) commentHolder;
1132+
return comments(ofNullable(type.getDefinition()).map(EnumValueDefinition::getComments).orElse(null));
1133+
} else if (commentHolder instanceof GraphQLUnionType) {
1134+
GraphQLUnionType type = (GraphQLUnionType) commentHolder;
1135+
return comments(ofNullable(type.getDefinition()).map(UnionTypeDefinition::getComments).orElse(null));
1136+
} else if (commentHolder instanceof GraphQLInputObjectType) {
1137+
GraphQLInputObjectType type = (GraphQLInputObjectType) commentHolder;
1138+
return comments(ofNullable(type.getDefinition()).map(InputObjectTypeDefinition::getComments).orElse(null));
1139+
} else if (commentHolder instanceof GraphQLInputObjectField) {
1140+
GraphQLInputObjectField type = (GraphQLInputObjectField) commentHolder;
1141+
return comments(ofNullable(type.getDefinition()).map(InputValueDefinition::getComments).orElse(null));
1142+
} else if (commentHolder instanceof GraphQLInterfaceType) {
1143+
GraphQLInterfaceType type = (GraphQLInterfaceType) commentHolder;
1144+
return comments(ofNullable(type.getDefinition()).map(InterfaceTypeDefinition::getComments).orElse(null));
1145+
} else if (commentHolder instanceof GraphQLScalarType) {
1146+
GraphQLScalarType type = (GraphQLScalarType) commentHolder;
1147+
return comments(ofNullable(type.getDefinition()).map(ScalarTypeDefinition::getComments).orElse(null));
1148+
} else if (commentHolder instanceof GraphQLArgument) {
1149+
GraphQLArgument type = (GraphQLArgument) commentHolder;
1150+
return comments(ofNullable(type.getDefinition()).map(InputValueDefinition::getComments).orElse(null));
1151+
} else if (commentHolder instanceof GraphQLDirective) {
1152+
GraphQLDirective type = (GraphQLDirective) commentHolder;
1153+
return comments(ofNullable(type.getDefinition()).map(DirectiveDefinition::getComments).orElse(null));
1154+
} else if (commentHolder instanceof GraphQLSchema) {
1155+
GraphQLSchema type = (GraphQLSchema) commentHolder;
1156+
return comments(ofNullable(type.getDefinition()).map(SchemaDefinition::getComments).orElse(null));
1157+
} else {
1158+
return Assert.assertShouldNeverHappen();
1159+
}
1160+
}
1161+
1162+
private String comments(List<Comment> comments) {
1163+
if ( comments == null || comments.isEmpty() ) {
1164+
return null;
1165+
}
1166+
String s = comments.stream().map(c -> c.getContent()).collect(joining("\n", "", "\n"));
1167+
return s;
1168+
}
1169+
10651170
private boolean hasDescription(Object descriptionHolder) {
10661171
String description = getDescription(descriptionHolder);
10671172
return !isNullOrEmpty(description);

0 commit comments

Comments
 (0)