Skip to content

Commit c788bfa

Browse files
committed
Merge remote-tracking branch 'upstream/master' into 410-validate-interfaces-are-implemented-in-idl
2 parents 2296cba + 3ffe2fb commit c788bfa

20 files changed

+299
-161
lines changed

README.md

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -641,35 +641,8 @@ This implementation is based on the [js reference implementation](https://github
641641
For example the StarWarSchema and the tests (among a lot of other things) are simply adapted to the Java world.
642642

643643
### Related projects
644-
* [todomvc-relay-java](https://github.com/graphql-java/todomvc-relay-java): Port of the Relay TodoMVC example to a java backend
645644

646-
* [graphql-java-type-generator](https://github.com/graphql-java/graphql-java-type-generator): This library will autogenerate GraphQL types for usage in com.graphql-java:graphql-java Edit
647-
648-
* [graphql-rxjava](https://github.com/nfl/graphql-rxjava): An execution strategy that makes it easier to use rxjava's Observable
649-
650-
* [graphql-java-reactive](https://github.com/bsideup/graphql-java-reactive): An execution strategy which is based on Reactive Streams. Project is evolving.
651-
652-
* [graphql-java-annotations](https://github.com/graphql-java/graphql-java-annotations): Annotations-based syntax for GraphQL schema definition.
653-
654-
* [graphql-java-servlet](https://github.com/graphql-java/graphql-java-servlet): Servlet that automatically exposes a schema dynamically built from GraphQL queries and mutations.
655-
656-
* [graphql-apigen](https://github.com/Distelli/graphql-apigen): Generate Java APIs with GraphQL Schemas
657-
658-
* [graphql-spring-boot](https://github.com/oembedler/graphql-spring-boot): GraphQL and GraphiQL Spring Framework Boot Starters
659-
660-
* [spring-graphql-common](https://github.com/oembedler/spring-graphql-common): Spring Framework GraphQL Library
661-
662-
* [graphql-jpa](https://github.com/jcrygier/graphql-jpa): JPA Implementation of GraphQL (builds on graphql-java)
663-
664-
* [graphql-jpa-spring-boot-starter](https://github.com/timtebeek/graphql-jpa-spring-boot-starter): Spring Boot starter for GraphQL JPA; Expose JPA entities with GraphQL.
665-
666-
* [graphkool](https://github.com/beyondeye/graphkool): GraphQl-java utilities in kotlin
667-
668-
* [schemagen-graphql](https://github.com/bpatters/schemagen-graphql): GraphQL-Java add-on that adds support for Schema Generation & Execution for enterprise level applications.
669-
670-
* [GraphQL-SPQR](https://github.com/leangen/GraphQL-SPQR): Java 8+ API for rapid development of GraphQL services
671-
672-
* [Light Java GraphQL](https://github.com/networknt/light-java-graphql): A lightweight, fast microservices framework with all other cross-cutting concerns addressed that is ready to plug in GraphQL schema.
645+
Please have a look at the [awesome-graphql-java](https://github.com/graphql-java/awesome-graphql-java) list.
673646

674647
### License
675648

src/main/java/graphql/schema/GraphQLSchema.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
import graphql.Directives;
55
import graphql.schema.validation.InvalidSchemaException;
6-
import graphql.schema.validation.ValidationError;
7-
import graphql.schema.validation.Validator;
6+
import graphql.schema.validation.SchemaValidationError;
7+
import graphql.schema.validation.SchemaValidator;
88

99
import java.util.ArrayList;
1010
import java.util.Arrays;
@@ -125,7 +125,7 @@ public GraphQLSchema build(Set<GraphQLType> additionalTypes) {
125125
assertNotNull(additionalTypes, "additionalTypes can't be null");
126126
GraphQLSchema graphQLSchema = new GraphQLSchema(queryType, mutationType, subscriptionType, additionalTypes);
127127
new SchemaUtil().replaceTypeReferences(graphQLSchema);
128-
Collection<ValidationError> errors = new Validator().validateSchema(graphQLSchema);
128+
Collection<SchemaValidationError> errors = new SchemaValidator().validateSchema(graphQLSchema);
129129
if (errors.size() > 0) {
130130
throw new InvalidSchemaException(errors);
131131
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package graphql.schema;
2+
3+
public class GraphQLTypeUtil {
4+
5+
/**
6+
* This will get the unwrapped type name that includes the non null and list wrappers
7+
* so it might be '[typeName!]'
8+
*
9+
* @param type the type in play
10+
*
11+
* @return the unwrapped type name
12+
*/
13+
public static String getUnwrappedTypeName(GraphQLType type) {
14+
StringBuilder sb = new StringBuilder();
15+
if (type instanceof GraphQLNonNull) {
16+
sb.append(getUnwrappedTypeName(((GraphQLNonNull) type).getWrappedType()));
17+
sb.append("!");
18+
} else if (type instanceof GraphQLList) {
19+
sb.append("[");
20+
sb.append(getUnwrappedTypeName(((GraphQLList) type).getWrappedType()));
21+
sb.append("]");
22+
} else {
23+
sb.append(type.getName());
24+
}
25+
return sb.toString();
26+
}
27+
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66

77
public class InvalidSchemaException extends GraphQLException {
88

9-
public InvalidSchemaException(Collection<ValidationError> errors) {
9+
public InvalidSchemaException(Collection<SchemaValidationError> errors) {
1010
super(buildErrorMsg(errors));
1111
}
1212

13-
private static String buildErrorMsg(Collection<ValidationError> errors) {
13+
private static String buildErrorMsg(Collection<SchemaValidationError> errors) {
1414
StringBuilder message = new StringBuilder("invalid schema:");
15-
for (ValidationError error : errors) {
15+
for (SchemaValidationError error : errors) {
1616
message.append("\n").append(error.getDescription());
1717
}
1818
return message.toString();

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

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
package graphql.schema.validation;
22

3-
import java.util.ArrayList;
4-
import java.util.HashSet;
5-
import java.util.List;
6-
import java.util.Set;
7-
83
import graphql.schema.GraphQLArgument;
94
import graphql.schema.GraphQLFieldDefinition;
105
import graphql.schema.GraphQLInputObjectField;
@@ -15,13 +10,23 @@
1510
import graphql.schema.GraphQLNonNull;
1611
import graphql.schema.GraphQLType;
1712

13+
import java.util.ArrayList;
14+
import java.util.HashSet;
15+
import java.util.List;
16+
import java.util.Set;
17+
1818
/**
1919
* Schema validation rule ensuring no input type forms an unbroken non-nullable recursion,
2020
* as such a type would be impossible to satisfy
2121
*/
22-
public class NoUnbrokenInputCycles implements ValidationRule {
22+
public class NoUnbrokenInputCycles implements SchemaValidationRule {
2323

24-
public void check(GraphQLFieldDefinition fieldDef, ValidationErrorCollector validationErrorCollector) {
24+
@Override
25+
public void check(GraphQLType type, SchemaValidationErrorCollector validationErrorCollector) {
26+
}
27+
28+
@Override
29+
public void check(GraphQLFieldDefinition fieldDef, SchemaValidationErrorCollector validationErrorCollector) {
2530
for (GraphQLArgument argument : fieldDef.getArguments()) {
2631
GraphQLInputType argumentType = argument.getType();
2732
if (argumentType instanceof GraphQLInputObjectType) {
@@ -32,9 +37,9 @@ public void check(GraphQLFieldDefinition fieldDef, ValidationErrorCollector vali
3237
}
3338
}
3439

35-
private void check(GraphQLInputObjectType type, Set<GraphQLType> seen, List<String> path, ValidationErrorCollector validationErrorCollector) {
40+
private void check(GraphQLInputObjectType type, Set<GraphQLType> seen, List<String> path, SchemaValidationErrorCollector validationErrorCollector) {
3641
if (seen.contains(type)) {
37-
validationErrorCollector.addError(new ValidationError(ValidationErrorType.UnbrokenInputCycle, getErrorMessage(path)));
42+
validationErrorCollector.addError(new SchemaValidationError(SchemaValidationErrorType.UnbrokenInputCycle, getErrorMessage(path)));
3843
return;
3944
}
4045
seen.add(type);
@@ -70,11 +75,11 @@ private GraphQLType unwrap(GraphQLType type) {
7075
}
7176
return type;
7277
}
73-
78+
7479
private String getErrorMessage(List<String> path) {
7580
StringBuilder message = new StringBuilder();
7681
message.append("[");
77-
for (int i = 0; i < path.size(); i++) {
82+
for (int i = 0; i < path.size(); i++) {
7883
if (i != 0) {
7984
message.append(".");
8085
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package graphql.schema.validation;
2+
3+
import graphql.schema.GraphQLFieldDefinition;
4+
import graphql.schema.GraphQLInterfaceType;
5+
import graphql.schema.GraphQLObjectType;
6+
import graphql.schema.GraphQLOutputType;
7+
import graphql.schema.GraphQLType;
8+
9+
import java.util.List;
10+
11+
import static graphql.schema.GraphQLTypeUtil.getUnwrappedTypeName;
12+
import static graphql.schema.validation.SchemaValidationErrorType.ObjectDoesNotImplementItsInterfaces;
13+
import static java.lang.String.format;
14+
15+
/**
16+
* Schema validation rule ensuring object types have all the fields that they need to implement the interfaces
17+
* they say they implement
18+
*/
19+
public class ObjectsImplementInterfaces implements SchemaValidationRule {
20+
21+
@Override
22+
public void check(GraphQLFieldDefinition fieldDef, SchemaValidationErrorCollector validationErrorCollector) {
23+
}
24+
25+
@Override
26+
public void check(GraphQLType type, SchemaValidationErrorCollector validationErrorCollector) {
27+
if (type instanceof GraphQLObjectType) {
28+
check((GraphQLObjectType) type, validationErrorCollector);
29+
}
30+
}
31+
32+
// visible for testing
33+
private void check(GraphQLObjectType objectTyoe, SchemaValidationErrorCollector validationErrorCollector) {
34+
List<GraphQLOutputType> interfaces = objectTyoe.getInterfaces();
35+
interfaces.forEach(interfaceType -> {
36+
// we have resolved the interfaces at this point and hence the cast is ok
37+
checkObjectImplementsInterface(objectTyoe, (GraphQLInterfaceType) interfaceType, validationErrorCollector);
38+
});
39+
40+
}
41+
42+
private void checkObjectImplementsInterface(GraphQLObjectType objectTyoe, GraphQLInterfaceType interfaceType, SchemaValidationErrorCollector validationErrorCollector) {
43+
List<GraphQLFieldDefinition> fieldDefinitions = interfaceType.getFieldDefinitions();
44+
for (GraphQLFieldDefinition interfaceFieldDef : fieldDefinitions) {
45+
GraphQLFieldDefinition objectFieldDef = objectTyoe.getFieldDefinition(interfaceFieldDef.getName());
46+
if (objectFieldDef == null) {
47+
validationErrorCollector.addError(
48+
error(format("object type '%s' does not implement interface '%s' because field '%s' is missing",
49+
objectTyoe.getName(), interfaceType.getName(), interfaceFieldDef.getName())));
50+
} else {
51+
checkFieldTypeEquivalence(objectTyoe, interfaceType, validationErrorCollector, interfaceFieldDef, objectFieldDef);
52+
}
53+
}
54+
}
55+
56+
private void checkFieldTypeEquivalence(GraphQLObjectType objectTyoe, GraphQLInterfaceType interfaceType, SchemaValidationErrorCollector validationErrorCollector, GraphQLFieldDefinition interfaceFieldDef, GraphQLFieldDefinition objectFieldDef) {
57+
// the reference implementation has a full but complicated abstract type equivalence check
58+
// this is not that but we can add that later. It does cover the major cases however
59+
String interfaceFieldDefStr = getUnwrappedTypeName(interfaceFieldDef.getType());
60+
String objectFieldDefStr = getUnwrappedTypeName(objectFieldDef.getType());
61+
62+
if (!interfaceFieldDefStr.equals(objectFieldDefStr)) {
63+
validationErrorCollector.addError(
64+
error(format("object type '%s' does not implement interface '%s' because field '%s' is defined as '%s' type and not as '%s' type",
65+
objectTyoe.getName(), interfaceType.getName(), interfaceFieldDef.getName(), objectFieldDefStr, interfaceFieldDefStr)));
66+
}
67+
}
68+
69+
private SchemaValidationError error(String msg) {
70+
return new SchemaValidationError(ObjectDoesNotImplementItsInterfaces, msg);
71+
}
72+
73+
}

src/main/java/graphql/schema/validation/ValidationError.java renamed to src/main/java/graphql/schema/validation/SchemaValidationError.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22

33
import static graphql.Assert.assertNotNull;
44

5-
public class ValidationError {
5+
public class SchemaValidationError {
66

7-
private final ValidationErrorType errorType;
7+
private final SchemaValidationErrorType errorType;
88
private final String description;
99

10-
public ValidationError(ValidationErrorType errorType, String description) {
10+
public SchemaValidationError(SchemaValidationErrorType errorType, String description) {
1111
assertNotNull(errorType, "error type can not be null");
1212
assertNotNull(description, "error description can not be null");
1313
this.errorType = errorType;
1414
this.description = description;
1515
}
1616

17-
public ValidationErrorType getErrorType() {
17+
public SchemaValidationErrorType getErrorType() {
1818
return errorType;
1919
}
2020

@@ -32,10 +32,10 @@ public boolean equals(Object other) {
3232
if (this == other) {
3333
return true;
3434
}
35-
if (!(other instanceof ValidationError)) {
35+
if (!(other instanceof SchemaValidationError)) {
3636
return false;
3737
}
38-
ValidationError that = (ValidationError) other;
38+
SchemaValidationError that = (SchemaValidationError) other;
3939
return this.errorType.equals(that.errorType) && this.description.equals(that.description);
4040
}
4141
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package graphql.schema.validation;
2+
3+
import java.util.LinkedHashSet;
4+
import java.util.Set;
5+
6+
public class SchemaValidationErrorCollector {
7+
8+
private final LinkedHashSet<SchemaValidationError> errors = new LinkedHashSet<>();
9+
10+
public void addError(SchemaValidationError validationError) {
11+
this.errors.add(validationError);
12+
}
13+
14+
public Set<SchemaValidationError> getErrors() {
15+
return errors;
16+
}
17+
18+
public boolean containsValidationError(SchemaValidationErrorType validationErrorType) {
19+
for (SchemaValidationError validationError : errors) {
20+
if (validationError.getErrorType() == validationErrorType) return true;
21+
}
22+
return false;
23+
}
24+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package graphql.schema.validation;
2+
3+
public enum SchemaValidationErrorType {
4+
5+
UnbrokenInputCycle,
6+
ObjectDoesNotImplementItsInterfaces
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package graphql.schema.validation;
2+
3+
import graphql.schema.GraphQLFieldDefinition;
4+
import graphql.schema.GraphQLType;
5+
6+
public interface SchemaValidationRule {
7+
8+
void check(GraphQLFieldDefinition fieldDef, SchemaValidationErrorCollector validationErrorCollector);
9+
10+
void check(GraphQLType type, SchemaValidationErrorCollector validationErrorCollector);
11+
}

0 commit comments

Comments
 (0)