3333import java .util .LinkedHashMap ;
3434import java .util .List ;
3535import java .util .Map ;
36+ import java .util .function .Predicate ;
3637import java .util .function .Supplier ;
38+ import java .util .stream .Collectors ;
3739import java .util .stream .Stream ;
3840
3941import static graphql .schema .visibility .DefaultGraphqlFieldVisibility .DEFAULT_FIELD_VISIBILITY ;
@@ -64,11 +66,11 @@ public static class Options {
6466 private final SchemaPrinterComparatorRegistry comparatorRegistry ;
6567
6668 private Options (boolean includeIntrospectionTypes ,
67- boolean includeScalars ,
68- boolean includeExtendedScalars ,
69- boolean includeSchemaDefinition ,
70- boolean includeDirectives ,
71- SchemaPrinterComparatorRegistry comparatorRegistry ) {
69+ boolean includeScalars ,
70+ boolean includeExtendedScalars ,
71+ boolean includeSchemaDefinition ,
72+ boolean includeDirectives ,
73+ SchemaPrinterComparatorRegistry comparatorRegistry ) {
7274 this .includeIntrospectionTypes = includeIntrospectionTypes ;
7375 this .includeScalars = includeScalars ;
7476 this .includeExtendedScalars = includeExtendedScalars ;
@@ -106,6 +108,7 @@ public static Options defaultOptions() {
106108 * This will allow you to include introspection types that are contained in a schema
107109 *
108110 * @param flag whether to include them
111+ *
109112 * @return options
110113 */
111114 public Options includeIntrospectionTypes (boolean flag ) {
@@ -116,6 +119,7 @@ public Options includeIntrospectionTypes(boolean flag) {
116119 * This will allow you to include scalar types that are contained in a schema
117120 *
118121 * @param flag whether to include them
122+ *
119123 * @return options
120124 */
121125 public Options includeScalarTypes (boolean flag ) {
@@ -127,6 +131,7 @@ public Options includeScalarTypes(boolean flag) {
127131 * GraphQLBigDecimal or GraphQLBigInteger
128132 *
129133 * @param flag whether to include them
134+ *
130135 * @return options
131136 */
132137 public Options includeExtendedScalarTypes (boolean flag ) {
@@ -140,6 +145,7 @@ public Options includeExtendedScalarTypes(boolean flag) {
140145 * types do not use the default names.
141146 *
142147 * @param flag whether to force include the schema definition
148+ *
143149 * @return options
144150 */
145151 public Options includeSchemaDefintion (boolean flag ) {
@@ -151,6 +157,7 @@ public Options includeSchemaDefintion(boolean flag) {
151157 * make the printout noisy and having this flag would allow cleaner printout. On by default.
152158 *
153159 * @param flag whether to print directives
160+ *
154161 * @return new instance of options
155162 */
156163 public Options includeDirectives (boolean flag ) {
@@ -161,6 +168,7 @@ public Options includeDirectives(boolean flag) {
161168 * The comparator registry controls the printing order for registered {@code GraphQLType}s.
162169 *
163170 * @param comparatorRegistry The registry containing the {@code Comparator} and environment scoping rules.
171+ *
164172 * @return options
165173 */
166174 public Options setComparators (SchemaPrinterComparatorRegistry comparatorRegistry ) {
@@ -199,6 +207,7 @@ public SchemaPrinter(Options options) {
199207 * first to get the {@link graphql.language.Document} and then print that.
200208 *
201209 * @param schemaIDL the parsed schema IDL
210+ *
202211 * @return the logical schema definition
203212 */
204213 public String print (Document schemaIDL ) {
@@ -210,6 +219,7 @@ public String print(Document schemaIDL) {
210219 * This can print an in memory GraphQL schema back to a logical schema definition
211220 *
212221 * @param schema the schema in play
222+ *
213223 * @return the logical schema definition
214224 */
215225 public String print (GraphQLSchema schema ) {
@@ -436,10 +446,10 @@ private static String printAst(Object value, GraphQLInputType type) {
436446 }
437447
438448 private TypePrinter <GraphQLSchema > schemaPrinter () {
439- return (out , type , visibility ) -> {
440- GraphQLObjectType queryType = type .getQueryType ();
441- GraphQLObjectType mutationType = type .getMutationType ();
442- GraphQLObjectType subscriptionType = type .getSubscriptionType ();
449+ return (out , schema , visibility ) -> {
450+ GraphQLObjectType queryType = schema .getQueryType ();
451+ GraphQLObjectType mutationType = schema .getMutationType ();
452+ GraphQLObjectType subscriptionType = schema .getSubscriptionType ();
443453
444454 // when serializing a GraphQL schema using the type system language, a
445455 // schema definition should be omitted if only uses the default root type names.
@@ -470,9 +480,26 @@ private TypePrinter<GraphQLSchema> schemaPrinter() {
470480 }
471481 out .format ("}\n \n " );
472482 }
483+
484+ if (options .includeDirectives ) {
485+ out .format ("%s" , directiveDefinitions (getDirectives (schema )));
486+ }
473487 };
474488 }
475489
490+ private List <GraphQLDirective > getDirectives (GraphQLSchema schema ) {
491+
492+ // we don't print the standard directives that always ship with graphql-java
493+ List <String > standardDirectives = Arrays .asList (
494+ "skip" ,
495+ "include" ,
496+ "defer" ,
497+ "deprecated" );
498+
499+ Predicate <GraphQLDirective > standard = directive -> standardDirectives .contains (directive .getName ());
500+ return schema .getDirectives ().stream ().filter (standard .negate ()).collect (Collectors .toList ());
501+ }
502+
476503 String typeString (GraphQLType rawType ) {
477504 return GraphQLTypeUtil .simplePrint (rawType );
478505 }
@@ -612,6 +639,50 @@ private String directiveString(GraphQLDirective directive) {
612639 return sb .toString ();
613640 }
614641
642+ private String directiveDefinitions (List <GraphQLDirective > directives ) {
643+ StringBuilder sb = new StringBuilder ();
644+ for (GraphQLDirective directive : directives ) {
645+ sb .append (directiveDefinition (directive ));
646+ sb .append ("\n \n " );
647+ }
648+ return sb .toString ();
649+ }
650+
651+ private String directiveDefinition (GraphQLDirective directive ) {
652+ StringBuilder sb = new StringBuilder ();
653+
654+ StringWriter sw = new StringWriter ();
655+ PrintWriter pw = new PrintWriter (sw );
656+
657+ printComments (pw , directive , "" );
658+
659+ sb .append (sw .toString ());
660+
661+ sb .append ("directive @" ).append (directive .getName ());
662+
663+ SchemaPrinterComparatorEnvironment environment = SchemaPrinterComparatorEnvironment .newEnvironment ()
664+ .parentType (GraphQLDirective .class )
665+ .elementType (GraphQLArgument .class )
666+ .build ();
667+ Comparator <? super GraphQLType > comparator = options .comparatorRegistry .getComparator (environment );
668+
669+ List <GraphQLArgument > args = directive .getArguments ();
670+ args = args
671+ .stream ()
672+ .sorted (comparator )
673+ .collect (toList ());
674+
675+ sb .append (argsString (GraphQLDirective .class , args ));
676+
677+ sb .append (" on " );
678+
679+ String locations = directive .validLocations ().stream ().map (Enum ::name ).collect (Collectors .joining (" | " ));
680+ sb .append (locations );
681+
682+ return sb .toString ();
683+ }
684+
685+
615686 @ SuppressWarnings ("unchecked" )
616687 private <T > TypePrinter <T > printer (Class <?> clazz ) {
617688 TypePrinter typePrinter = printers .computeIfAbsent (clazz , k -> {
@@ -732,13 +803,16 @@ private AstDescriptionAndComments getDescriptionAndComments(Object descriptionHo
732803 } else if (descriptionHolder instanceof GraphQLArgument ) {
733804 GraphQLArgument type = (GraphQLArgument ) descriptionHolder ;
734805 return descriptionAndComments (type ::getDescription , type ::getDefinition , () -> type .getDefinition ().getDescription ());
806+ } else if (descriptionHolder instanceof GraphQLDirective ) {
807+ GraphQLDirective type = (GraphQLDirective ) descriptionHolder ;
808+ return descriptionAndComments (type ::getDescription , () -> null , () -> null );
735809 } else {
736810 return Assert .assertShouldNeverHappen ();
737811 }
738812 }
739813
740- AstDescriptionAndComments descriptionAndComments (Supplier <String > stringSupplier , Supplier <Node > nodeSupplier , Supplier <Description > descriptionSupplier ) {
741- String runtimeDesc = stringSupplier .get ();
814+ AstDescriptionAndComments descriptionAndComments (Supplier <String > runtimeDescriptionSupplier , Supplier <Node > nodeSupplier , Supplier <Description > descriptionSupplier ) {
815+ String runtimeDesc = runtimeDescriptionSupplier .get ();
742816 Node node = nodeSupplier .get ();
743817 Description description = null ;
744818 List <Comment > comments = null ;
0 commit comments