Skip to content

Commit ab72000

Browse files
authored
Merge pull request graphql-java#415 from bbakerman/arguments-are-checked-on-interfaces
Arguments are checked on interfaces
2 parents 663c3de + 5d88f55 commit ab72000

File tree

3 files changed

+94
-12
lines changed

3 files changed

+94
-12
lines changed

src/main/java/graphql/schema/validation/ObjectsImplementInterfaces.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package graphql.schema.validation;
22

3+
import graphql.schema.GraphQLArgument;
34
import graphql.schema.GraphQLFieldDefinition;
45
import graphql.schema.GraphQLInterfaceType;
56
import graphql.schema.GraphQLObjectType;
67
import graphql.schema.GraphQLOutputType;
78
import graphql.schema.GraphQLType;
89

910
import java.util.List;
11+
import java.util.Objects;
1012

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

73+
private void checkFieldArgumentEquivalence(GraphQLObjectType objectTyoe, GraphQLInterfaceType interfaceType, SchemaValidationErrorCollector validationErrorCollector, GraphQLFieldDefinition interfaceFieldDef, GraphQLFieldDefinition objectFieldDef) {
74+
List<GraphQLArgument> interfaceArgs = interfaceFieldDef.getArguments();
75+
List<GraphQLArgument> objectArgs = objectFieldDef.getArguments();
76+
if (interfaceArgs.size() != objectArgs.size()) {
77+
validationErrorCollector.addError(
78+
error(format("object type '%s' does not implement interface '%s' because field '%s' has a different number of arguments",
79+
objectTyoe.getName(), interfaceType.getName(), interfaceFieldDef.getName())));
80+
} else {
81+
for (int i = 0; i < interfaceArgs.size(); i++) {
82+
GraphQLArgument interfaceArg = interfaceArgs.get(i);
83+
GraphQLArgument objectArg = objectArgs.get(i);
84+
85+
String interfaceArgStr = makeArgStr(interfaceArg);
86+
String objectArgStr = makeArgStr(objectArg);
87+
88+
boolean same = true;
89+
if (!interfaceArgStr.equals(objectArgStr)) {
90+
same = false;
91+
}
92+
if (!Objects.equals(interfaceArg.getDefaultValue(), objectArg.getDefaultValue())) {
93+
same = false;
94+
}
95+
if (!same) {
96+
validationErrorCollector.addError(
97+
error(format("object type '%s' does not implement interface '%s' because field '%s' argument '%s' is defined differently",
98+
objectTyoe.getName(), interfaceType.getName(), interfaceFieldDef.getName(), interfaceArg.getName())));
99+
}
100+
101+
}
102+
}
103+
}
104+
105+
private String makeArgStr(GraphQLArgument argument) {
106+
// we don't do default value checking because toString of getDefaultValue is not guaranteed to be stable
107+
return argument.getName() +
108+
":" +
109+
getUnwrappedTypeName(argument.getType());
110+
111+
}
112+
69113
private SchemaValidationError error(String msg) {
70114
return new SchemaValidationError(ObjectDoesNotImplementItsInterfaces, msg);
71115
}

src/test/groovy/graphql/schema/validation/ObjectsImplementInterfacesTest.groovy

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import graphql.schema.GraphQLObjectType
66
import graphql.schema.TypeResolver
77
import spock.lang.Specification
88

9-
import static graphql.Scalars.GraphQLInt
10-
import static graphql.Scalars.GraphQLString
9+
import static SchemaValidationErrorType.ObjectDoesNotImplementItsInterfaces
10+
import static graphql.Scalars.*
11+
import static graphql.schema.GraphQLArgument.newArgument
1112
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition
1213
import static graphql.schema.GraphQLInterfaceType.newInterface
1314
import static graphql.schema.GraphQLList.list
1415
import static graphql.schema.GraphQLNonNull.nonNull
15-
import static SchemaValidationErrorType.ObjectDoesNotImplementItsInterfaces
1616

