diff --git a/src/main/java/graphql/language/ImplementingTypeDefinition.java b/src/main/java/graphql/language/ImplementingTypeDefinition.java index 31effe0d11..405ee1ccfa 100644 --- a/src/main/java/graphql/language/ImplementingTypeDefinition.java +++ b/src/main/java/graphql/language/ImplementingTypeDefinition.java @@ -2,6 +2,7 @@ import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import java.util.List; @@ -11,6 +12,7 @@ * @param for two */ @PublicApi +@NullMarked public interface ImplementingTypeDefinition extends TypeDefinition { List getImplements(); diff --git a/src/main/java/graphql/language/InlineFragment.java b/src/main/java/graphql/language/InlineFragment.java index b065a5b8df..173427071e 100644 --- a/src/main/java/graphql/language/InlineFragment.java +++ b/src/main/java/graphql/language/InlineFragment.java @@ -7,6 +7,9 @@ import graphql.collect.ImmutableKit; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -20,8 +23,9 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class InlineFragment extends AbstractNode implements Selection, SelectionSetContainer, DirectivesContainer { - private final TypeName typeCondition; + private final @Nullable TypeName typeCondition; private final NodeUtil.DirectivesHolder directives; private final SelectionSet selectionSet; @@ -30,10 +34,10 @@ public class InlineFragment extends AbstractNode implements Sele public static final String CHILD_SELECTION_SET = "selectionSet"; @Internal - protected InlineFragment(TypeName typeCondition, + protected InlineFragment(@Nullable TypeName typeCondition, List directives, SelectionSet selectionSet, - SourceLocation sourceLocation, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -48,8 +52,8 @@ protected InlineFragment(TypeName typeCondition, * * @param typeCondition the type condition of the inline fragment */ - public InlineFragment(TypeName typeCondition) { - this(typeCondition, emptyList(), null, null, emptyList(), IgnoredChars.EMPTY, emptyMap()); + public InlineFragment(@Nullable TypeName typeCondition) { + this(typeCondition, emptyList(), SelectionSet.newSelectionSet().build(), null, emptyList(), IgnoredChars.EMPTY, emptyMap()); } /** @@ -58,11 +62,11 @@ public InlineFragment(TypeName typeCondition) { * @param typeCondition the type condition of the inline fragment * @param selectionSet of the inline fragment */ - public InlineFragment(TypeName typeCondition, SelectionSet selectionSet) { + public InlineFragment(@Nullable TypeName typeCondition, SelectionSet selectionSet) { this(typeCondition, emptyList(), selectionSet, null, emptyList(), IgnoredChars.EMPTY, emptyMap()); } - public TypeName getTypeCondition() { + public @Nullable TypeName getTypeCondition() { return typeCondition; } @@ -121,7 +125,7 @@ public InlineFragment withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -132,8 +136,8 @@ public boolean isEqualTo(Node o) { public InlineFragment deepCopy() { return new InlineFragment( deepCopy(typeCondition), - deepCopy(directives.getDirectives()), - deepCopy(selectionSet), + assertNotNull(deepCopy(directives.getDirectives()), "directives cannot be null"), + assertNotNull(deepCopy(selectionSet), "selectionSet cannot be null"), getSourceLocation(), getComments(), getIgnoredChars(), @@ -164,6 +168,7 @@ public InlineFragment transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/InputObjectTypeDefinition.java b/src/main/java/graphql/language/InputObjectTypeDefinition.java index d545814091..ce1a9c7425 100644 --- a/src/main/java/graphql/language/InputObjectTypeDefinition.java +++ b/src/main/java/graphql/language/InputObjectTypeDefinition.java @@ -7,6 +7,9 @@ import graphql.util.FpKit; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.List; @@ -19,6 +22,7 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class InputObjectTypeDefinition extends AbstractDescribedNode implements TypeDefinition, DirectivesContainer, NamedNode { private final String name; @@ -32,8 +36,8 @@ public class InputObjectTypeDefinition extends AbstractDescribedNode directives, List inputValueDefinitions, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -94,7 +98,7 @@ public InputObjectTypeDefinition withNewChildren(NodeChildrenContainer newChildr } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -110,8 +114,8 @@ public boolean isEqualTo(Node o) { @Override public InputObjectTypeDefinition deepCopy() { return new InputObjectTypeDefinition(name, - deepCopy(directives.getDirectives()), - deepCopy(inputValueDefinitions), + assertNotNull(deepCopy(directives.getDirectives()), "directives cannot be null"), + assertNotNull(deepCopy(inputValueDefinitions), "inputValueDefinitions cannot be null"), description, getSourceLocation(), getComments(), @@ -144,6 +148,7 @@ public InputObjectTypeDefinition transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/InputObjectTypeExtensionDefinition.java b/src/main/java/graphql/language/InputObjectTypeExtensionDefinition.java index 0835c0eb44..76c1b4055c 100644 --- a/src/main/java/graphql/language/InputObjectTypeExtensionDefinition.java +++ b/src/main/java/graphql/language/InputObjectTypeExtensionDefinition.java @@ -4,6 +4,9 @@ import graphql.Internal; import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.List; @@ -14,14 +17,15 @@ import static graphql.collect.ImmutableKit.emptyList; @PublicApi +@NullMarked public class InputObjectTypeExtensionDefinition extends InputObjectTypeDefinition implements SDLExtensionDefinition { @Internal protected InputObjectTypeExtensionDefinition(String name, List directives, List inputValueDefinitions, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -31,8 +35,8 @@ protected InputObjectTypeExtensionDefinition(String name, @Override public InputObjectTypeExtensionDefinition deepCopy() { return new InputObjectTypeExtensionDefinition(getName(), - deepCopy(getDirectives()), - deepCopy(getInputValueDefinitions()), + assertNotNull(deepCopy(getDirectives()), "directives cannot be null"), + assertNotNull(deepCopy(getInputValueDefinitions()), "inputValueDefinitions cannot be null"), getDescription(), getSourceLocation(), getComments(), @@ -67,6 +71,7 @@ public InputObjectTypeExtensionDefinition transformExtension(Consumer b return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/InputValueDefinition.java b/src/main/java/graphql/language/InputValueDefinition.java index c0db3318fa..0c2ca489b6 100644 --- a/src/main/java/graphql/language/InputValueDefinition.java +++ b/src/main/java/graphql/language/InputValueDefinition.java @@ -7,6 +7,9 @@ import graphql.collect.ImmutableKit; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -21,10 +24,11 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class InputValueDefinition extends AbstractDescribedNode implements DirectivesContainer, NamedNode { private final String name; private final Type type; - private final Value defaultValue; + private final @Nullable Value defaultValue; private final NodeUtil.DirectivesHolder directives; public static final String CHILD_TYPE = "type"; @@ -34,10 +38,10 @@ public class InputValueDefinition extends AbstractDescribedNode directives, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -70,7 +74,7 @@ public InputValueDefinition(String name, public InputValueDefinition(String name, Type type, - Value defaultValue) { + @Nullable Value defaultValue) { this(name, type, defaultValue, emptyList(), null, null, emptyList(), IgnoredChars.EMPTY, emptyMap()); } @@ -84,7 +88,7 @@ public String getName() { return name; } - public Value getDefaultValue() { + public @Nullable Value getDefaultValue() { return defaultValue; } @@ -139,7 +143,7 @@ public InputValueDefinition withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -155,9 +159,9 @@ public boolean isEqualTo(Node o) { @Override public InputValueDefinition deepCopy() { return new InputValueDefinition(name, - deepCopy(type), + assertNotNull(deepCopy(type), "type cannot be null"), deepCopy(defaultValue), - deepCopy(directives.getDirectives()), + assertNotNull(deepCopy(directives.getDirectives()), "directives cannot be null"), description, getSourceLocation(), getComments(), @@ -190,6 +194,7 @@ public InputValueDefinition transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/InterfaceTypeDefinition.java b/src/main/java/graphql/language/InterfaceTypeDefinition.java index 3fd1343f89..64f956e1ce 100644 --- a/src/main/java/graphql/language/InterfaceTypeDefinition.java +++ b/src/main/java/graphql/language/InterfaceTypeDefinition.java @@ -7,6 +7,9 @@ import graphql.collect.ImmutableKit; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -21,6 +24,7 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class InterfaceTypeDefinition extends AbstractDescribedNode implements ImplementingTypeDefinition, DirectivesContainer, NamedNode { private final String name; @@ -37,8 +41,8 @@ protected InterfaceTypeDefinition(String name, List implementz, List definitions, List directives, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -121,7 +125,7 @@ public InterfaceTypeDefinition withNewChildren(NodeChildrenContainer newChildren } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -137,9 +141,9 @@ public boolean isEqualTo(Node o) { @Override public InterfaceTypeDefinition deepCopy() { return new InterfaceTypeDefinition(name, - deepCopy(implementz), - deepCopy(definitions), - deepCopy(directives.getDirectives()), + assertNotNull(deepCopy(implementz), "implementz cannot be null"), + assertNotNull(deepCopy(definitions), "definitions cannot be null"), + assertNotNull(deepCopy(directives.getDirectives()), "directives cannot be null"), description, getSourceLocation(), getComments(), @@ -173,6 +177,7 @@ public InterfaceTypeDefinition transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/InterfaceTypeExtensionDefinition.java b/src/main/java/graphql/language/InterfaceTypeExtensionDefinition.java index 2de917065b..6c5eb2bf4e 100644 --- a/src/main/java/graphql/language/InterfaceTypeExtensionDefinition.java +++ b/src/main/java/graphql/language/InterfaceTypeExtensionDefinition.java @@ -4,6 +4,9 @@ import graphql.Internal; import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.List; @@ -14,6 +17,7 @@ import static graphql.collect.ImmutableKit.emptyList; @PublicApi +@NullMarked public class InterfaceTypeExtensionDefinition extends InterfaceTypeDefinition implements SDLExtensionDefinition { @Internal @@ -21,8 +25,8 @@ protected InterfaceTypeExtensionDefinition(String name, List implementz, List definitions, List directives, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -33,8 +37,8 @@ protected InterfaceTypeExtensionDefinition(String name, public InterfaceTypeExtensionDefinition deepCopy() { return new InterfaceTypeExtensionDefinition(getName(), getImplements(), - deepCopy(getFieldDefinitions()), - deepCopy(getDirectives()), + assertNotNull(deepCopy(getFieldDefinitions()), "fieldDefinitions cannot be null"), + assertNotNull(deepCopy(getDirectives()), "directives cannot be null"), getDescription(), getSourceLocation(), getComments(), @@ -70,6 +74,7 @@ public InterfaceTypeExtensionDefinition transformExtension(Consumer bui return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/ListType.java b/src/main/java/graphql/language/ListType.java index f193179bb6..670ffe7d53 100644 --- a/src/main/java/graphql/language/ListType.java +++ b/src/main/java/graphql/language/ListType.java @@ -6,6 +6,9 @@ import graphql.PublicApi; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.List; @@ -18,6 +21,7 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class ListType extends AbstractNode implements Type { private final Type type; @@ -25,7 +29,7 @@ public class ListType extends AbstractNode implements Type { public static final String CHILD_TYPE = "type"; @Internal - protected ListType(Type type, SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { + protected ListType(Type type, @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { super(sourceLocation, comments, ignoredChars, additionalData); this.type = type; } @@ -63,7 +67,7 @@ public ListType withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -76,7 +80,7 @@ public boolean isEqualTo(Node o) { @Override public ListType deepCopy() { - return new ListType(deepCopy(type), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData()); + return new ListType(assertNotNull(deepCopy(type), "type cannot be null"), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData()); } @Override @@ -105,6 +109,7 @@ public ListType transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeBuilder { private Type type; private SourceLocation sourceLocation; diff --git a/src/main/java/graphql/language/NodeDirectivesBuilder.java b/src/main/java/graphql/language/NodeDirectivesBuilder.java index 3694165fab..12b793f3a9 100644 --- a/src/main/java/graphql/language/NodeDirectivesBuilder.java +++ b/src/main/java/graphql/language/NodeDirectivesBuilder.java @@ -1,10 +1,12 @@ package graphql.language; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import java.util.List; @PublicApi +@NullMarked public interface NodeDirectivesBuilder extends NodeBuilder { NodeDirectivesBuilder directives(List directives); diff --git a/src/main/java/graphql/language/NodeParentTree.java b/src/main/java/graphql/language/NodeParentTree.java index 91907e6ef0..7edc6e2dd3 100644 --- a/src/main/java/graphql/language/NodeParentTree.java +++ b/src/main/java/graphql/language/NodeParentTree.java @@ -4,11 +4,14 @@ import graphql.Internal; import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; +import java.util.Objects; import java.util.Optional; import static graphql.Assert.assertNotNull; @@ -21,10 +24,11 @@ * on an ObjectTypeDefinition. */ @PublicApi +@NullMarked public class NodeParentTree { private final T node; - private final NodeParentTree parent; + private final @Nullable NodeParentTree parent; private final ImmutableList path; @Internal @@ -45,7 +49,7 @@ public NodeParentTree(Deque nodeStack) { private ImmutableList mkPath(Deque copy) { return ImmutableKit.filterAndMap(copy, node1 -> node1 instanceof NamedNode, - node1 -> ((NamedNode) node1).getName()); + node1 -> Objects.toString(((NamedNode) node1).getName(), "")); } diff --git a/src/main/java/graphql/util/Anonymizer.java b/src/main/java/graphql/util/Anonymizer.java index 00073f5ad6..a4929f9dd4 100644 --- a/src/main/java/graphql/util/Anonymizer.java +++ b/src/main/java/graphql/util/Anonymizer.java @@ -870,9 +870,13 @@ public TraversalControl visitFragmentDefinition(FragmentDefinition node, Travers @Override public TraversalControl visitInlineFragment(InlineFragment node, TraverserContext context) { - GraphQLType currentCondition = assertNotNull(schema.getType(node.getTypeCondition().getName())); - String newCondition = newNames.get(currentCondition); - return changeNode(context, node.transform(builder -> builder.typeCondition(new TypeName(newCondition)))); + TypeName typeCondition = node.getTypeCondition(); + if (typeCondition != null) { + GraphQLType currentCondition = assertNotNull(schema.getType(typeCondition.getName())); + String newCondition = newNames.get(currentCondition); + return changeNode(context, node.transform(builder -> builder.typeCondition(new TypeName(newCondition)))); + } + return TraversalControl.CONTINUE; } @Override diff --git a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy index 3e2797916f..979904f737 100644 --- a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy @@ -92,16 +92,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.introspection.IntrospectionResultToSchema", "graphql.introspection.IntrospectionWithDirectivesSupport", "graphql.introspection.IntrospectionWithDirectivesSupport\$DirectivePredicateEnvironment", - "graphql.language.ImplementingTypeDefinition", - "graphql.language.InlineFragment", - "graphql.language.InputObjectTypeDefinition", - "graphql.language.InputObjectTypeExtensionDefinition", - "graphql.language.InputValueDefinition", - "graphql.language.InterfaceTypeDefinition", - "graphql.language.InterfaceTypeExtensionDefinition", - "graphql.language.ListType", - "graphql.language.NodeDirectivesBuilder", - "graphql.language.NodeParentTree", "graphql.language.ScalarTypeDefinition", "graphql.language.ScalarTypeExtensionDefinition", "graphql.language.SchemaDefinition", diff --git a/src/test/groovy/graphql/util/AnonymizerTest.groovy b/src/test/groovy/graphql/util/AnonymizerTest.groovy index ae6ed614a4..804ee88387 100644 --- a/src/test/groovy/graphql/util/AnonymizerTest.groovy +++ b/src/test/groovy/graphql/util/AnonymizerTest.groovy @@ -79,6 +79,27 @@ type Object2 { } + def "query with inline fragment without type condition"() { + given: + def schema = TestUtil.schema(""" + type Query { + foo: Foo + } + type Foo { + bar1: String + bar2: ID + } + """) + def query = "{foo {... {bar1 bar2}}}" + + when: + def result = Anonymizer.anonymizeSchemaAndQueries(schema, [query]) + def newQuery = result.queries[0] + + then: + newQuery == "{field1{...{field2 field3}}}" + } + def "query with arguments"() { given: def schema = TestUtil.schema("""