diff --git a/bin/jmh.sh b/bin/jmh.sh new file mode 100755 index 0000000000..26aa5e9460 --- /dev/null +++ b/bin/jmh.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +BRANCH=$(git rev-parse --abbrev-ref HEAD) +JAR="build/libs/graphql-java-0.0.0-$BRANCH-SNAPSHOT-jmh.jar" +echo "build and then running jmh for $JAR" + +./gradlew clean jmhJar + +java -jar "$JAR" "$@" \ No newline at end of file diff --git a/src/jmh/java/benchmark/CreateExtendedSchemaBenchmark.java b/src/jmh/java/benchmark/CreateExtendedSchemaBenchmark.java new file mode 100644 index 0000000000..92c624967d --- /dev/null +++ b/src/jmh/java/benchmark/CreateExtendedSchemaBenchmark.java @@ -0,0 +1,121 @@ +package benchmark; + +import graphql.schema.GraphQLSchema; +import graphql.schema.idl.RuntimeWiring; +import graphql.schema.idl.SchemaGenerator; +import graphql.schema.idl.SchemaParser; +import graphql.schema.idl.TypeDefinitionRegistry; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +import static benchmark.BenchmarkUtils.runInToolingForSomeTimeThenExit; + +/** + * This JMH + */ +@Warmup(iterations = 2, time = 5) +@Measurement(iterations = 3) +@Fork(3) +public class CreateExtendedSchemaBenchmark { + + private static final String SDL = mkSDL(); + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MINUTES) + public void benchmarkLargeSchemaCreate(Blackhole blackhole) { + blackhole.consume(createSchema(SDL)); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + public void benchmarkLargeSchemaCreateAvgTime(Blackhole blackhole) { + blackhole.consume(createSchema(SDL)); + } + + private static GraphQLSchema createSchema(String sdl) { + TypeDefinitionRegistry registry = new SchemaParser().parse(sdl); + return new SchemaGenerator().makeExecutableSchema(registry, RuntimeWiring.MOCKED_WIRING); + } + + /* something like + type Query { q : String } interface I { f : String } + interface I1 implements I { + f : String + f1 : String + } + type O1_1 implements I1 & I { + f : String + f1 : String + } + type O1_2 implements I1 & I { + f : String + f1 : String + } + */ + private static String mkSDL() { + int numTypes = 10000; + int numExtends = 10; + + StringBuilder sb = new StringBuilder(); + sb.append("type Query { q : String } interface I { f : String } interface X { x : String }\n"); + for (int i = 0; i < numTypes; i++) { + sb.append("interface I").append(i).append(" implements I { \n") + .append("\tf : String \n") + .append("\tf").append(i).append(" : String \n").append("}\n"); + + sb.append("type O").append(i).append(" implements I").append(i).append(" & I { \n") + .append("\tf : String \n") + .append("\tf").append(i).append(" : String \n") + .append("}\n"); + + sb.append("extend type O").append(i).append(" implements X").append(" { \n") + .append("\tx : String \n") + .append("}\n"); + + for (int j = 0; j < numExtends; j++) { + sb.append("extend type O").append(i).append(" { \n") + .append("\textendedF").append(j).append(" : String \n") + .append("}\n"); + + } + } + return sb.toString(); + } + + public static void main(String[] args) throws RunnerException { + try { + runAtStartup(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + Options opt = new OptionsBuilder() + .include("benchmark.CreateExtendedSchemaBenchmark") + .build(); + + new Runner(opt).run(); + } + + private static void runAtStartup() { + runInToolingForSomeTimeThenExit( + () -> { + }, + () -> createSchema(SDL), + () -> { + } + ); + } +} \ No newline at end of file diff --git a/src/main/java/graphql/schema/idl/ArgValueOfAllowedTypeChecker.java b/src/main/java/graphql/schema/idl/ArgValueOfAllowedTypeChecker.java index e00a40812f..1711b635c0 100644 --- a/src/main/java/graphql/schema/idl/ArgValueOfAllowedTypeChecker.java +++ b/src/main/java/graphql/schema/idl/ArgValueOfAllowedTypeChecker.java @@ -122,8 +122,10 @@ private void checkArgValueMatchesAllowedTypeName(List errors, Valu } String allowedTypeName = ((TypeName) allowedArgType).getName(); - TypeDefinition allowedTypeDefinition = typeRegistry.getType(allowedTypeName) - .orElseThrow(() -> new AssertException(format("Directive unknown argument type '%s'. This should have been validated before.", allowedTypeName))); + TypeDefinition allowedTypeDefinition = typeRegistry.getTypeOrNull(allowedTypeName); + if (allowedTypeDefinition == null) { + throw new AssertException(format("Directive unknown argument type '%s'. This should have been validated before.", allowedTypeName)); + } if (allowedTypeDefinition instanceof ScalarTypeDefinition) { checkArgValueMatchesAllowedScalar(errors, instanceValue, (ScalarTypeDefinition) allowedTypeDefinition); diff --git a/src/main/java/graphql/schema/idl/ImmutableTypeDefinitionRegistry.java b/src/main/java/graphql/schema/idl/ImmutableTypeDefinitionRegistry.java index 1604551f25..fee750ac05 100644 --- a/src/main/java/graphql/schema/idl/ImmutableTypeDefinitionRegistry.java +++ b/src/main/java/graphql/schema/idl/ImmutableTypeDefinitionRegistry.java @@ -6,13 +6,17 @@ import graphql.PublicApi; import graphql.language.DirectiveDefinition; import graphql.language.EnumTypeExtensionDefinition; +import graphql.language.ImplementingTypeDefinition; import graphql.language.InputObjectTypeExtensionDefinition; +import graphql.language.InterfaceTypeDefinition; import graphql.language.InterfaceTypeExtensionDefinition; +import graphql.language.ObjectTypeDefinition; import graphql.language.ObjectTypeExtensionDefinition; import graphql.language.SDLDefinition; import graphql.language.ScalarTypeDefinition; import graphql.language.ScalarTypeExtensionDefinition; import graphql.language.SchemaExtensionDefinition; +import graphql.language.Type; import graphql.language.TypeDefinition; import graphql.language.UnionTypeExtensionDefinition; import graphql.schema.idl.errors.SchemaProblem; @@ -34,6 +38,7 @@ @PublicApi @NullMarked public class ImmutableTypeDefinitionRegistry extends TypeDefinitionRegistry { + ImmutableTypeDefinitionRegistry(TypeDefinitionRegistry registry) { super( copyOf(registry.objectTypeExtensions), @@ -51,6 +56,7 @@ public class ImmutableTypeDefinitionRegistry extends TypeDefinitionRegistry { ); } + private UnsupportedOperationException unsupportedOperationException() { return new UnsupportedOperationException("The TypeDefinitionRegistry is in read only mode"); } diff --git a/src/main/java/graphql/schema/idl/ImplementingTypesChecker.java b/src/main/java/graphql/schema/idl/ImplementingTypesChecker.java index 6f2a22ec10..0294e290a1 100644 --- a/src/main/java/graphql/schema/idl/ImplementingTypesChecker.java +++ b/src/main/java/graphql/schema/idl/ImplementingTypesChecker.java @@ -29,7 +29,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; +import java.util.Objects; import java.util.Set; import java.util.function.BinaryOperator; import java.util.function.Function; @@ -73,7 +73,7 @@ void checkImplementingTypes(List errors, TypeDefinitionRegistry ty private void checkImplementingType( List errors, TypeDefinitionRegistry typeRegistry, - ImplementingTypeDefinition type) { + ImplementingTypeDefinition type) { Map implementedInterfaces = checkInterfacesNotImplementedMoreThanOnce(errors, type, typeRegistry); @@ -172,7 +172,7 @@ private void checkInterfaceIsImplemented( private void checkArgumentConsistency( String typeOfType, - ImplementingTypeDefinition objectTypeDef, + ImplementingTypeDefinition objectTypeDef, InterfaceTypeDefinition interfaceTypeDef, FieldDefinition objectFieldDef, FieldDefinition interfaceFieldDef, @@ -211,7 +211,7 @@ private void checkArgumentConsistency( } private Map> getLogicallyImplementedInterfaces( - ImplementingTypeDefinition type, + ImplementingTypeDefinition type, TypeDefinitionRegistry typeRegistry ) { @@ -255,18 +255,17 @@ private BinaryOperator mergeFirstValue() { return (v1, v2) -> v1; } - private Optional toInterfaceTypeDefinition(Type type, TypeDefinitionRegistry typeRegistry) { + private InterfaceTypeDefinition toInterfaceTypeDefinition(Type type, TypeDefinitionRegistry typeRegistry) { TypeInfo typeInfo = TypeInfo.typeInfo(type); TypeName unwrapped = typeInfo.getTypeName(); - return typeRegistry.getType(unwrapped, InterfaceTypeDefinition.class); + return typeRegistry.getTypeOrNull(unwrapped, InterfaceTypeDefinition.class); } private Set toInterfaceTypeDefinitions(TypeDefinitionRegistry typeRegistry, Collection implementsTypes) { return implementsTypes.stream() .map(t -> toInterfaceTypeDefinition(t, typeRegistry)) - .filter(Optional::isPresent) - .map(Optional::get) + .filter(Objects::nonNull) .collect(toSet()); } } diff --git a/src/main/java/graphql/schema/idl/SchemaExtensionsChecker.java b/src/main/java/graphql/schema/idl/SchemaExtensionsChecker.java index cb022bb666..9a3fb7d11e 100644 --- a/src/main/java/graphql/schema/idl/SchemaExtensionsChecker.java +++ b/src/main/java/graphql/schema/idl/SchemaExtensionsChecker.java @@ -76,10 +76,10 @@ static List checkSchemaInvariants(List er // ensure we have a "query" one Optional query = operationTypeDefinitions.stream().filter(op -> "query".equals(op.getName())).findFirst(); - if (!query.isPresent()) { + if (query.isEmpty()) { // its ok if they have a type named Query - Optional queryType = typeRegistry.getType("Query"); - if (!queryType.isPresent()) { + TypeDefinition queryType = typeRegistry.getTypeOrNull("Query"); + if (queryType == null) { errors.add(new QueryOperationMissingError()); } } @@ -117,13 +117,13 @@ private static Consumer checkOperationTypesExist(TypeDe private static Consumer checkOperationTypesAreObjects(TypeDefinitionRegistry typeRegistry, List errors) { return op -> { // make sure it is defined as a ObjectTypeDef - Type queryType = op.getTypeName(); - Optional type = typeRegistry.getType(queryType); - type.ifPresent(typeDef -> { - if (!(typeDef instanceof ObjectTypeDefinition)) { + Type queryType = op.getTypeName(); + TypeDefinition type = typeRegistry.getTypeOrNull(queryType); + if (type != null) { + if (!(type instanceof ObjectTypeDefinition)) { errors.add(new OperationTypesMustBeObjects(op)); } - }); + } }; } diff --git a/src/main/java/graphql/schema/idl/SchemaGeneratorHelper.java b/src/main/java/graphql/schema/idl/SchemaGeneratorHelper.java index 521bb185b0..768253fe5c 100644 --- a/src/main/java/graphql/schema/idl/SchemaGeneratorHelper.java +++ b/src/main/java/graphql/schema/idl/SchemaGeneratorHelper.java @@ -141,11 +141,12 @@ public TypeDefinitionRegistry getTypeRegistry() { } TypeDefinition getTypeDefinition(Type type) { - Optional optionalTypeDefinition = typeRegistry.getType(type); - - return optionalTypeDefinition.orElseThrow( - () -> new AssertException(format(" type definition for type '%s' not found", type)) - ); + TypeDefinition typeDefinition = typeRegistry.getTypeOrNull(type); + if (typeDefinition != null) { + return typeDefinition; + } else { + throw new AssertException(format(" type definition for type '%s' not found", type)); + } } boolean stackContains(TypeInfo typeInfo) { @@ -905,9 +906,8 @@ void buildOperations(BuildContext buildCtx, GraphQLSchema.Builder schemaBuilder) GraphQLObjectType subscription; Optional queryOperation = getOperationNamed("query", operationTypeDefs); - if (!queryOperation.isPresent()) { - @SuppressWarnings({"OptionalGetWithoutIsPresent"}) - TypeDefinition queryTypeDef = typeRegistry.getType("Query").get(); + if (queryOperation.isEmpty()) { + TypeDefinition queryTypeDef = Objects.requireNonNull(typeRegistry.getTypeOrNull("Query")); query = buildOutputType(buildCtx, TypeName.newTypeName().name(queryTypeDef.getName()).build()); } else { query = buildOperation(buildCtx, queryOperation.get()); @@ -915,12 +915,12 @@ void buildOperations(BuildContext buildCtx, GraphQLSchema.Builder schemaBuilder) schemaBuilder.query(query); Optional mutationOperation = getOperationNamed("mutation", operationTypeDefs); - if (!mutationOperation.isPresent()) { - if (!typeRegistry.schemaDefinition().isPresent()) { + if (mutationOperation.isEmpty()) { + if (typeRegistry.schemaDefinition().isEmpty()) { // If no schema definition, then there is no schema keyword. Default to using type called Mutation - Optional mutationTypeDef = typeRegistry.getType("Mutation"); - if (mutationTypeDef.isPresent()) { - mutation = buildOutputType(buildCtx, TypeName.newTypeName().name(mutationTypeDef.get().getName()).build()); + TypeDefinition mutationTypeDef = typeRegistry.getTypeOrNull("Mutation"); + if (mutationTypeDef != null) { + mutation = buildOutputType(buildCtx, TypeName.newTypeName().name(mutationTypeDef.getName()).build()); schemaBuilder.mutation(mutation); } } @@ -930,12 +930,12 @@ void buildOperations(BuildContext buildCtx, GraphQLSchema.Builder schemaBuilder) } Optional subscriptionOperation = getOperationNamed("subscription", operationTypeDefs); - if (!subscriptionOperation.isPresent()) { - if (!typeRegistry.schemaDefinition().isPresent()) { + if (subscriptionOperation.isEmpty()) { + if (typeRegistry.schemaDefinition().isEmpty()) { // If no schema definition, then there is no schema keyword. Default to using type called Subscription - Optional subscriptionTypeDef = typeRegistry.getType("Subscription"); - if (subscriptionTypeDef.isPresent()) { - subscription = buildOutputType(buildCtx, TypeName.newTypeName().name(subscriptionTypeDef.get().getName()).build()); + TypeDefinition subscriptionTypeDef = typeRegistry.getTypeOrNull("Subscription"); + if (subscriptionTypeDef != null) { + subscription = buildOutputType(buildCtx, TypeName.newTypeName().name(subscriptionTypeDef.getName()).build()); schemaBuilder.subscription(subscription); } } diff --git a/src/main/java/graphql/schema/idl/SchemaTypeChecker.java b/src/main/java/graphql/schema/idl/SchemaTypeChecker.java index 5d702b6444..b57a4f8798 100644 --- a/src/main/java/graphql/schema/idl/SchemaTypeChecker.java +++ b/src/main/java/graphql/schema/idl/SchemaTypeChecker.java @@ -34,7 +34,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -333,8 +332,9 @@ private void checkFieldTypesPresent(TypeDefinitionRegistry typeRegistry, List checkTypeExists(String typeOfType, TypeDefinitionRegistry typeRegistry, List errors, TypeDefinition typeDefinition) { return t -> { - TypeName unwrapped = TypeInfo.typeInfo(t).getTypeName(); - if (!typeRegistry.hasType(unwrapped)) { + String name = TypeInfo.typeName(t); + if (!typeRegistry.hasType(name)) { + TypeName unwrapped = TypeInfo.typeInfo(t).getTypeName(); errors.add(new MissingTypeError(typeOfType, typeDefinition, unwrapped)); } }; @@ -342,8 +342,9 @@ private Consumer checkTypeExists(String typeOfType, TypeDefinitionRegistry private Consumer checkTypeExists(TypeDefinitionRegistry typeRegistry, List errors, String typeOfType, Node element, String elementName) { return ivType -> { - TypeName unwrapped = TypeInfo.typeInfo(ivType).getTypeName(); - if (!typeRegistry.hasType(unwrapped)) { + String name = TypeInfo.typeName(ivType); + if (!typeRegistry.hasType(name)) { + TypeName unwrapped = TypeInfo.typeInfo(ivType).getTypeName(); errors.add(new MissingTypeError(typeOfType, element, elementName, unwrapped)); } }; @@ -353,10 +354,10 @@ private Consumer checkInterfaceTypeExists(TypeDefinitionRegistry t return t -> { TypeInfo typeInfo = TypeInfo.typeInfo(t); TypeName unwrapped = typeInfo.getTypeName(); - Optional type = typeRegistry.getType(unwrapped); - if (!type.isPresent()) { + TypeDefinition type = typeRegistry.getTypeOrNull(unwrapped); + if (type == null) { errors.add(new MissingInterfaceTypeError("interface", typeDefinition, unwrapped)); - } else if (!(type.get() instanceof InterfaceTypeDefinition)) { + } else if (!(type instanceof InterfaceTypeDefinition)) { errors.add(new MissingInterfaceTypeError("interface", typeDefinition, unwrapped)); } }; diff --git a/src/main/java/graphql/schema/idl/SchemaTypeDirectivesChecker.java b/src/main/java/graphql/schema/idl/SchemaTypeDirectivesChecker.java index 8ec32a0550..eb87b563a5 100644 --- a/src/main/java/graphql/schema/idl/SchemaTypeDirectivesChecker.java +++ b/src/main/java/graphql/schema/idl/SchemaTypeDirectivesChecker.java @@ -137,7 +137,7 @@ private void checkFieldsDirectives(List errors, TypeDefinitionRegi private void checkDirectives(DirectiveLocation expectedLocation, List errors, TypeDefinitionRegistry typeRegistry, Node element, String elementName, List directives) { directives.forEach(directive -> { Optional directiveDefinition = typeRegistry.getDirectiveDefinition(directive.getName()); - if (!directiveDefinition.isPresent()) { + if (directiveDefinition.isEmpty()) { errors.add(new DirectiveUndeclaredError(element, elementName, directive.getName())); } else { if (!inRightLocation(expectedLocation, directiveDefinition.get())) { @@ -157,7 +157,7 @@ private boolean inRightLocation(DirectiveLocation expectedLocation, DirectiveDef return names.contains(expectedLocation.name().toUpperCase()); } - private void checkDirectiveArguments(List errors, TypeDefinitionRegistry typeRegistry, Node element, String elementName, Directive directive, DirectiveDefinition directiveDefinition) { + private void checkDirectiveArguments(List errors, TypeDefinitionRegistry typeRegistry, Node element, String elementName, Directive directive, DirectiveDefinition directiveDefinition) { Map allowedArgs = getByName(directiveDefinition.getInputValueDefinitions(), (InputValueDefinition::getName), mergeFirst()); Map providedArgs = getByName(directive.getArguments(), (Argument::getName), mergeFirst()); directive.getArguments().forEach(argument -> { @@ -195,7 +195,7 @@ private void commonCheck(Collection directiveDefinitions, L }); } - private void assertTypeName(NamedNode node, List errors) { + private void assertTypeName(NamedNode node, List errors) { if (node.getName().length() >= 2 && node.getName().startsWith("__")) { errors.add((new IllegalNameError(node))); } @@ -204,7 +204,7 @@ private void assertTypeName(NamedNode node, List errors) { public void assertExistAndIsInputType(InputValueDefinition definition, List errors) { TypeName namedType = TypeUtil.unwrapAll(definition.getType()); - TypeDefinition unwrappedType = findTypeDefFromRegistry(namedType.getName(), typeRegistry); + TypeDefinition unwrappedType = findTypeDefFromRegistry(namedType.getName(), typeRegistry); if (unwrappedType == null) { errors.add(new MissingTypeError(namedType.getName(), definition, definition.getName())); @@ -218,7 +218,11 @@ public void assertExistAndIsInputType(InputValueDefinition definition, List typeRegistry.scalars().get(typeName)); + private TypeDefinition findTypeDefFromRegistry(String typeName, TypeDefinitionRegistry typeRegistry) { + TypeDefinition typeDefinition = typeRegistry.getTypeOrNull(typeName); + if (typeDefinition != null) { + return typeDefinition; + } + return typeRegistry.scalars().get(typeName); } } \ No newline at end of file diff --git a/src/main/java/graphql/schema/idl/SchemaTypeExtensionsChecker.java b/src/main/java/graphql/schema/idl/SchemaTypeExtensionsChecker.java index 36b4a2c3ff..fe233dcbf6 100644 --- a/src/main/java/graphql/schema/idl/SchemaTypeExtensionsChecker.java +++ b/src/main/java/graphql/schema/idl/SchemaTypeExtensionsChecker.java @@ -88,9 +88,10 @@ private void checkObjectTypeExtensions(List errors, TypeDefinition // // then check for field re-defs from the base type - Optional baseTypeOpt = typeRegistry.getType(extension.getName(), ObjectTypeDefinition.class); - baseTypeOpt.ifPresent(baseTypeDef -> checkForFieldRedefinition(errors, extension, fieldDefinitions, baseTypeDef.getFieldDefinitions())); - + ObjectTypeDefinition baseTypeDef = typeRegistry.getTypeOrNull(extension.getName(), ObjectTypeDefinition.class); + if (baseTypeDef != null) { + checkForFieldRedefinition(errors, extension, fieldDefinitions, baseTypeDef.getFieldDefinitions()); + } }); } ); @@ -132,8 +133,10 @@ private void checkInterfaceTypeExtensions(List errors, TypeDefinit // // then check for field re-defs from the base type - Optional baseTypeOpt = typeRegistry.getType(extension.getName(), InterfaceTypeDefinition.class); - baseTypeOpt.ifPresent(baseTypeDef -> checkForFieldRedefinition(errors, extension, fieldDefinitions, baseTypeDef.getFieldDefinitions())); + InterfaceTypeDefinition baseTypeDef = typeRegistry.getTypeOrNull(extension.getName(), InterfaceTypeDefinition.class); + if (baseTypeDef != null) { + checkForFieldRedefinition(errors, extension, fieldDefinitions, baseTypeDef.getFieldDefinitions()); + } }); }); } @@ -161,8 +164,8 @@ private void checkUnionTypeExtensions(List errors, TypeDefinitionR memberTypes.forEach( memberType -> { - Optional unionTypeDefinition = typeRegistry.getType(memberType, ObjectTypeDefinition.class); - if (!unionTypeDefinition.isPresent()) { + ObjectTypeDefinition unionTypeDefinition = typeRegistry.getTypeOrNull(memberType, ObjectTypeDefinition.class); + if (unionTypeDefinition == null) { errors.add(new MissingTypeError("union member", extension, memberType)); } } @@ -197,8 +200,10 @@ private void checkEnumTypeExtensions(List errors, TypeDefinitionRe // // then check for field re-defs from the base type - Optional baseTypeOpt = typeRegistry.getType(extension.getName(), EnumTypeDefinition.class); - baseTypeOpt.ifPresent(baseTypeDef -> checkForEnumValueRedefinition(errors, extension, enumValueDefinitions, baseTypeDef.getEnumValueDefinitions())); + EnumTypeDefinition baseTypeDef = typeRegistry.getTypeOrNull(extension.getName(), EnumTypeDefinition.class); + if (baseTypeDef != null) { + checkForEnumValueRedefinition(errors, extension, enumValueDefinitions, baseTypeDef.getEnumValueDefinitions()); + } }); @@ -251,8 +256,10 @@ private void checkInputObjectTypeExtensions(List errors, TypeDefin // // then check for field re-defs from the base type - Optional baseTypeOpt = typeRegistry.getType(extension.getName(), InputObjectTypeDefinition.class); - baseTypeOpt.ifPresent(baseTypeDef -> checkForInputValueRedefinition(errors, extension, inputValueDefinitions, baseTypeDef.getInputValueDefinitions())); + InputObjectTypeDefinition baseTypeDef = typeRegistry.getTypeOrNull(extension.getName(), InputObjectTypeDefinition.class); + if (baseTypeDef != null) { + checkForInputValueRedefinition(errors, extension, inputValueDefinitions, baseTypeDef.getInputValueDefinitions()); + } }); }); @@ -260,16 +267,16 @@ private void checkInputObjectTypeExtensions(List errors, TypeDefin private void checkTypeExtensionHasCorrespondingType(List errors, TypeDefinitionRegistry typeRegistry, String name, List extTypeList, Class targetClass) { - TypeDefinition extensionDefinition = extTypeList.get(0); - Optional typeDefinition = typeRegistry.getType(TypeName.newTypeName().name(name).build(), targetClass); - if (!typeDefinition.isPresent()) { + TypeDefinition extensionDefinition = extTypeList.get(0); + TypeDefinition typeDefinition = typeRegistry.getTypeOrNull(TypeName.newTypeName().name(name).build(), targetClass); + if (typeDefinition == null) { errors.add(new TypeExtensionMissingBaseTypeError(extensionDefinition)); } } @SuppressWarnings("unchecked") - private void checkForFieldRedefinition(List errors, TypeDefinition typeDefinition, List fieldDefinitions, List referenceFieldDefinitions) { + private void checkForFieldRedefinition(List errors, TypeDefinition typeDefinition, List fieldDefinitions, List referenceFieldDefinitions) { Map referenceMap = FpKit.getByName(referenceFieldDefinitions, FieldDefinition::getName, mergeFirst()); @@ -290,7 +297,7 @@ private void checkForInputValueRedefinition(List errors, InputObje }); } - private void checkForEnumValueRedefinition(List errors, TypeDefinition typeDefinition, List enumValueDefinitions, List referenceEnumValueDefinitions) { + private void checkForEnumValueRedefinition(List errors, TypeDefinition typeDefinition, List enumValueDefinitions, List referenceEnumValueDefinitions) { Map referenceMap = FpKit.getByName(referenceEnumValueDefinitions, EnumValueDefinition::getName, mergeFirst()); diff --git a/src/main/java/graphql/schema/idl/TypeDefinitionRegistry.java b/src/main/java/graphql/schema/idl/TypeDefinitionRegistry.java index dcbfb9c413..850e7b2155 100644 --- a/src/main/java/graphql/schema/idl/TypeDefinitionRegistry.java +++ b/src/main/java/graphql/schema/idl/TypeDefinitionRegistry.java @@ -3,6 +3,7 @@ import graphql.Assert; import graphql.GraphQLError; import graphql.PublicApi; +import graphql.collect.ImmutableKit; import graphql.language.DirectiveDefinition; import graphql.language.EnumTypeExtensionDefinition; import graphql.language.ImplementingTypeDefinition; @@ -36,13 +37,14 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.function.Function; -import java.util.stream.Collectors; import static graphql.Assert.assertNotNull; import static graphql.schema.idl.SchemaExtensionsChecker.defineOperationDefs; import static graphql.schema.idl.SchemaExtensionsChecker.gatherOperationDefs; +import static graphql.schema.idl.TypeInfo.typeName; import static java.util.Optional.ofNullable; /** @@ -83,17 +85,17 @@ public TypeDefinitionRegistry() { } protected TypeDefinitionRegistry(Map> objectTypeExtensions, - Map> interfaceTypeExtensions, - Map> unionTypeExtensions, - Map> enumTypeExtensions, - Map> scalarTypeExtensions, - Map> inputObjectTypeExtensions, - Map types, - Map scalarTypes, - Map directiveDefinitions, - List schemaExtensionDefinitions, - @Nullable SchemaDefinition schema, - SchemaParseOrder schemaParseOrder) { + Map> interfaceTypeExtensions, + Map> unionTypeExtensions, + Map> enumTypeExtensions, + Map> scalarTypeExtensions, + Map> inputObjectTypeExtensions, + Map types, + Map scalarTypes, + Map directiveDefinitions, + List schemaExtensionDefinitions, + @Nullable SchemaDefinition schema, + SchemaParseOrder schemaParseOrder) { this.objectTypeExtensions = objectTypeExtensions; this.interfaceTypeExtensions = interfaceTypeExtensions; this.unionTypeExtensions = unionTypeExtensions; @@ -344,6 +346,7 @@ public void remove(SDLDefinition definition) { } private void removeFromList(Map source, TypeDefinition value) { + //noinspection unchecked List list = (List) source.get(value.getName()); if (list == null) { return; @@ -490,43 +493,149 @@ public Map getDirectiveDefinitions() { return new LinkedHashMap<>(directiveDefinitions); } + /** + * Returns true if the registry has a type of the specified {@link TypeName} + * + * @param typeName the type name to check + * + * @return true if the registry has a type by that type name + */ public boolean hasType(TypeName typeName) { String name = typeName.getName(); + return hasType(name); + } + + /** + * Returns true if the registry has a type of the specified name + * + * @param name the name to check + * + * @return true if the registry has a type by that name + */ + public boolean hasType(String name) { return types.containsKey(name) || ScalarInfo.GRAPHQL_SPECIFICATION_SCALARS_DEFINITIONS.containsKey(name) || scalarTypes.containsKey(name) || objectTypeExtensions.containsKey(name); } + /** + * Returns an optional {@link TypeDefinition} of the specified type or {@link Optional#empty()} + * + * @param type the type to check + * + * @return an optional {@link TypeDefinition} or empty if it's not found + * + * @deprecated use the {@link #getTypeOrNull(Type)} variants instead since they avoid the allocation of an + * optional object + */ + @Deprecated(since = "2025-07-7") public Optional getType(Type type) { - String typeName = TypeInfo.typeInfo(type).getName(); - return getType(typeName); + return Optional.ofNullable(getTypeOrNull(type)); } + /** + * Returns an optional {@link TypeDefinition} of the specified type with the specified class or {@link Optional#empty()} + * + * @param type the type to check + * @param ofType the class of {@link TypeDefinition} + * + * @return an optional {@link TypeDefinition} or empty if it's not found + * + * @deprecated use the {@link #getTypeOrNull(Type)} variants instead since they avoid the allocation of an + * optional object + */ + @Deprecated(since = "2025-07-7") public Optional getType(Type type, Class ofType) { - String typeName = TypeInfo.typeInfo(type).getName(); - return getType(typeName, ofType); + return Optional.ofNullable(getTypeOrNull(typeName(type), ofType)); } + /** + * Returns an optional {@link TypeDefinition} of the specified type name or {@link Optional#empty()} + * + * @param typeName the type to check + * + * @return an optional {@link TypeDefinition} or empty if it's not found + * + * @deprecated use the {@link #getTypeOrNull(Type)} variants instead since they avoid the allocation of an + * optional object + */ + @Deprecated(since = "2025-07-7") public Optional getType(String typeName) { + return Optional.ofNullable(getTypeOrNull(typeName)); + } + + /** + * Returns an optional {@link TypeDefinition} of the specified type name with the specified class or {@link Optional#empty()} + * + * @param typeName the type to check + * @param ofType the class of {@link TypeDefinition} + * + * @deprecated use the {@link #getTypeOrNull(Type)} variants instead since they avoid the allocation of an + * optional object + */ + @Deprecated(since = "2025-07-7") + public Optional getType(String typeName, Class ofType) { + return Optional.ofNullable(getTypeOrNull(typeName, ofType)); + } + + /** + * Returns a {@link TypeDefinition} of the specified type or null + * + * @param type the type to check + * + * @return a {@link TypeDefinition} or null if it's not found + */ + @Nullable + public TypeDefinition getTypeOrNull(Type type) { + return getTypeOrNull(typeName(type)); + } + + /** + * Returns a {@link TypeDefinition} of the specified type with the specified class or null + * + * @param type the type to check + * @param ofType the class of {@link TypeDefinition} + * + * @return a {@link TypeDefinition} or null if it's not found + */ + @Nullable + public T getTypeOrNull(Type type, Class ofType) { + return getTypeOrNull(typeName(type), ofType); + } + + /** + * Returns a {@link TypeDefinition} of the specified name or null + * + * @param typeName the type name to check + * + * @return a {@link TypeDefinition} or null if it's not found + */ + @Nullable + public TypeDefinition getTypeOrNull(String typeName) { TypeDefinition typeDefinition = types.get(typeName); if (typeDefinition != null) { - return Optional.of(typeDefinition); + return typeDefinition; } typeDefinition = scalars().get(typeName); - if (typeDefinition != null) { - return Optional.of(typeDefinition); - } - return Optional.empty(); + return typeDefinition; } - public Optional getType(String typeName, Class ofType) { - Optional type = getType(typeName); - if (type.isPresent()) { - TypeDefinition typeDefinition = type.get(); - if (typeDefinition.getClass().equals(ofType)) { + /** + * Returns a {@link TypeDefinition} of the specified name and class or null + * + * @param typeName the type name to check + * @param ofType the class of {@link TypeDefinition} + * + * @return a {@link TypeDefinition} or null if it's not found + */ + @Nullable + public T getTypeOrNull(String typeName, Class ofType) { + TypeDefinition type = getTypeOrNull(typeName); + if (type != null) { + if (type.getClass().equals(ofType)) { //noinspection unchecked - return Optional.of((T) typeDefinition); + return (T) type; } } - return Optional.empty(); + return null; } /** @@ -537,10 +646,9 @@ public Optional getType(String typeName, Class * @return true if its abstract */ public boolean isInterfaceOrUnion(Type type) { - Optional typeDefinition = getType(type); - if (typeDefinition.isPresent()) { - TypeDefinition definition = typeDefinition.get(); - return definition instanceof UnionTypeDefinition || definition instanceof InterfaceTypeDefinition; + TypeDefinition typeDefinition = getTypeOrNull(type); + if (typeDefinition != null) { + return typeDefinition instanceof UnionTypeDefinition || typeDefinition instanceof InterfaceTypeDefinition; } return false; } @@ -553,10 +661,9 @@ public boolean isInterfaceOrUnion(Type type) { * @return true if its an object type or interface */ public boolean isObjectTypeOrInterface(Type type) { - Optional typeDefinition = getType(type); - if (typeDefinition.isPresent()) { - TypeDefinition definition = typeDefinition.get(); - return definition instanceof ObjectTypeDefinition || definition instanceof InterfaceTypeDefinition; + TypeDefinition typeDefinition = getTypeOrNull(type); + if (typeDefinition != null) { + return typeDefinition instanceof ObjectTypeDefinition || typeDefinition instanceof InterfaceTypeDefinition; } return false; } @@ -569,7 +676,7 @@ public boolean isObjectTypeOrInterface(Type type) { * @return true if its an object type */ public boolean isObjectType(Type type) { - return getType(type, ObjectTypeDefinition.class).isPresent(); + return getTypeOrNull(type, ObjectTypeDefinition.class) != null; } /** @@ -581,10 +688,10 @@ public boolean isObjectType(Type type) { * @return a list of types of the target class */ public List getTypes(Class targetClass) { - return types.values().stream() - .filter(targetClass::isInstance) - .map(targetClass::cast) - .collect(Collectors.toList()); + return ImmutableKit.filterAndMap(types.values(), + targetClass::isInstance, + targetClass::cast + ); } /** @@ -610,20 +717,20 @@ public Map getTypesMap(Class targetClas * @see TypeDefinitionRegistry#getImplementationsOf(InterfaceTypeDefinition) */ public List getAllImplementationsOf(InterfaceTypeDefinition targetInterface) { - List typeDefinitions = getTypes(ImplementingTypeDefinition.class); - return typeDefinitions.stream().filter(typeDefinition -> { - List implementsList = typeDefinition.getImplements(); - for (Type iFace : implementsList) { - Optional interfaceTypeDef = getType(iFace, InterfaceTypeDefinition.class); - if (interfaceTypeDef.isPresent()) { - boolean equals = interfaceTypeDef.get().getName().equals(targetInterface.getName()); - if (equals) { - return true; + return ImmutableKit.filter( + getTypes(ImplementingTypeDefinition.class), + implementingTypeDefinition -> { + List> implementsList = implementingTypeDefinition.getImplements(); + for (Type iFace : implementsList) { + InterfaceTypeDefinition interfaceTypeDef = getTypeOrNull(iFace, InterfaceTypeDefinition.class); + if (interfaceTypeDef != null) { + if (interfaceTypeDef.getName().equals(targetInterface.getName())) { + return true; + } + } } - } - } - return false; - }).collect(Collectors.toList()); + return false; + }); } /** @@ -636,11 +743,11 @@ public List getAllImplementationsOf(InterfaceTypeDef * @see TypeDefinitionRegistry#getAllImplementationsOf(InterfaceTypeDefinition) */ public List getImplementationsOf(InterfaceTypeDefinition targetInterface) { - return this.getAllImplementationsOf(targetInterface) - .stream() - .filter(typeDefinition -> typeDefinition instanceof ObjectTypeDefinition) - .map(typeDefinition -> (ObjectTypeDefinition) typeDefinition) - .collect(Collectors.toList()); + return ImmutableKit.filterAndMap( + getAllImplementationsOf(targetInterface), + typeDefinition -> typeDefinition instanceof ObjectTypeDefinition, + typeDefinition -> (ObjectTypeDefinition) typeDefinition + ); } /** @@ -658,14 +765,14 @@ public boolean isPossibleType(Type abstractType, Type possibleType) { if (!isObjectTypeOrInterface(possibleType)) { return false; } - TypeDefinition targetObjectTypeDef = getType(possibleType).get(); - TypeDefinition abstractTypeDef = getType(abstractType).get(); + TypeDefinition targetObjectTypeDef = Objects.requireNonNull(getTypeOrNull(possibleType)); + TypeDefinition abstractTypeDef = Objects.requireNonNull(getTypeOrNull(abstractType)); if (abstractTypeDef instanceof UnionTypeDefinition) { List memberTypes = ((UnionTypeDefinition) abstractTypeDef).getMemberTypes(); for (Type memberType : memberTypes) { - Optional checkType = getType(memberType, ObjectTypeDefinition.class); - if (checkType.isPresent()) { - if (checkType.get().getName().equals(targetObjectTypeDef.getName())) { + ObjectTypeDefinition checkType = getTypeOrNull(memberType, ObjectTypeDefinition.class); + if (checkType != null) { + if (checkType.getName().equals(targetObjectTypeDef.getName())) { return true; } } @@ -673,9 +780,21 @@ public boolean isPossibleType(Type abstractType, Type possibleType) { return false; } else { InterfaceTypeDefinition iFace = (InterfaceTypeDefinition) abstractTypeDef; - List implementingTypeDefinitions = getAllImplementationsOf(iFace); - return implementingTypeDefinitions.stream() - .anyMatch(od -> od.getName().equals(targetObjectTypeDef.getName())); + for (TypeDefinition t : types.values()) { + if (t instanceof ImplementingTypeDefinition) { + if (t.getName().equals(targetObjectTypeDef.getName())) { + ImplementingTypeDefinition itd = (ImplementingTypeDefinition) t; + + for (Type implementsType : itd.getImplements()) { + TypeDefinition matchingInterface = types.get(typeName(implementsType)); + if (matchingInterface != null && matchingInterface.getName().equals(iFace.getName())) { + return true; + } + } + } + } + } + return false; } } diff --git a/src/main/java/graphql/schema/idl/TypeInfo.java b/src/main/java/graphql/schema/idl/TypeInfo.java index fe12d49885..82a3499fe9 100644 --- a/src/main/java/graphql/schema/idl/TypeInfo.java +++ b/src/main/java/graphql/schema/idl/TypeInfo.java @@ -139,6 +139,36 @@ public Type unwrapOneType() { return unwrapOne().getRawType(); } + /** + * Gets the {@link TypeName} type name of a [Type], unwrapping any lists or non-null decorations + * + * @param type the Type + * + * @return the inner {@link TypeName} for this type + */ + public static TypeName getTypeName(Type type) { + while (!(type instanceof TypeName)) { + if (type instanceof NonNullType) { + type = ((NonNullType) type).getType(); + } + if (type instanceof ListType) { + type = ((ListType) type).getType(); + } + } + return (TypeName) type; + } + + /** + * Gets the string type name of a [Type], unwrapping any lists or non-null decorations + * + * @param type the Type + * + * @return the inner string name for this type + */ + public static String typeName(Type type) { + return getTypeName(type).getName(); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/graphql/schema/idl/UnionTypesChecker.java b/src/main/java/graphql/schema/idl/UnionTypesChecker.java index 73e11b3e0f..f2134b2d54 100644 --- a/src/main/java/graphql/schema/idl/UnionTypesChecker.java +++ b/src/main/java/graphql/schema/idl/UnionTypesChecker.java @@ -10,11 +10,8 @@ import graphql.language.UnionTypeExtensionDefinition; import graphql.schema.idl.errors.UnionTypeError; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.stream.Stream; @@ -33,13 +30,6 @@ */ @Internal class UnionTypesChecker { - private static final Map, String> TYPE_OF_MAP = new HashMap<>(); - - static { - TYPE_OF_MAP.put(UnionTypeDefinition.class, "union"); - TYPE_OF_MAP.put(UnionTypeExtensionDefinition.class, "union extension"); - } - void checkUnionType(List errors, TypeDefinitionRegistry typeRegistry) { List unionTypes = typeRegistry.getTypes(UnionTypeDefinition.class); @@ -52,18 +42,18 @@ void checkUnionType(List errors, TypeDefinitionRegistry typeRegist private void checkUnionType(TypeDefinitionRegistry typeRegistry, UnionTypeDefinition unionTypeDefinition, List errors) { assertTypeName(unionTypeDefinition, errors); + //noinspection rawtypes List memberTypes = unionTypeDefinition.getMemberTypes(); - if (memberTypes == null || memberTypes.size() == 0) { + if (memberTypes == null || memberTypes.isEmpty()) { errors.add(new UnionTypeError(unionTypeDefinition, format("Union type '%s' must include one or more member types.", unionTypeDefinition.getName()))); return; } Set typeNames = new LinkedHashSet<>(); - for (Type memberType : memberTypes) { + for (Type memberType : memberTypes) { String memberTypeName = ((TypeName) memberType).getName(); - Optional memberTypeDefinition = typeRegistry.getType(memberTypeName); - - if (!memberTypeDefinition.isPresent() || !(memberTypeDefinition.get() instanceof ObjectTypeDefinition)) { + TypeDefinition memberTypeDefinition = typeRegistry.getTypeOrNull(memberTypeName); + if (!(memberTypeDefinition instanceof ObjectTypeDefinition)) { errors.add(new UnionTypeError(unionTypeDefinition, format("The member types of a Union type must all be Object base types. member type '%s' in Union '%s' is invalid.", ((TypeName) memberType).getName(), unionTypeDefinition.getName()))); continue; } diff --git a/src/test/groovy/graphql/schema/idl/ImmutableTypeDefinitionRegistryTest.groovy b/src/test/groovy/graphql/schema/idl/ImmutableTypeDefinitionRegistryTest.groovy index c29636b3e1..9bf440c572 100644 --- a/src/test/groovy/graphql/schema/idl/ImmutableTypeDefinitionRegistryTest.groovy +++ b/src/test/groovy/graphql/schema/idl/ImmutableTypeDefinitionRegistryTest.groovy @@ -1,5 +1,6 @@ package graphql.schema.idl +import graphql.language.InterfaceTypeDefinition import graphql.language.TypeDefinition import spock.lang.Specification @@ -50,8 +51,8 @@ class ImmutableTypeDefinitionRegistryTest extends Specification { then: - TypeDefinition typeIn = registryIn.getType(typeName).get() - TypeDefinition typeOut = registryOut.getType(typeName).get() + TypeDefinition typeIn = registryIn.getTypeOrNull(typeName) + TypeDefinition typeOut = registryOut.getTypeOrNull(typeName) typeIn.isEqualTo(typeOut) where: @@ -73,8 +74,8 @@ class ImmutableTypeDefinitionRegistryTest extends Specification { containsSameObjects(mutableRegistry.types(), immutableRegistry.types()) - TypeDefinition typeIn = mutableRegistry.getType(typeName).get() - TypeDefinition typeOut = immutableRegistry.getType(typeName).get() + TypeDefinition typeIn = mutableRegistry.getTypeOrNull(typeName) + TypeDefinition typeOut = immutableRegistry.getTypeOrNull(typeName) typeIn.isEqualTo(typeOut) where: @@ -169,6 +170,64 @@ class ImmutableTypeDefinitionRegistryTest extends Specification { immutableRegistry.remove("key", someDef) then: thrown(UnsupportedOperationException) + } + + def "get implementations of"() { + def sdl = serializableSchema + """ + interface IType { + i : String + } + + interface DerivedIType implements IType { + i : String + d : String + } + + """ + for (int i = 0; i < 10; i++) { + sdl += """ + type OT$i implements IType { + i : String + } + """ + + } + for (int i = 0; i < 5; i++) { + sdl += """ + type DT$i implements DerivedIType { + i : String + d : String + } + """ + + } + def immutableRegistry = new SchemaParser().parse(sdl).readOnly() + + Map interfaces = immutableRegistry.getTypesMap(InterfaceTypeDefinition.class) + + when: + def iFieldType = interfaces.get("IType") + def allImplementationsOf = immutableRegistry.getAllImplementationsOf(iFieldType) + def implementationsOf = immutableRegistry.getImplementationsOf(iFieldType) + + then: + allImplementationsOf.size() == 11 + allImplementationsOf.collect({ it.getName() }).every { it.startsWith("OT") || it == "DerivedIType" } + + implementationsOf.size() == 10 + implementationsOf.collect({ it.getName() }).every { it.startsWith("OT") } + + when: + def iDerivedType = interfaces.get("DerivedIType") + allImplementationsOf = immutableRegistry.getAllImplementationsOf(iDerivedType) + implementationsOf = immutableRegistry.getImplementationsOf(iDerivedType) + + then: + allImplementationsOf.size() == 5 + allImplementationsOf.collect({ it.getName() }).every { it.startsWith("DT") } + + implementationsOf.size() == 5 + implementationsOf.collect({ it.getName() }).every { it.startsWith("DT") } } diff --git a/src/test/groovy/graphql/schema/idl/SchemaTypeCheckerTest.groovy b/src/test/groovy/graphql/schema/idl/SchemaTypeCheckerTest.groovy index 7bdd2fea01..50cbade723 100644 --- a/src/test/groovy/graphql/schema/idl/SchemaTypeCheckerTest.groovy +++ b/src/test/groovy/graphql/schema/idl/SchemaTypeCheckerTest.groovy @@ -32,7 +32,7 @@ import static java.lang.String.format class SchemaTypeCheckerTest extends Specification { static TypeDefinitionRegistry parseSDL(String spec) { - new SchemaParser().parse(spec) + new SchemaParser().parse(spec).readOnly() } def resolver = new TypeResolver() { diff --git a/src/test/groovy/graphql/schema/idl/TypeDefinitionRegistryTest.groovy b/src/test/groovy/graphql/schema/idl/TypeDefinitionRegistryTest.groovy index 91aef9d938..0ebd6dc882 100644 --- a/src/test/groovy/graphql/schema/idl/TypeDefinitionRegistryTest.groovy +++ b/src/test/groovy/graphql/schema/idl/TypeDefinitionRegistryTest.groovy @@ -375,7 +375,7 @@ class TypeDefinitionRegistryTest extends Specification { } - def "test can get implements of interface"() { + def "test can get implements of interface #typeOfReg"() { def spec = ''' interface Interface { name : String @@ -407,11 +407,19 @@ class TypeDefinitionRegistryTest extends Specification { ''' when: def registry = parse(spec) - def interfaceDef = registry.getType("Interface", InterfaceTypeDefinition.class).get() + if (typeOfReg == "immutable") { + registry = registry.readOnly() + } + def interfaceDef = registry.getTypeOrNull("Interface", InterfaceTypeDefinition.class) def implementingTypeDefinitions = registry.getAllImplementationsOf(interfaceDef) def names = implementingTypeDefinitions.collect { it.getName() } then: names == ["Type1", "Type2", "Type3", "Type5"] + + where: + typeOfReg | _ + "mutable" | _ + "immutable" | _ } def animalia = ''' @@ -461,9 +469,16 @@ class TypeDefinitionRegistryTest extends Specification { ''' - def "test possible type detection"() { + def "test possible type detection #typeOfReg"() { + given: + TypeDefinitionRegistry mutableReg = parse(animalia) + ImmutableTypeDefinitionRegistry immutableReg = mutableReg.readOnly() + when: - def registry = parse(animalia) + def registry = mutableReg + if (typeOfReg == "immutable") { + registry = immutableReg + } then: registry.isPossibleType(type("Mammal"), type("Canine")) @@ -485,12 +500,19 @@ class TypeDefinitionRegistryTest extends Specification { !registry.isPossibleType(type("Platypus"), type("Dog")) !registry.isPossibleType(type("Platypus"), type("Cat")) + where: + typeOfReg | _ + "mutable" | _ + "immutable" | _ } - def "isSubTypeOf detection"() { + def "isSubTypeOf detection #typeOfReg"() { when: def registry = parse(animalia) + if (typeOfReg == "immutable") { + registry = registry.readOnly() + } then: registry.isSubTypeOf(type("Mammal"), type("Mammal")) @@ -517,6 +539,11 @@ class TypeDefinitionRegistryTest extends Specification { registry.isSubTypeOf(listType(nonNullType(listType(type("Canine")))), listType(nonNullType(listType(type("Mammal"))))) !registry.isSubTypeOf(listType(nonNullType(listType(type("Turtle")))), listType(nonNullType(listType(type("Mammal"))))) + + where: + typeOfReg | _ + "mutable" | _ + "immutable" | _ } @Unroll @@ -527,7 +554,7 @@ class TypeDefinitionRegistryTest extends Specification { when: registry.remove(definition) then: - !registry.getType(definition.getName()).isPresent() + registry.getTypeOrNull(definition.getName()) == null where: definition | _ @@ -920,8 +947,8 @@ class TypeDefinitionRegistryTest extends Specification { when: registry.addAll(Arrays.asList(obj1, obj2)) then: - registry.getType("foo").isPresent() - registry.getType("bar").isPresent() + registry.getTypeOrNull("foo") != null + registry.getTypeOrNull("bar") != null } def "addAll will return an error on the first abd thing"() { @@ -974,8 +1001,8 @@ class TypeDefinitionRegistryTest extends Specification { TypeDefinitionRegistry registryIn = serialise(registryOut) then: - TypeDefinition typeIn = registryIn.getType(typeName).get() - TypeDefinition typeOut = registryOut.getType(typeName).get() + TypeDefinition typeIn = registryIn.getTypeOrNull(typeName) + TypeDefinition typeOut = registryOut.getTypeOrNull(typeName) typeIn.isEqualTo(typeOut) where: diff --git a/src/test/groovy/graphql/schema/idl/TypeInfoTest.groovy b/src/test/groovy/graphql/schema/idl/TypeInfoTest.groovy index 92ec7b1b8c..556755e436 100644 --- a/src/test/groovy/graphql/schema/idl/TypeInfoTest.groovy +++ b/src/test/groovy/graphql/schema/idl/TypeInfoTest.groovy @@ -136,4 +136,22 @@ class TypeInfoTest extends Specification { "[named!]!" | "[newName!]!" "[[named!]!]" | "[[newName!]!]" } + + @Unroll + def "test getTypeName gets to the inner type"() { + + expect: + Type actualType = TestUtil.parseType(actual) + def typeName = TypeInfo.getTypeName(actualType) + typeName.getName() == expected + + where: + actual | expected + "named" | "named" + "named!" | "named" + "[named]" | "named" + "[named!]" | "named" + "[named!]!" | "named" + "[[named!]!]" | "named" + } } diff --git a/src/test/groovy/graphql/schema/idl/WiringFactoryTest.groovy b/src/test/groovy/graphql/schema/idl/WiringFactoryTest.groovy index edb7efc12d..70c96fc7da 100644 --- a/src/test/groovy/graphql/schema/idl/WiringFactoryTest.groovy +++ b/src/test/groovy/graphql/schema/idl/WiringFactoryTest.groovy @@ -259,7 +259,7 @@ class WiringFactoryTest extends Specification { boolean providesDataFetcher(FieldWiringEnvironment environment) { assert ["id", "name", "homePlanet"].contains(environment.fieldDefinition.name) assert environment.parentType.name == "Human" - assert environment.registry.getType("Human").isPresent() + assert environment.registry.getTypeOrNull("Human") != null return true } @@ -267,7 +267,7 @@ class WiringFactoryTest extends Specification { DataFetcher getDataFetcher(FieldWiringEnvironment environment) { assert ["id", "name", "homePlanet"].contains(environment.fieldDefinition.name) assert environment.parentType.name == "Human" - assert environment.registry.getType("Human").isPresent() + assert environment.registry.getTypeOrNull("Human") != null new PropertyDataFetcher(environment.fieldDefinition.name) } }