1717
class ObjectsImplementInterfacesTest extends Specification {
1818

@@ -30,6 +30,19 @@ class ObjectsImplementInterfacesTest extends Specification {
3030
.field(newFieldDefinition().name("friends").type(list(GraphQLString)))
3131
.field(newFieldDefinition().name("age").type(GraphQLInt))
3232
.field(newFieldDefinition().name("address").type(list(GraphQLString)))
33+
34+
.field(newFieldDefinition().name("argField1").type(GraphQLString)
35+
.argument(newArgument().name("arg1").type(GraphQLString))
36+
.argument(newArgument().name("arg2").type(GraphQLInt))
37+
.argument(newArgument().name("arg3").type(GraphQLBoolean))
38+
.argument(newArgument().name("arg4").type(GraphQLString).defaultValue("ABC"))
39+
)
40+
41+
.field(newFieldDefinition().name("argField2").type(GraphQLString)
42+
.argument(newArgument().name("arg1").type(GraphQLString))
43+
.argument(newArgument().name("arg2").type(GraphQLInt))
44+
.argument(newArgument().name("arg3").type(GraphQLBoolean))
45+
)
3346
.typeResolver(typeResolver)
3447
.build()
3548

@@ -44,6 +57,18 @@ class ObjectsImplementInterfacesTest extends Specification {
4457
.field(newFieldDefinition().name("missing").type(list(GraphQLString)))
4558
.field(newFieldDefinition().name("age").type(GraphQLString))
4659
.field(newFieldDefinition().name("address").type(list(nonNull(GraphQLString))))
60+
61+
.field(newFieldDefinition().name("argField1").type(GraphQLString)
62+
.argument(newArgument().name("arg1").type(GraphQLInt))
63+
.argument(newArgument().name("arg2").type(GraphQLInt))
64+
.argument(newArgument().name("arg3").type(GraphQLInt))
65+
.argument(newArgument().name("arg4").type(GraphQLString).defaultValue("XYZ"))
66+
)
67+
68+
.field(newFieldDefinition().name("argField2").type(GraphQLString)
69+
.argument(newArgument().name("arg1").type(GraphQLString))
70+
)
71+
4772
.build()
4873

4974
when:
@@ -53,12 +78,20 @@ class ObjectsImplementInterfacesTest extends Specification {
5378

5479
errorCollector.containsValidationError(ObjectDoesNotImplementItsInterfaces)
5580
def errors = errorCollector.getErrors()
56-
errors.size() == 3
81+
errors.size() == 7
5782
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
5883
"object type 'obj' does not implement interface 'Interface' because field 'friends' is missing"))
5984
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
6085
"object type 'obj' does not implement interface 'Interface' because field 'age' is defined as 'String' type and not as 'Int' type"))
6186
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
6287
"object type 'obj' does not implement interface 'Interface' because field 'address' is defined as '[String!]' type and not as '[String]' type"))
88+
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
89+
"object type 'obj' does not implement interface 'Interface' because field 'address' is defined as '[String!]' type and not as '[String]' type"))
90+
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
91+
"object type 'obj' does not implement interface 'Interface' because field 'argField1' argument 'arg1' is defined differently"))
92+
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
93+
"object type 'obj' does not implement interface 'Interface' because field 'argField1' argument 'arg1' is defined differently"))
94+
errors.contains(new SchemaValidationError(ObjectDoesNotImplementItsInterfaces,
95+
"object type 'obj' does not implement interface 'Interface' because field 'argField2' has a different number of arguments"))
6396
}
6497
}

src/test/groovy/graphql/validation/rules/Harness.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
package graphql.validation.rules;
22

3-
import graphql.TypeResolutionEnvironment;
4-
import graphql.schema.*;
5-
6-
import static graphql.Scalars.*;
3+
import graphql.schema.GraphQLEnumType;
4+
import graphql.schema.GraphQLInterfaceType;
5+
import graphql.schema.GraphQLList;
6+
import graphql.schema.GraphQLObjectType;
7+
import graphql.schema.GraphQLSchema;
8+
import graphql.schema.GraphQLTypeReference;
9+
import graphql.schema.GraphQLUnionType;
10+
import graphql.schema.TypeResolver;
11+
12+
import static graphql.Scalars.GraphQLBoolean;
13+
import static graphql.Scalars.GraphQLInt;
14+
import static graphql.Scalars.GraphQLString;
715
import static graphql.schema.GraphQLArgument.newArgument;
816
import static graphql.schema.GraphQLEnumType.newEnum;
917
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
@@ -128,10 +136,7 @@ public class Harness {
128136
.name("Human")
129137
.field(newFieldDefinition()
130138
.name("name")
131-
.type(GraphQLString)
132-
.argument(newArgument()
133-
.name("surname")
134-
.type(GraphQLBoolean)))
139+
.type(GraphQLString))
135140
.field(newFieldDefinition()
136141
.name("pets")
137142
.type(new GraphQLList(Pet)))

0 commit comments

Comments
 (0)