diff --git a/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/Federation.java b/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/Federation.java index c5e05845..a61bec2b 100644 --- a/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/Federation.java +++ b/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/Federation.java @@ -141,7 +141,7 @@ private static RuntimeWiring.Builder copyRuntimeWiring(RuntimeWiring runtimeWiri runtimeWiring.getRegisteredDirectiveWiring().forEach(builder::directive); runtimeWiring.getDirectiveWiring().forEach(builder::directiveWiring); builder.comparatorRegistry(runtimeWiring.getComparatorRegistry()); - runtimeWiring.getSchemaTransformers().forEach(builder::transformer); + runtimeWiring.getSchemaGeneratorPostProcessings().forEach(builder::transformer); return builder; } diff --git a/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/FederationSdlPrinter.java b/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/FederationSdlPrinter.java index c65ec57a..69d300fe 100644 --- a/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/FederationSdlPrinter.java +++ b/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/FederationSdlPrinter.java @@ -3,10 +3,18 @@ import graphql.Assert; import graphql.language.AstPrinter; import graphql.language.AstValueHelper; -import graphql.language.Comment; import graphql.language.Description; import graphql.language.Document; -import graphql.language.Node; +import graphql.language.EnumTypeDefinition; +import graphql.language.EnumValueDefinition; +import graphql.language.FieldDefinition; +import graphql.language.InputObjectTypeDefinition; +import graphql.language.InputValueDefinition; +import graphql.language.InterfaceTypeDefinition; +import graphql.language.ObjectTypeDefinition; +import graphql.language.ScalarTypeDefinition; +import graphql.language.TypeDefinition; +import graphql.language.UnionTypeDefinition; import graphql.schema.DefaultGraphqlTypeComparatorRegistry; import graphql.schema.GraphQLArgument; import graphql.schema.GraphQLDirective; @@ -17,10 +25,13 @@ import graphql.schema.GraphQLInputObjectType; import graphql.schema.GraphQLInputType; import graphql.schema.GraphQLInterfaceType; +import graphql.schema.GraphQLNamedOutputType; +import graphql.schema.GraphQLNamedType; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLOutputType; import graphql.schema.GraphQLScalarType; import graphql.schema.GraphQLSchema; +import graphql.schema.GraphQLSchemaElement; import graphql.schema.GraphQLType; import graphql.schema.GraphQLTypeUtil; import graphql.schema.GraphQLUnionType; @@ -34,17 +45,21 @@ import java.io.PrintWriter; import java.io.StringWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; +import static graphql.Directives.DeprecatedDirective; +import static graphql.introspection.Introspection.DirectiveLocation.ENUM_VALUE; +import static graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION; import static graphql.schema.visibility.DefaultGraphqlFieldVisibility.DEFAULT_FIELD_VISIBILITY; +import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; @@ -52,6 +67,14 @@ * This can print an in memory GraphQL schema back to a logical schema definition */ public class FederationSdlPrinter { + // + // we use this so that we get the simple "@deprecated" as text and not a full exploded + // text with arguments (but only when we auto add this) + // + private static final GraphQLDirective DeprecatedDirective4Printing = GraphQLDirective.newDirective() + .name("deprecated") + .validLocations(FIELD_DEFINITION, ENUM_VALUE) + .build(); /** * Options to use when printing a schema @@ -62,15 +85,19 @@ public static class Options { private final boolean includeScalars; + private final boolean useAstDefinitions; + private final boolean includeExtendedScalars; private final boolean includeSchemaDefinition; - private final boolean includeDirectives; + private final boolean descriptionsAsHashComments; + + private final Predicate includeDirective; private final Predicate includeDirectiveDefinition; - private final Predicate includeTypeDefinition; + private final Predicate includeTypeDefinition; private final GraphqlTypeComparatorRegistry comparatorRegistry; @@ -78,17 +105,21 @@ private Options(boolean includeIntrospectionTypes, boolean includeScalars, boolean includeExtendedScalars, boolean includeSchemaDefinition, - boolean includeDirectives, + boolean useAstDefinitions, + boolean descriptionsAsHashComments, + Predicate includeDirective, Predicate includeDirectiveDefinition, - Predicate includeTypeDefinition, + Predicate includeTypeDefinition, GraphqlTypeComparatorRegistry comparatorRegistry) { this.includeIntrospectionTypes = includeIntrospectionTypes; this.includeScalars = includeScalars; this.includeExtendedScalars = includeExtendedScalars; this.includeSchemaDefinition = includeSchemaDefinition; - this.includeDirectives = includeDirectives; + this.includeDirective = includeDirective; this.includeDirectiveDefinition = includeDirectiveDefinition; this.includeTypeDefinition = includeTypeDefinition; + this.useAstDefinitions = useAstDefinitions; + this.descriptionsAsHashComments = descriptionsAsHashComments; this.comparatorRegistry = comparatorRegistry; } @@ -108,13 +139,34 @@ public boolean isIncludeSchemaDefinition() { return includeSchemaDefinition; } - public boolean isIncludeDirectives() { - return includeDirectives; + public Predicate getIncludeDirective() { + return includeDirective; + } + + public Predicate getIncludeDirectiveDefinition() { + return includeDirectiveDefinition; + } + + public Predicate getIncludeTypeDefinition() { + return includeTypeDefinition; + } + + public boolean isDescriptionsAsHashComments() { + return descriptionsAsHashComments; + } + + public GraphqlTypeComparatorRegistry getComparatorRegistry() { + return comparatorRegistry; + } + + public boolean isUseAstDefinitions() { + return useAstDefinitions; } public static Options defaultOptions() { - return new Options(false, false, false, false, true, - directiveDefinition -> true, typeDefinition -> true, + return new Options(false, false, false, + false, false, false, + directive -> true, directiveDefinition -> true, typeDefinition -> true, DefaultGraphqlTypeComparatorRegistry.defaultComparators()); } @@ -122,22 +174,20 @@ public static Options defaultOptions() { * This will allow you to include introspection types that are contained in a schema * * @param flag whether to include them - * * @return options */ public Options includeIntrospectionTypes(boolean flag) { - return new Options(flag, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, this.includeDirectives, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); + return new Options(flag, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, this.useAstDefinitions, this.descriptionsAsHashComments, this.includeDirective, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); } /** * This will allow you to include scalar types that are contained in a schema * * @param flag whether to include them - * * @return options */ public Options includeScalarTypes(boolean flag) { - return new Options(this.includeIntrospectionTypes, flag, this.includeExtendedScalars, this.includeSchemaDefinition, this.includeDirectives, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); + return new Options(this.includeIntrospectionTypes, flag, this.includeExtendedScalars, this.includeSchemaDefinition, this.useAstDefinitions, this.descriptionsAsHashComments, this.includeDirective, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); } /** @@ -145,11 +195,10 @@ public Options includeScalarTypes(boolean flag) { * GraphQLBigDecimal or GraphQLBigInteger * * @param flag whether to include them - * * @return options */ public Options includeExtendedScalarTypes(boolean flag) { - return new Options(this.includeIntrospectionTypes, this.includeScalars, flag, this.includeSchemaDefinition, this.includeDirectives, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); + return new Options(this.includeIntrospectionTypes, this.includeScalars, flag, this.includeSchemaDefinition, this.useAstDefinitions, this.descriptionsAsHashComments, this.includeDirective, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); } /** @@ -159,11 +208,10 @@ public Options includeExtendedScalarTypes(boolean flag) { * types do not use the default names. * * @param flag whether to force include the schema definition - * * @return options */ - public Options includeSchemaDefintion(boolean flag) { - return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, flag, this.includeDirectives, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); + public Options includeSchemaDefinition(boolean flag) { + return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, flag, this.useAstDefinitions, this.descriptionsAsHashComments, this.includeDirective, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); } /** @@ -171,24 +219,27 @@ public Options includeSchemaDefintion(boolean flag) { * make the printout noisy and having this flag would allow cleaner printout. On by default. * * @param flag whether to print directives - * * @return new instance of options */ public Options includeDirectives(boolean flag) { - return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, flag, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); + return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, this.useAstDefinitions, this.descriptionsAsHashComments, directive -> flag, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); + } + + public Options includeDirectives(Predicate includeDirective) { + return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, this.useAstDefinitions, this.descriptionsAsHashComments, includeDirective, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); } /** * Filter printing of directive definitions. In Apollo Federation, some directive * definitions need to be hidden, and this predicate allows filtering out such definitions. - * Prints all definitions by default. Note that both this predicate and the flag in - * {@link #includeDirectives(boolean)} must be true for a definition to be printed. + * Prints all definitions by default. Note that both this predicate and the predicate in + * {@link #includeDirectives(Predicate)} must be true for a definition to be printed. * * @param includeDirectiveDefinition returns true if the definition should be printed * @return new instance of options */ public Options includeDirectiveDefinitions(Predicate includeDirectiveDefinition) { - return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, this.includeDirectives, includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); + return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, this.useAstDefinitions, this.descriptionsAsHashComments, this.includeDirective, includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); } /** @@ -199,8 +250,32 @@ public Options includeDirectiveDefinitions(Predicate includeDi * @param includeTypeDefinition returns true if the definition should be printed * @return new instance of options */ - public Options includeTypeDefinitions(Predicate includeTypeDefinition) { - return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, this.includeDirectives, this.includeDirectiveDefinition, includeTypeDefinition, this.comparatorRegistry); + public Options includeTypeDefinitions(Predicate includeTypeDefinition) { + return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, this.useAstDefinitions, this.descriptionsAsHashComments, this.includeDirective, this.includeDirectiveDefinition, includeTypeDefinition, this.comparatorRegistry); + } + + /** + * This flag controls whether schema printer will use the {@link graphql.schema.GraphQLType}'s original Ast {@link graphql.language.TypeDefinition}s when printing the type. This + * allows access to any `extend type` declarations that might have been originally made. + * + * @param flag whether to print via AST type definitions + * @return new instance of options + */ + public Options useAstDefinitions(boolean flag) { + return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, flag, this.descriptionsAsHashComments, this.includeDirective, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); + } + + /** + * Descriptions are defined as preceding string literals, however an older legacy + * versions of SDL supported preceding '#' comments as + * descriptions. Set this to true to enable this deprecated behavior. + * This option is provided to ease adoption and may be removed in future versions. + * + * @param flag whether to print description as # comments + * @return new instance of options + */ + public Options descriptionsAsHashComments(boolean flag) { + return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, this.useAstDefinitions, flag, this.includeDirective, this.includeDirectiveDefinition, this.includeTypeDefinition, this.comparatorRegistry); } /** @@ -209,16 +284,10 @@ public Options includeTypeDefinitions(Predicate includeTypeDefiniti * The default is to sort elements by name but you can put in your own code to decide on the field order * * @param comparatorRegistry The registry containing the {@code Comparator} and environment scoping rules. - * * @return options */ public Options setComparators(GraphqlTypeComparatorRegistry comparatorRegistry) { - return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, this.includeDirectives, this.includeDirectiveDefinition, this.includeTypeDefinition, - comparatorRegistry); - } - - public GraphqlTypeComparatorRegistry getComparatorRegistry() { - return comparatorRegistry; + return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeExtendedScalars, this.includeSchemaDefinition, this.useAstDefinitions, this.descriptionsAsHashComments, this.includeDirective, this.includeDirectiveDefinition, this.includeTypeDefinition, comparatorRegistry); } } @@ -248,7 +317,6 @@ public FederationSdlPrinter(Options options) { * first to get the {@link graphql.language.Document} and then print that. * * @param schemaIDL the parsed schema IDL - * * @return the logical schema definition */ public String print(Document schemaIDL) { @@ -260,7 +328,6 @@ public String print(Document schemaIDL) { * This can print an in memory GraphQL schema back to a logical schema definition * * @param schema the schema in play - * * @return the logical schema definition */ public String print(GraphQLSchema schema) { @@ -273,8 +340,8 @@ public String print(GraphQLSchema schema) { List typesAsList = schema.getAllTypesAsList() .stream() - .sorted(Comparator.comparing(GraphQLType::getName)) - .filter(options.includeTypeDefinition) + .sorted(Comparator.comparing(GraphQLNamedType::getName)) + .filter(options.getIncludeTypeDefinition()) .collect(toList()); printType(out, typesAsList, GraphQLInterfaceType.class, visibility); @@ -297,7 +364,7 @@ private interface TypePrinter { } - private boolean isIntrospectionType(GraphQLType type) { + private boolean isIntrospectionType(GraphQLNamedType type) { return !options.isIncludeIntrospectionTypes() && type.getName().startsWith("__"); } @@ -317,12 +384,17 @@ private TypePrinter scalarPrinter() { printScalar = true; } if (printScalar) { - printComments(out, type, ""); - out.format("scalar %s%s\n\n", type.getName(), directivesString(GraphQLScalarType.class, type.getDirectives())); + if (shouldPrintAsAst(type.getDefinition())) { + printAsAst(out, type.getDefinition(), type.getExtensionDefinitions()); + } else { + printComments(out, type, ""); + out.format("scalar %s%s\n\n", type.getName(), directivesString(GraphQLScalarType.class, type.getDirectives())); + } } }; } + private TypePrinter enumPrinter() { return (out, type, visibility) -> { if (isIntrospectionType(type)) { @@ -333,26 +405,57 @@ private TypePrinter enumPrinter() { .parentType(GraphQLEnumType.class) .elementType(GraphQLEnumValueDefinition.class) .build(); - Comparator comparator = options.comparatorRegistry.getComparator(environment); - - printComments(out, type, ""); - out.format("enum %s%s", type.getName(), directivesString(GraphQLEnumType.class, type.getDirectives())); - List values = type.getValues() - .stream() - .sorted(comparator) - .collect(toList()); - if (values.size() > 0) { - out.format(" {\n"); - for (GraphQLEnumValueDefinition enumValueDefinition : values) { - printComments(out, enumValueDefinition, " "); - out.format(" %s%s\n", enumValueDefinition.getName(), directivesString(GraphQLEnumValueDefinition.class, enumValueDefinition.getDirectives())); + Comparator comparator = options.comparatorRegistry.getComparator(environment); + + if (shouldPrintAsAst(type.getDefinition())) { + printAsAst(out, type.getDefinition(), type.getExtensionDefinitions()); + } else { + printComments(out, type, ""); + out.format("enum %s%s", type.getName(), directivesString(GraphQLEnumType.class, type.getDirectives())); + List values = type.getValues() + .stream() + .sorted(comparator) + .collect(toList()); + if (values.size() > 0) { + out.format(" {\n"); + for (GraphQLEnumValueDefinition enumValueDefinition : values) { + printComments(out, enumValueDefinition, " "); + List enumValueDirectives = enumValueDefinition.getDirectives(); + if (enumValueDefinition.isDeprecated()) { + enumValueDirectives = addDeprecatedDirectiveIfNeeded(enumValueDirectives); + } + out.format(" %s%s\n", enumValueDefinition.getName(), directivesString(GraphQLEnumValueDefinition.class, enumValueDirectives)); + } + out.format("}"); } - out.format("}"); + out.format("\n\n"); } - out.format("\n\n"); }; } + private void printFieldDefinitions(PrintWriter out, Comparator comparator, List fieldDefinitions) { + if (fieldDefinitions.size() == 0) { + return; + } + + out.format(" {\n"); + fieldDefinitions + .stream() + .sorted(comparator) + .forEach(fd -> { + printComments(out, fd, " "); + List fieldDirectives = fd.getDirectives(); + if (fd.isDeprecated()) { + fieldDirectives = addDeprecatedDirectiveIfNeeded(fieldDirectives); + } + + out.format(" %s%s: %s%s\n", + fd.getName(), argsString(GraphQLFieldDefinition.class, fd.getArguments()), typeString(fd.getType()), + directivesString(GraphQLFieldDefinition.class, fieldDirectives)); + }); + out.format("}"); + } + private TypePrinter interfacePrinter() { return (out, type, visibility) -> { if (isIntrospectionType(type)) { @@ -363,25 +466,16 @@ private TypePrinter interfacePrinter() { .parentType(GraphQLInterfaceType.class) .elementType(GraphQLFieldDefinition.class) .build(); - Comparator comparator = options.comparatorRegistry.getComparator(environment); - - printComments(out, type, ""); - out.format("interface %s%s", type.getName(), directivesString(GraphQLInterfaceType.class, type.getDirectives())); - List fieldDefinitions = visibility.getFieldDefinitions(type); - if (fieldDefinitions.size() > 0) { - out.format(" {\n"); - fieldDefinitions - .stream() - .sorted(comparator) - .forEach(fd -> { - printComments(out, fd, " "); - out.format(" %s%s: %s%s\n", - fd.getName(), argsString(GraphQLFieldDefinition.class, fd.getArguments()), typeString(fd.getType()), - directivesString(GraphQLFieldDefinition.class, fd.getDirectives())); - }); - out.format("}"); + Comparator comparator = options.comparatorRegistry.getComparator(environment); + + if (shouldPrintAsAst(type.getDefinition())) { + printAsAst(out, type.getDefinition(), type.getExtensionDefinitions()); + } else { + printComments(out, type, ""); + out.format("interface %s%s", type.getName(), directivesString(GraphQLInterfaceType.class, type.getDirectives())); + printFieldDefinitions(out, comparator, visibility.getFieldDefinitions(type)); + out.format("\n\n"); } - out.format("\n\n"); }; } @@ -395,22 +489,26 @@ private TypePrinter unionPrinter() { .parentType(GraphQLUnionType.class) .elementType(GraphQLOutputType.class) .build(); - Comparator comparator = options.comparatorRegistry.getComparator(environment); - - printComments(out, type, ""); - out.format("union %s%s = ", type.getName(), directivesString(GraphQLUnionType.class, type.getDirectives())); - List types = type.getTypes() - .stream() - .sorted(comparator) - .collect(toList()); - for (int i = 0; i < types.size(); i++) { - GraphQLOutputType objectType = types.get(i); - if (i > 0) { - out.format(" | "); + Comparator comparator = options.comparatorRegistry.getComparator(environment); + + if (shouldPrintAsAst(type.getDefinition())) { + printAsAst(out, type.getDefinition(), type.getExtensionDefinitions()); + } else { + printComments(out, type, ""); + out.format("union %s%s = ", type.getName(), directivesString(GraphQLUnionType.class, type.getDirectives())); + List types = type.getTypes() + .stream() + .sorted(comparator) + .collect(toList()); + for (int i = 0; i < types.size(); i++) { + GraphQLNamedOutputType objectType = types.get(i); + if (i > 0) { + out.format(" | "); + } + out.format("%s", objectType.getName()); } - out.format("%s", objectType.getName()); + out.format("\n\n"); } - out.format("\n\n"); }; } @@ -419,48 +517,39 @@ private TypePrinter objectPrinter() { if (isIntrospectionType(type)) { return; } - printComments(out, type, ""); - if (type.getInterfaces().isEmpty()) { - out.format("type %s%s", type.getName(), directivesString(GraphQLObjectType.class, type.getDirectives())); + if (shouldPrintAsAst(type.getDefinition())) { + printAsAst(out, type.getDefinition(), type.getExtensionDefinitions()); } else { + printComments(out, type, ""); + if (type.getInterfaces().isEmpty()) { + out.format("type %s%s", type.getName(), directivesString(GraphQLObjectType.class, type.getDirectives())); + } else { + + GraphqlTypeComparatorEnvironment environment = GraphqlTypeComparatorEnvironment.newEnvironment() + .parentType(GraphQLObjectType.class) + .elementType(GraphQLOutputType.class) + .build(); + Comparator implementsComparator = options.comparatorRegistry.getComparator(environment); + + Stream interfaceNames = type.getInterfaces() + .stream() + .sorted(implementsComparator) + .map(GraphQLNamedType::getName); + out.format("type %s implements %s%s", + type.getName(), + interfaceNames.collect(joining(" & ")), + directivesString(GraphQLObjectType.class, type.getDirectives())); + } GraphqlTypeComparatorEnvironment environment = GraphqlTypeComparatorEnvironment.newEnvironment() .parentType(GraphQLObjectType.class) - .elementType(GraphQLOutputType.class) + .elementType(GraphQLFieldDefinition.class) .build(); - Comparator implementsComparator = options.comparatorRegistry.getComparator(environment); + Comparator comparator = options.comparatorRegistry.getComparator(environment); - Stream interfaceNames = type.getInterfaces() - .stream() - .sorted(implementsComparator) - .map(GraphQLType::getName); - out.format("type %s implements %s%s", - type.getName(), - interfaceNames.collect(joining(" & ")), - directivesString(GraphQLObjectType.class, type.getDirectives())); + printFieldDefinitions(out, comparator, visibility.getFieldDefinitions(type)); + out.format("\n\n"); } - - GraphqlTypeComparatorEnvironment environment = GraphqlTypeComparatorEnvironment.newEnvironment() - .parentType(GraphQLObjectType.class) - .elementType(GraphQLFieldDefinition.class) - .build(); - Comparator comparator = options.comparatorRegistry.getComparator(environment); - - List fieldDefinitions = visibility.getFieldDefinitions(type); - if (fieldDefinitions.size() > 0) { - out.format(" {\n"); - fieldDefinitions - .stream() - .sorted(comparator) - .forEach(fd -> { - printComments(out, fd, " "); - out.format(" %s%s: %s%s\n", - fd.getName(), argsString(GraphQLFieldDefinition.class, fd.getArguments()), typeString(fd.getType()), - directivesString(GraphQLFieldDefinition.class, fd.getDirectives())); - }); - out.format("}"); - } - out.format("\n\n"); }; } @@ -469,37 +558,69 @@ private TypePrinter inputObjectPrinter() { if (isIntrospectionType(type)) { return; } - printComments(out, type, ""); + if (shouldPrintAsAst(type.getDefinition())) { + printAsAst(out, type.getDefinition(), type.getExtensionDefinitions()); + } else { + printComments(out, type, ""); + GraphqlTypeComparatorEnvironment environment = GraphqlTypeComparatorEnvironment.newEnvironment() + .parentType(GraphQLInputObjectType.class) + .elementType(GraphQLInputObjectField.class) + .build(); + Comparator comparator = options.comparatorRegistry.getComparator(environment); + + out.format("input %s%s", type.getName(), directivesString(GraphQLInputObjectType.class, type.getDirectives())); + List inputObjectFields = visibility.getFieldDefinitions(type); + if (inputObjectFields.size() > 0) { + out.format(" {\n"); + inputObjectFields + .stream() + .sorted(comparator) + .forEach(fd -> { + printComments(out, fd, " "); + out.format(" %s: %s", + fd.getName(), typeString(fd.getType())); + Object defaultValue = fd.getDefaultValue(); + if (defaultValue != null) { + String astValue = printAst(defaultValue, fd.getType()); + out.format(" = %s", astValue); + } + out.format(directivesString(GraphQLInputObjectField.class, fd.getDirectives())); + out.format("\n"); + }); + out.format("}"); + } + out.format("\n\n"); + } + }; + } - GraphqlTypeComparatorEnvironment environment = GraphqlTypeComparatorEnvironment.newEnvironment() - .parentType(GraphQLInputObjectType.class) - .elementType(GraphQLInputObjectField.class) - .build(); - Comparator comparator = options.comparatorRegistry.getComparator(environment); + /** + * This will return true if the options say to use the AST and we have an AST element + * + * @param definition the AST type definition + * @return true if we should print using AST nodes + */ + private boolean shouldPrintAsAst(TypeDefinition definition) { + return options.isUseAstDefinitions() && definition != null; + } - out.format("input %s%s", type.getName(), directivesString(GraphQLInputObjectType.class, type.getDirectives())); - List inputObjectFields = visibility.getFieldDefinitions(type); - if (inputObjectFields.size() > 0) { - out.format(" {\n"); - inputObjectFields - .stream() - .sorted(comparator) - .forEach(fd -> { - printComments(out, fd, " "); - out.format(" %s: %s", - fd.getName(), typeString(fd.getType())); - Object defaultValue = fd.getDefaultValue(); - if (defaultValue != null) { - String astValue = printAst(defaultValue, fd.getType()); - out.format(" = %s", astValue); - } - out.format(directivesString(GraphQLInputObjectField.class, fd.getDirectives())); - out.format("\n"); - }); - out.format("}"); + /** + * This will print out a runtime graphql schema element using its contained AST type definition. This + * must be guarded by a called to {@link #shouldPrintAsAst(TypeDefinition)} + * + * @param out the output writer + * @param definition the AST type definition + * @param extensions a list of type definition extensions + */ + private void printAsAst(PrintWriter out, TypeDefinition definition, List extensions) { + out.printf("%s\n", AstPrinter.printAst(definition)); + if (extensions != null) { + for (TypeDefinition extension : extensions) { + out.printf("\n%s\n", AstPrinter.printAst(extension)); } - out.format("\n\n"); - }; + } + out.println(); } private static String printAst(Object value, GraphQLInputType type) { @@ -514,7 +635,7 @@ private TypePrinter schemaPrinter() { // when serializing a GraphQL schema using the type system language, a // schema definition should be omitted if only uses the default root type names. - boolean needsSchemaPrinted = options.includeSchemaDefinition; + boolean needsSchemaPrinted = options.isIncludeSchemaDefinition(); if (!needsSchemaPrinted) { if (queryType != null && !queryType.getName().equals("Query")) { @@ -542,26 +663,18 @@ private TypePrinter schemaPrinter() { out.format("}\n\n"); } - if (options.includeDirectives) { - out.format("%s", directiveDefinitions(getDirectives(schema))); + List directives = getSchemaDirectives(schema); + if (!directives.isEmpty()) { + out.format("%s", directiveDefinitions(directives)); } }; } - private List getDirectives(GraphQLSchema schema) { - - // we don't print the standard directives that always ship with graphql-java - List standardDirectives = Arrays.asList( - "skip", - "include", - "defer", - "deprecated"); - - Predicate standard = directive -> standardDirectives.contains(directive.getName()); + private List getSchemaDirectives(GraphQLSchema schema) { return schema.getDirectives().stream() - .filter(standard.negate()) - .filter(options.includeDirectiveDefinition) - .collect(Collectors.toList()); + .filter(options.getIncludeDirective()) + .filter(options.getIncludeDirectiveDefinition()) + .collect(toList()); } String typeString(GraphQLType rawType) { @@ -572,8 +685,8 @@ String argsString(List arguments) { return argsString(null, arguments); } - String argsString(Class parent, List arguments) { - boolean hasDescriptions = arguments.stream().anyMatch(arg -> !isNullOrEmpty(arg.getDescription())); + String argsString(Class parent, List arguments) { + boolean hasDescriptions = arguments.stream().anyMatch(this::hasDescription); String halfPrefix = hasDescriptions ? " " : ""; String prefix = hasDescriptions ? " " : ""; int count = 0; @@ -583,7 +696,7 @@ String argsString(Class parent, List arg .parentType(parent) .elementType(GraphQLArgument.class) .build(); - Comparator comparator = options.comparatorRegistry.getComparator(environment); + Comparator comparator = options.comparatorRegistry.getComparator(environment); arguments = arguments .stream() @@ -598,19 +711,8 @@ String argsString(Class parent, List arg if (hasDescriptions) { sb.append("\n"); } - String description = argument.getDescription(); - if (!isNullOrEmpty(description)) { - String[] descriptionSplitByNewlines = description.split("\n"); - Stream stream = Arrays.stream(descriptionSplitByNewlines); - if (descriptionSplitByNewlines.length > 1) { - String multiLineComment = "\"\"\""; - stream = Stream.concat(Stream.of(multiLineComment), stream); - stream = Stream.concat(stream, Stream.of(multiLineComment)); - stream.map(s -> prefix + s + "\n").forEach(sb::append); - } else { - stream.map(s -> prefix + "#" + s + "\n").forEach(sb::append); - } - } + sb.append(printComments(argument, prefix)); + sb.append(prefix).append(argument.getName()).append(": ").append(typeString(argument.getType())); Object defaultValue = argument.getDefaultValue(); if (defaultValue != null) { @@ -634,8 +736,13 @@ String argsString(Class parent, List arg return sb.toString(); } - String directivesString(Class parent, List directives) { - if (!options.includeDirectives) { + String directivesString(Class parent, List directives) { + directives = directives.stream() + // @deprecated is special - we always print it if something is deprecated + .filter(directive -> options.getIncludeDirective().test(directive) || isDeprecatedDirective(directive)) + .collect(toList()); + + if (directives.isEmpty()) { return ""; } StringBuilder sb = new StringBuilder(); @@ -647,7 +754,7 @@ String directivesString(Class parent, List comparator = options.comparatorRegistry.getComparator(environment); + Comparator comparator = options.comparatorRegistry.getComparator(environment); directives = directives .stream() @@ -664,8 +771,11 @@ String directivesString(Class parent, List comparator = options.comparatorRegistry.getComparator(environment); + Comparator comparator = options.comparatorRegistry.getComparator(environment); List args = directive.getArguments(); args = args @@ -686,16 +796,19 @@ private String directiveString(GraphQLDirective directive) { sb.append("("); for (int i = 0; i < args.size(); i++) { GraphQLArgument arg = args.get(i); - sb.append(arg.getName()); + String argValue = null; if (arg.getValue() != null) { - sb.append(" : "); - sb.append(printAst(arg.getValue(), arg.getType())); + argValue = printAst(arg.getValue(), arg.getType()); } else if (arg.getDefaultValue() != null) { - sb.append(" : "); - sb.append(printAst(arg.getDefaultValue(), arg.getType())); + argValue = printAst(arg.getDefaultValue(), arg.getType()); } - if (i < args.size() - 1) { - sb.append(", "); + if (!isNullOrEmpty(argValue)) { + sb.append(arg.getName()); + sb.append(" : "); + sb.append(argValue); + if (i < args.size() - 1) { + sb.append(", "); + } } } sb.append(")"); @@ -703,6 +816,24 @@ private String directiveString(GraphQLDirective directive) { return sb.toString(); } + private boolean isDeprecatedDirective(GraphQLDirective directive) { + return directive.getName().equals(DeprecatedDirective.getName()); + } + + private boolean hasDeprecatedDirective(List directives) { + return directives.stream() + .filter(this::isDeprecatedDirective) + .count() == 1; + } + + private List addDeprecatedDirectiveIfNeeded(List directives) { + if (!hasDeprecatedDirective(directives)) { + directives = new ArrayList<>(directives); + directives.add(DeprecatedDirective4Printing); + } + return directives; + } + private String directiveDefinitions(List directives) { StringBuilder sb = new StringBuilder(); for (GraphQLDirective directive : directives) { @@ -726,7 +857,7 @@ private String directiveDefinition(GraphQLDirective directive) { .parentType(GraphQLDirective.class) .elementType(GraphQLArgument.class) .build(); - Comparator comparator = options.comparatorRegistry.getComparator(environment); + Comparator comparator = options.comparatorRegistry.getComparator(environment); List args = directive.getArguments(); args = args @@ -747,19 +878,20 @@ private String directiveDefinition(GraphQLDirective directive) { @SuppressWarnings("unchecked") private TypePrinter printer(Class clazz) { - TypePrinter typePrinter = printers.computeIfAbsent(clazz, k -> { + TypePrinter typePrinter = printers.get(clazz); + if (typePrinter == null) { Class superClazz = clazz.getSuperclass(); - TypePrinter result; if (superClazz != Object.class) { - result = printer(superClazz); + typePrinter = printer(superClazz); } else { - result = (out, type, visibility) -> out.println("Type not implemented : " + type); + typePrinter = (out, type, visibility) -> out.println("Type not implemented : " + type); } - return result; - }); + printers.put(clazz, typePrinter); + } return (TypePrinter) typePrinter; } + public String print(GraphQLType type) { StringWriter sw = new StringWriter(); PrintWriter out = new PrintWriter(sw); @@ -770,7 +902,8 @@ public String print(GraphQLType type) { } @SuppressWarnings("unchecked") - private void printType(PrintWriter out, List typesAsList, Class typeClazz, GraphqlFieldVisibility visibility) { + private void printType(PrintWriter out, List typesAsList, Class + typeClazz, GraphqlFieldVisibility visibility) { typesAsList.stream() .filter(type -> typeClazz.isAssignableFrom(type.getClass())) .forEach(type -> printType(out, type, visibility)); @@ -781,115 +914,104 @@ private void printType(PrintWriter out, GraphQLType type, GraphqlFieldVisibility printer.print(out, type, visibility); } - private void printComments(PrintWriter out, Object graphQLType, String prefix) { - - AstDescriptionAndComments descriptionAndComments = getDescriptionAndComments(graphQLType); - if (descriptionAndComments == null) { - return; - } + private String printComments(Object graphQLType, String prefix) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + printComments(pw, graphQLType, prefix); + return sw.toString(); + } - Description astDescription = descriptionAndComments.descriptionAst; - if (astDescription != null) { - String quoteStr = "\""; - if (astDescription.isMultiLine()) { - quoteStr = "\"\"\""; - } - out.write(prefix); - out.write(quoteStr); - out.write(astDescription.getContent()); - out.write(quoteStr); - out.write("\n"); + private void printComments(PrintWriter out, Object graphQLType, String prefix) { + String descriptionText = getDescription(graphQLType); + if (isNullOrEmpty(descriptionText)) { return; } - if (descriptionAndComments.comments != null) { - descriptionAndComments.comments.forEach(cmt -> { - String commentText = cmt.getContent() == null ? "" : cmt.getContent(); - // it possible that in fact they manage to sneak in a multi line comment - // into what should be a single line comment. So cater for that. - List lines = Arrays.asList(commentText.split("\n")); - lines.forEach(t -> { - out.write(prefix); - out.write("#"); - out.write(commentText); - out.write("\n"); - }); - }); - } else { - String runtimeDescription = descriptionAndComments.runtimeDescription; - if (!isNullOrEmpty(runtimeDescription)) { - Stream stream = Arrays.stream(runtimeDescription.split("\n")); - stream.map(s -> prefix + "#" + s + "\n").forEach(out::write); + if (!isNullOrEmpty(descriptionText)) { + List lines = Arrays.asList(descriptionText.split("\n")); + if (options.isDescriptionsAsHashComments()) { + printMultiLineHashDescription(out, prefix, lines); + } else { + if (lines.size() > 1) { + printMultiLineDescription(out, prefix, lines); + } else { + printSingleLineDescription(out, prefix, lines.get(0)); + } } } } - static class AstDescriptionAndComments { - - String runtimeDescription; + private void printMultiLineHashDescription(PrintWriter out, String prefix, List lines) { + lines.forEach(l -> out.printf("%s#%s\n", prefix, l)); + } - Description descriptionAst; + private void printMultiLineDescription(PrintWriter out, String prefix, List lines) { + out.printf("%s\"\"\"\n", prefix); + lines.forEach(l -> out.printf("%s%s\n", prefix, l)); + out.printf("%s\"\"\"\n", prefix); + } - List comments; + private void printSingleLineDescription(PrintWriter out, String prefix, String s) { + out.printf("%s\"%s\"\n", prefix, s); + } - public AstDescriptionAndComments(String runtimeDescription, Description descriptionAst, List comments) { - this.runtimeDescription = runtimeDescription; - this.descriptionAst = descriptionAst; - this.comments = comments; - } + private boolean hasDescription(Object descriptionHolder) { + String description = getDescription(descriptionHolder); + return !isNullOrEmpty(description); } - private AstDescriptionAndComments getDescriptionAndComments(Object descriptionHolder) { + private String getDescription(Object descriptionHolder) { if (descriptionHolder instanceof GraphQLObjectType) { GraphQLObjectType type = (GraphQLObjectType) descriptionHolder; - return descriptionAndComments(type::getDescription, type::getDefinition, () -> type.getDefinition().getDescription()); + return description(type.getDescription(), ofNullable(type.getDefinition()).map(ObjectTypeDefinition::getDescription).orElse(null)); } else if (descriptionHolder instanceof GraphQLEnumType) { GraphQLEnumType type = (GraphQLEnumType) descriptionHolder; - return descriptionAndComments(type::getDescription, type::getDefinition, () -> type.getDefinition().getDescription()); + return description(type.getDescription(), ofNullable(type.getDefinition()).map(EnumTypeDefinition::getDescription).orElse(null)); } else if (descriptionHolder instanceof GraphQLFieldDefinition) { GraphQLFieldDefinition type = (GraphQLFieldDefinition) descriptionHolder; - return descriptionAndComments(type::getDescription, type::getDefinition, () -> type.getDefinition().getDescription()); + return description(type.getDescription(), ofNullable(type.getDefinition()).map(FieldDefinition::getDescription).orElse(null)); } else if (descriptionHolder instanceof GraphQLEnumValueDefinition) { GraphQLEnumValueDefinition type = (GraphQLEnumValueDefinition) descriptionHolder; - return descriptionAndComments(type::getDescription, () -> null, () -> null); + return description(type.getDescription(), ofNullable(type.getDefinition()).map(EnumValueDefinition::getDescription).orElse(null)); } else if (descriptionHolder instanceof GraphQLUnionType) { GraphQLUnionType type = (GraphQLUnionType) descriptionHolder; - return descriptionAndComments(type::getDescription, type::getDefinition, () -> type.getDefinition().getDescription()); + return description(type.getDescription(), ofNullable(type.getDefinition()).map(UnionTypeDefinition::getDescription).orElse(null)); } else if (descriptionHolder instanceof GraphQLInputObjectType) { GraphQLInputObjectType type = (GraphQLInputObjectType) descriptionHolder; - return descriptionAndComments(type::getDescription, type::getDefinition, () -> type.getDefinition().getDescription()); + return description(type.getDescription(), ofNullable(type.getDefinition()).map(InputObjectTypeDefinition::getDescription).orElse(null)); } else if (descriptionHolder instanceof GraphQLInputObjectField) { GraphQLInputObjectField type = (GraphQLInputObjectField) descriptionHolder; - return descriptionAndComments(type::getDescription, type::getDefinition, () -> type.getDefinition().getDescription()); + return description(type.getDescription(), ofNullable(type.getDefinition()).map(InputValueDefinition::getDescription).orElse(null)); } else if (descriptionHolder instanceof GraphQLInterfaceType) { GraphQLInterfaceType type = (GraphQLInterfaceType) descriptionHolder; - return descriptionAndComments(type::getDescription, type::getDefinition, () -> type.getDefinition().getDescription()); + return description(type.getDescription(), ofNullable(type.getDefinition()).map(InterfaceTypeDefinition::getDescription).orElse(null)); } else if (descriptionHolder instanceof GraphQLScalarType) { GraphQLScalarType type = (GraphQLScalarType) descriptionHolder; - return descriptionAndComments(type::getDescription, type::getDefinition, () -> type.getDefinition().getDescription()); + return description(type.getDescription(), ofNullable(type.getDefinition()).map(ScalarTypeDefinition::getDescription).orElse(null)); } else if (descriptionHolder instanceof GraphQLArgument) { GraphQLArgument type = (GraphQLArgument) descriptionHolder; - return descriptionAndComments(type::getDescription, type::getDefinition, () -> type.getDefinition().getDescription()); + return description(type.getDescription(), ofNullable(type.getDefinition()).map(InputValueDefinition::getDescription).orElse(null)); } else if (descriptionHolder instanceof GraphQLDirective) { GraphQLDirective type = (GraphQLDirective) descriptionHolder; - return descriptionAndComments(type::getDescription, () -> null, () -> null); + return description(type.getDescription(), null); } else { return Assert.assertShouldNeverHappen(); } } - AstDescriptionAndComments descriptionAndComments(Supplier runtimeDescriptionSupplier, Supplier nodeSupplier, Supplier descriptionSupplier) { - String runtimeDesc = runtimeDescriptionSupplier.get(); - Node node = nodeSupplier.get(); - Description description = null; - List comments = null; - if (node != null) { - comments = node.getComments(); - description = descriptionSupplier.get(); + String description(String runtimeDescription, Description descriptionAst) { + // + // 95% of the time if the schema was built from SchemaGenerator then the runtime description is the only description + // So the other code here is a really defensive way to get the description + // + String descriptionText = runtimeDescription; + if (isNullOrEmpty(descriptionText)) { + if (descriptionAst != null) { + descriptionText = descriptionAst.getContent(); + } } - return new AstDescriptionAndComments(runtimeDesc, description, comments); - + return descriptionText; } private static boolean isNullOrEmpty(String s) { diff --git a/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/SchemaTransformer.java b/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/SchemaTransformer.java index 6efd1e7f..f034eb77 100644 --- a/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/SchemaTransformer.java +++ b/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/SchemaTransformer.java @@ -7,6 +7,7 @@ import graphql.schema.FieldCoordinates; import graphql.schema.GraphQLCodeRegistry; import graphql.schema.GraphQLDirectiveContainer; +import graphql.schema.GraphQLNamedType; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import graphql.schema.GraphQLType; @@ -15,6 +16,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -22,6 +24,9 @@ public final class SchemaTransformer { private static final Object DUMMY = new Object(); + // Apollo Gateway will fail composition if it sees standard directive definitions. + private static final Set STANDARD_DIRECTIVES = + new HashSet<>(Arrays.asList("deprecated", "include", "skip")); private final GraphQLSchema originalSchema; private TypeResolver entityTypeResolver = null; private DataFetcher entitiesDataFetcher = null; @@ -88,7 +93,7 @@ public final GraphQLSchema build() throws SchemaProblem { final Set entityTypeNames = originalSchema.getAllTypesAsList().stream() .filter(t -> t instanceof GraphQLDirectiveContainer && ((GraphQLDirectiveContainer) t).getDirective(FederationDirectives.keyName) != null) - .map(GraphQLType::getName) + .map(GraphQLNamedType::getName) .collect(Collectors.toSet()); final Set entityConcreteTypeNames = originalSchema.getAllTypesAsList() @@ -98,7 +103,7 @@ public final GraphQLSchema build() throws SchemaProblem { ((GraphQLObjectType) type).getInterfaces() .stream() .anyMatch(itf -> entityTypeNames.contains(itf.getName()))) - .map(GraphQLType::getName) + .map(GraphQLNamedType::getName) .collect(Collectors.toSet()); // If there are entity types install: Query._entities(representations: [_Any!]!): [_Entity]! @@ -140,7 +145,9 @@ public final GraphQLSchema build() throws SchemaProblem { public static String sdl(GraphQLSchema schema) { // Gather directive definitions to hide. - final Set hiddenDirectiveDefinitions = new HashSet<>(FederationDirectives.allNames); + final Set hiddenDirectiveDefinitions = new HashSet<>(); + hiddenDirectiveDefinitions.addAll(STANDARD_DIRECTIVES); + hiddenDirectiveDefinitions.addAll(FederationDirectives.allNames); // Gather type definitions to hide. final Set hiddenTypeDefinitions = new HashSet<>(); @@ -162,7 +169,7 @@ public static String sdl(GraphQLSchema schema) { final FederationSdlPrinter.Options options = FederationSdlPrinter.Options.defaultOptions() .includeScalarTypes(true) .includeExtendedScalarTypes(true) - .includeSchemaDefintion(true) + .includeSchemaDefinition(true) .includeDirectives(true) .includeDirectiveDefinitions(def -> !hiddenDirectiveDefinitions.contains(def.getName())) .includeTypeDefinitions(def -> !hiddenTypeDefinitions.contains(def.getName())); diff --git a/graphql-java-support/src/test/java/com/apollographql/federation/graphqljava/FederationTest.java b/graphql-java-support/src/test/java/com/apollographql/federation/graphqljava/FederationTest.java index 79b9213c..015ccaa6 100644 --- a/graphql-java-support/src/test/java/com/apollographql/federation/graphqljava/FederationTest.java +++ b/graphql-java-support/src/test/java/com/apollographql/federation/graphqljava/FederationTest.java @@ -3,6 +3,7 @@ import graphql.ExecutionResult; import graphql.Scalars; import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLNamedType; import graphql.schema.GraphQLScalarType; import graphql.schema.GraphQLSchema; import graphql.schema.GraphQLType; @@ -17,8 +18,10 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -34,6 +37,8 @@ class FederationTest { private final String printerEmptySDL = TestUtils.readResource("schemas/printerEmpty.graphql"); private final String printerFilterSDL = TestUtils.readResource("schemas/printerFilter.graphql"); private final String printerFilterExpectedSDL = TestUtils.readResource("schemas/printerFilterExpected.graphql"); + private final Set standardDirectives = + new HashSet<>(Arrays.asList("deprecated", "include", "skip")); @Test void testEmpty() { @@ -57,7 +62,12 @@ void testEmpty() { " sdl: String!\n" + "}\n" + "\n" + - "scalar _FieldSet\n", SchemaUtils.printSchema(federated)); + "scalar _FieldSet\n", + new FederationSdlPrinter(FederationSdlPrinter.Options.defaultOptions() + .includeScalarTypes(true) + .includeDirectiveDefinitions(def -> !standardDirectives.contains(def.getName())) + ).print(federated) + ); final GraphQLType _Service = federated.getType("_Service"); assertNotNull(_Service, "_Service type present"); @@ -141,7 +151,7 @@ void testInterfacesAreCovered() { final Iterable unionTypes = entityType .getTypes() .stream() - .map(GraphQLType::getName) + .map(GraphQLNamedType::getName) .sorted() .collect(Collectors.toList()); @@ -163,7 +173,12 @@ void testPrinterEmpty() { typeDefinitionRegistry, runtimeWiring ); - Assertions.assertEquals(printerEmptySDL.trim(), new FederationSdlPrinter().print(graphQLSchema).trim()); + Assertions.assertEquals( + printerEmptySDL.trim(), + new FederationSdlPrinter(FederationSdlPrinter.Options.defaultOptions() + .includeDirectiveDefinitions(def -> !standardDirectives.contains(def.getName())) + ).print(graphQLSchema).trim() + ); } @Test @@ -195,7 +210,9 @@ void testPrinterFilter() { printerFilterExpectedSDL.trim(), new FederationSdlPrinter(FederationSdlPrinter.Options.defaultOptions() .includeScalarTypes(true) - .includeDirectiveDefinitions(def -> !def.getName().endsWith("1")) + .includeDirectiveDefinitions(def -> + !def.getName().endsWith("1") && !standardDirectives.contains(def.getName()) + ) .includeTypeDefinitions(def -> !def.getName().endsWith("1")) ).print(graphQLSchema).trim() ); diff --git a/graphql-java-support/src/test/java/com/apollographql/federation/graphqljava/SchemaUtils.java b/graphql-java-support/src/test/java/com/apollographql/federation/graphqljava/SchemaUtils.java index 285cef22..25160b02 100644 --- a/graphql-java-support/src/test/java/com/apollographql/federation/graphqljava/SchemaUtils.java +++ b/graphql-java-support/src/test/java/com/apollographql/federation/graphqljava/SchemaUtils.java @@ -15,12 +15,6 @@ final class SchemaUtils { private SchemaUtils() { } - static String printSchema(GraphQLSchema schema) { - return new FederationSdlPrinter(FederationSdlPrinter.Options.defaultOptions() - .includeScalarTypes(true) - ).print(schema); - } - static ExecutionResult execute(GraphQLSchema schema, String query) { return newGraphQL(schema).build().execute(newExecutionInput().query(query).build()); } diff --git a/graphql-java-support/src/test/resources/com/apollographql/federation/graphqljava/schemas/product.graphql b/graphql-java-support/src/test/resources/com/apollographql/federation/graphqljava/schemas/product.graphql index 7f96ecfb..f5e2213e 100644 --- a/graphql-java-support/src/test/resources/com/apollographql/federation/graphqljava/schemas/product.graphql +++ b/graphql-java-support/src/test/resources/com/apollographql/federation/graphqljava/schemas/product.graphql @@ -4,7 +4,7 @@ schema { type Money { amount: Int! - currencyCode: String! + currencyCode: String! @deprecated(reason : "dummy") } type Product @key(fields : "upc") { diff --git a/pom.xml b/pom.xml index 01cfc791..5c9be9dc 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ ${java.version} ${java.version} 2.7 - 13.0 + 14.0 5.10.0 1.8 17.0.0