66import graphql .PublicApi ;
77import graphql .execution .ValuesResolver ;
88import graphql .language .AstPrinter ;
9+ import graphql .language .Comment ;
910import graphql .language .Description ;
11+ import graphql .language .DirectiveDefinition ;
1012import graphql .language .Document ;
1113import graphql .language .EnumTypeDefinition ;
1214import 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