Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package graphql.schema.validation;

import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLInterfaceType;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLOutputType;
import graphql.schema.GraphQLType;

import java.util.List;
import java.util.Objects;

import static graphql.schema.GraphQLTypeUtil.getUnwrappedTypeName;
import static graphql.schema.validation.SchemaValidationErrorType.ObjectDoesNotImplementItsInterfaces;
Expand Down Expand Up @@ -63,9 +65,51 @@ private void checkFieldTypeEquivalence(GraphQLObjectType objectTyoe, GraphQLInte
validationErrorCollector.addError(
error(format("object type '%s' does not implement interface '%s' because field '%s' is defined as '%s' type and not as '%s' type",
objectTyoe.getName(), interfaceType.getName(), interfaceFieldDef.getName(), objectFieldDefStr, interfaceFieldDefStr)));
} else {
checkFieldArgumentEquivalence(objectTyoe, interfaceType, validationErrorCollector, interfaceFieldDef, objectFieldDef);
}
}

private void checkFieldArgumentEquivalence(GraphQLObjectType objectTyoe, GraphQLInterfaceType interfaceType, SchemaValidationErrorCollector validationErrorCollector, GraphQLFieldDefinition interfaceFieldDef, GraphQLFieldDefinition objectFieldDef) {
List<GraphQLArgument> interfaceArgs = interfaceFieldDef.getArguments();
List<GraphQLArgument> objectArgs = objectFieldDef.getArguments();
if (interfaceArgs.size() != objectArgs.size()) {
validationErrorCollector.addError(
error(format("object type '%s' does not implement interface '%s' because field '%s' has a different number of arguments",
objectTyoe.getName(), interfaceType.getName(), interfaceFieldDef.getName())));
} else {
for (int i = 0; i < interfaceArgs.size(); i++) {
GraphQLArgument interfaceArg = interfaceArgs.get(i);
GraphQLArgument objectArg = objectArgs.get(i);

String interfaceArgStr = makeArgStr(interfaceArg);
String objectArgStr = makeArgStr(objectArg);

boolean same = true;
if (!interfaceArgStr.equals(objectArgStr)) {
same = false;
}
if (!Objects.equals(interfaceArg.getDefaultValue(), objectArg.getDefaultValue())) {
same = false;
}
if (!same) {
validationErrorCollector.addError(
error(format("object type '%s' does not implement interface '%s' because field '%s' argument '%s' is defined differently",
objectTyoe.getName(), interfaceType.getName(), interfaceFieldDef.getName(), interfaceArg.getName())));
}

}
}
}

private String makeArgStr(GraphQLArgument argument) {
// we don't do default value checking because toString of getDefaultValue is not guaranteed to be stable
return argument.getName() +
":" +
getUnwrappedTypeName(argument.getType());

}

private SchemaValidationError error(String msg) {
return new SchemaValidationError(ObjectDoesNotImplementItsInterfaces, msg);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import graphql.schema.GraphQLObjectType
import graphql.schema.TypeResolver
import spock.lang.Specification

import static graphql.Scalars.GraphQLInt
import static graphql.Scalars.GraphQLString
import static SchemaValidationErrorType.ObjectDoesNotImplementItsInterfaces
import static graphql.Scalars.*
import static graphql.schema.GraphQLArgument.newArgument
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition
import static graphql.schema.GraphQLInterfaceType.newInterface
import static graphql.schema.GraphQLList.list
import static graphql.schema.GraphQLNonNull.nonNull
import static SchemaValidationErrorType.ObjectDoesNotImplementItsInterfaces

class ObjectsImplementInterfacesTest extends Specification {

Expand All @@ -30,6 +30,19 @@ class ObjectsImplementInterfacesTest extends Specification {
.field(newFieldDefinition().name("friends").type(list(GraphQLString)))
.field(newFieldDefinition().name("age").type(GraphQLInt))
.field(newFieldDefinition().name("address").type(list(GraphQLString)))

.field(newFieldDefinition().name("argField1").type(GraphQLString)
.argument(newArgument().name("arg1").type(GraphQLString))
.argument(newArgument().name("arg2").type(GraphQLInt))
.argument(newArgument().name("arg3").type(GraphQLBoolean))
.argument(newArgument().name("arg4").type(GraphQLString).defaultValue("ABC"))
)

.field(newFieldDefinition().name("argField2").type(GraphQLString)
.argument(newArgument().name("arg1").type(GraphQLString))
.argument(newArgument().name("arg2").type(GraphQLInt))
.argument(newArgument().name("arg3").type(GraphQLBoolean))
)
.typeResolver(typeResolver)
.build()

Expand All @@ -44,6 +57,18 @@ class ObjectsImplementInterfacesTest extends Specification {
.field(newFieldDefinition().name("missing").type(list(GraphQLString)))
.field(newFieldDefinition().name("age").type(GraphQLString))
.field(newFieldDefinition().name("address").type(list(nonNull(GraphQLString))))

.field(newFieldDefinition().name("argField1").type(GraphQLString)
.argument(newArgument().name("arg1").type(GraphQLInt))
.argument(newArgument().name("arg2").type(GraphQLInt))
.argument(newArgument().name("arg3").type(GraphQLInt))
.argument(newArgument().name("arg4").type(GraphQLString).defaultValue("XYZ"))
)

.field(newFieldDefinition().name("argField2").type(GraphQLString)
.argument(newArgument().name("arg1").type(GraphQLString))
)

.build()

when:
Expand All @@ -53,12 +78,20 @@ class ObjectsImplementInterfacesTest extends Specification {

errorCollector.containsValidationError(ObjectDoesNotImplementItsInterfaces)
def errors = errorCollector.getErrors()
errors.size() == 3
errors.size() == 7
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
"object type 'obj' does not implement interface 'Interface' because field 'friends' is missing"))
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
"object type 'obj' does not implement interface 'Interface' because field 'age' is defined as 'String' type and not as 'Int' type"))
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
"object type 'obj' does not implement interface 'Interface' because field 'address' is defined as '[String!]' type and not as '[String]' type"))
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
"object type 'obj' does not implement interface 'Interface' because field 'address' is defined as '[String!]' type and not as '[String]' type"))
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
"object type 'obj' does not implement interface 'Interface' because field 'argField1' argument 'arg1' is defined differently"))
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
"object type 'obj' does not implement interface 'Interface' because field 'argField1' argument 'arg1' is defined differently"))
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
"object type 'obj' does not implement interface 'Interface' because field 'argField2' has a different number of arguments"))
}
}
21 changes: 13 additions & 8 deletions src/test/groovy/graphql/validation/rules/Harness.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package graphql.validation.rules;

import graphql.TypeResolutionEnvironment;
import graphql.schema.*;

import static graphql.Scalars.*;
import graphql.schema.GraphQLEnumType;
import graphql.schema.GraphQLInterfaceType;
import graphql.schema.GraphQLList;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLTypeReference;
import graphql.schema.GraphQLUnionType;
import graphql.schema.TypeResolver;

import static graphql.Scalars.GraphQLBoolean;
import static graphql.Scalars.GraphQLInt;
import static graphql.Scalars.GraphQLString;
import static graphql.schema.GraphQLArgument.newArgument;
import static graphql.schema.GraphQLEnumType.newEnum;
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
Expand Down Expand Up @@ -128,10 +136,7 @@ public class Harness {
.name("Human")
.field(newFieldDefinition()
.name("name")
.type(GraphQLString)
.argument(newArgument()
.name("surname")
.type(GraphQLBoolean)))
.type(GraphQLString))
.field(newFieldDefinition()
.name("pets")
.type(new GraphQLList(Pet)))
Expand Down