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
38 changes: 38 additions & 0 deletions src/main/java/graphql/schema/GraphQLSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,14 @@ public GraphQLObjectType getObjectType(String typeName) {
return (GraphQLObjectType) graphQLType;
}

/**
* Returns a {@link GraphQLFieldDefinition} as the specified co-ordinates or null
* if it does not exist
*
* @param fieldCoordinates the field co-ordinates
*
* @return the field or null if it does not exist
*/
public GraphQLFieldDefinition getFieldDefinition(FieldCoordinates fieldCoordinates) {
String fieldName = fieldCoordinates.getFieldName();
if (fieldCoordinates.isSystemCoordinates()) {
Expand All @@ -317,14 +325,35 @@ public GraphQLFieldDefinition getFieldDefinition(FieldCoordinates fieldCoordinat
return null;
}

/**
* @return all the named types in the scheme as a map from name to named type
*/
public Map<String, GraphQLNamedType> getTypeMap() {
return typeMap;
}

/**
* This returns all the {@link GraphQLNamedType} named types in th schema
*
* @return all the {@link GraphQLNamedType} types in the schema
*/
public List<GraphQLNamedType> getAllTypesAsList() {
return getAllTypesAsList(typeMap);
}

/**
* This returns all the top level {@link GraphQLNamedSchemaElement} named types and directives
* in the schema
*
* @return all the top level {@link GraphQLNamedSchemaElement} types and directives in the schema
*/
public List<GraphQLNamedSchemaElement> getAllElementsAsList() {
List<GraphQLNamedSchemaElement> list = new ArrayList<>();
list.addAll(getDirectives());
list.addAll(getAllTypesAsList());
return list;
}

/**
* This will return the list of {@link graphql.schema.GraphQLObjectType} types that implement the given
* interface type.
Expand Down Expand Up @@ -358,14 +387,23 @@ public boolean isPossibleType(GraphQLNamedType abstractType, GraphQLObjectType c
return assertShouldNeverHappen("Unsupported abstract type %s. Abstract types supported are Union and Interface.", abstractType.getName());
}

/**
* @return the Query type of the schema
*/
public GraphQLObjectType getQueryType() {
return queryType;
}

/**
* @return the Mutation type of the schema of null if there is not one
*/
public GraphQLObjectType getMutationType() {
return mutationType;
}

/**
* @return the Subscription type of the schema of null if there is not one
*/
public GraphQLObjectType getSubscriptionType() {
return subscriptionType;
}
Expand Down
32 changes: 28 additions & 4 deletions src/main/java/graphql/schema/GraphQLTypeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

import graphql.Assert;
import graphql.PublicApi;
import graphql.introspection.Introspection;
import graphql.schema.idl.DirectiveInfo;
import graphql.schema.idl.ScalarInfo;

import java.util.Stack;
import java.util.function.Predicate;

import static graphql.Assert.assertNotNull;
import static graphql.Assert.assertShouldNeverHappen;
Expand Down Expand Up @@ -175,7 +179,7 @@ public static GraphQLType unwrapOne(GraphQLType type) {
* and then cast to the target type.
*
* @param type the type to unwrapOne
* @param <T> for two
* @param <T> for two
*
* @return the unwrapped type or the same type again if its not wrapped
*/
Expand Down Expand Up @@ -205,7 +209,7 @@ public static GraphQLUnmodifiedType unwrapAll(GraphQLType type) {
* and then cast to the target type.
*
* @param type the type to unwrapOne
* @param <T> for two
* @param <T> for two
*
* @return the underlying type
*/
Expand Down Expand Up @@ -234,7 +238,7 @@ public static GraphQLType unwrapNonNull(GraphQLType type) {
* and then cast to the target type.
*
* @param type the type to unwrap
* @param <T> for two
* @param <T> for two
*
* @return the underlying type that is not {@link GraphQLNonNull}
*/
Expand All @@ -253,7 +257,7 @@ public static <T extends GraphQLType> T unwrapNonNullAs(GraphQLType type) {
* @return a stack of the type wrapping which will be at least 1 later deep
*/
public static Stack<GraphQLType> unwrapType(GraphQLType type) {
type = assertNotNull(type);
assertNotNull(type);
Stack<GraphQLType> decoration = new Stack<>();
while (true) {
decoration.push(type);
Expand All @@ -274,4 +278,24 @@ public static boolean isObjectType(GraphQLType type) {
}


/**
* This predicate returns true if the schema element is an inbuilt schema element
* such as the system scalars and directives or introspection types
*
* @return true if its a system schema element
*/
public static Predicate<GraphQLNamedSchemaElement> isSystemElement() {
return schemaElement -> {
if (schemaElement instanceof GraphQLScalarType) {
return ScalarInfo.isGraphqlSpecifiedScalar((GraphQLScalarType) schemaElement);
}
if (schemaElement instanceof GraphQLDirective) {
return DirectiveInfo.isGraphqlSpecifiedDirective((GraphQLDirective) schemaElement);
}
if (schemaElement instanceof GraphQLNamedType) {
return Introspection.isIntrospectionTypes((GraphQLNamedType) schemaElement);
}
return false;
};
}
}
158 changes: 158 additions & 0 deletions src/main/java/graphql/schema/idl/SchemaParseOrder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package graphql.schema.idl;

import com.google.common.collect.ImmutableMap;
import graphql.language.SDLDefinition;
import graphql.language.SDLNamedDefinition;
import graphql.language.SourceLocation;
import graphql.schema.GraphQLDirective;
import graphql.schema.GraphQLEnumValueDefinition;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLInputObjectField;
import graphql.schema.GraphQLNamedSchemaElement;
import graphql.schema.GraphQLNamedType;
import graphql.schema.GraphQLSchemaElement;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.Collectors;

import static java.util.Optional.ofNullable;

/**
* This class will track what order {@link SDLDefinition} were parsed in
* via {@link SchemaParser} and {@link TypeDefinitionRegistry}
*/
public class SchemaParseOrder implements Serializable {

private final Map<String, List<SDLDefinition<?>>> definitionOrder = new LinkedHashMap<>();


/**
* This map is the order in which {@link SDLDefinition}s were parsed per unique {@link SourceLocation#getSourceName()}. If there
* is no source then the empty string "" is used.
*
* @return a map of source names to definitions in parsed order
*/
public Map<String, List<SDLDefinition<?>>> getInOrder() {
return ImmutableMap.copyOf(definitionOrder);
}

/**
* This map is the order in which {@link SDLDefinition}s were parsed per unique {@link SourceLocation#getSourceName()} and it
* only contains {@link SDLNamedDefinition}s. If there is no source then the empty string "" is used.
*
* @return a map of source names to definitions in parsed order
*/
public Map<String, List<SDLNamedDefinition<?>>> getInNameOrder() {
Map<String, List<SDLNamedDefinition<?>>> named = new LinkedHashMap<>();
definitionOrder.forEach((location, def) -> {
List<SDLNamedDefinition<?>> namedDefs = def.stream()
.filter(d -> d instanceof SDLNamedDefinition)
.map(d -> (SDLNamedDefinition<?>) d)
.collect(Collectors.toList());
named.put(location, namedDefs);
});
return named;
}

/**
* This comparator will sort according to the original parsed order
*
* @param <T> is a {@link GraphQLSchemaElement}
*
* @return a comparator that sorts schema elements in parsed order
*/
public <T extends GraphQLSchemaElement> Comparator<? super T> getElementComparator() {
// relies in the fact that names in graphql schema are unique across ALL elements
Map<String, Integer> namedSortValues = buildNameIndex(getInNameOrder());
return (e1, e2) -> {
if (isAssignable(e1, GraphQLFieldDefinition.class, GraphQLInputObjectField.class, GraphQLEnumValueDefinition.class) ||
isAssignable(e2, GraphQLFieldDefinition.class, GraphQLInputObjectField.class, GraphQLEnumValueDefinition.class)
) {
return 0; // as is - they are not parsed tracked
}
if (isAssignable(e1, GraphQLDirective.class, GraphQLNamedType.class) && isAssignable(e2, GraphQLDirective.class, GraphQLNamedType.class)) {
int sortVal1 = sortValue((GraphQLNamedSchemaElement) e1, namedSortValues);
int sortVal2 = sortValue((GraphQLNamedSchemaElement) e2, namedSortValues);
return Integer.compare(sortVal1, sortVal2);
}
return -1; // sort to the top
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that neither elements are GraphQLFieldDefinition, GraphQLInputObjectField, GraphQLEnumValueDefinition or GraphQLNamedSchemaElement?
Because in that case the outcome of the comparator would be conflicting.
i.e. both compare(x, y) and compare(y, x) would be -1.

};
}

private <T extends GraphQLSchemaElement> boolean isAssignable(T e1, Class<?>... classes) {
return Arrays.stream(classes).anyMatch(aClass -> aClass.isAssignableFrom(e1.getClass()));
}

private Integer sortValue(GraphQLNamedSchemaElement e1, Map<String, Integer> namedSortValues) {
return namedSortValues.getOrDefault(e1.getName(), -1);
}

private Map<String, Integer> buildNameIndex(Map<String, List<SDLNamedDefinition<?>>> inNameOrder) {
Map<String, Integer> nameIndex = new HashMap<>();
int sourceIndex = 0;
for (Map.Entry<String, List<SDLNamedDefinition<?>>> entry : inNameOrder.entrySet()) {
List<SDLNamedDefinition<?>> namedDefs = entry.getValue();
for (int i = 0; i < namedDefs.size(); i++) {
SDLNamedDefinition<?> namedDefinition = namedDefs.get(i);
// we will put it in parsed order AND with first sources first
int index = i + (sourceIndex * 100_000_000);
nameIndex.put(namedDefinition.getName(), index);
}
sourceIndex++;
}
return nameIndex;
}

/**
* This adds a new {@link SDLDefinition} to the order
*
* @param sdlDefinition the SDL definition to add
* @param <T> for two
*
* @return this {@link SchemaParseOrder} for fluent building
*/
public <T extends SDLDefinition<?>> SchemaParseOrder addDefinition(T sdlDefinition) {
if (sdlDefinition != null) {
definitionList(sdlDefinition).add(sdlDefinition);
}
return this;
}

/**
* This removes a {@link SDLDefinition} from the order
*
* @param sdlDefinition the SDL definition to remove
* @param <T> for two
*
* @return this {@link SchemaParseOrder} for fluent building
*/
public <T extends SDLDefinition<?>> SchemaParseOrder removeDefinition(T sdlDefinition) {
definitionList(sdlDefinition).remove(sdlDefinition);
return this;
}

private <T extends SDLDefinition<?>> List<SDLDefinition<?>> definitionList(T sdlDefinition) {
String location = ofNullable(sdlDefinition.getSourceLocation())
.map(SourceLocation::getSourceName).orElse("");
return computeIfAbsent(location);
}

private List<SDLDefinition<?>> computeIfAbsent(String location) {
return definitionOrder.computeIfAbsent(location, k -> new ArrayList<>());
}

@Override
public String toString() {
return new StringJoiner(", ", SchemaParseOrder.class.getSimpleName() + "[", "]")
.add("definitionOrder=" + definitionOrder)
.toString();
}
}
4 changes: 2 additions & 2 deletions src/main/java/graphql/schema/idl/SchemaParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
import graphql.schema.idl.errors.NonSDLDefinitionError;
import graphql.schema.idl.errors.SchemaProblem;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.file.Files;
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/graphql/schema/idl/SchemaPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,22 @@ public String print(GraphQLType type) {
return sw.toString();
}

public String print(List<GraphQLSchemaElement> elements) {
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);

for (GraphQLSchemaElement element : elements) {
if (element instanceof GraphQLDirective) {
out.print(print(((GraphQLDirective) element)));
} else if (element instanceof GraphQLType) {
printType(out, (GraphQLType) element, DEFAULT_FIELD_VISIBILITY);
} else {
Assert.assertShouldNeverHappen("How did we miss a %s", element.getClass());
}
}
return sw.toString();
}

public String print(GraphQLDirective graphQLDirective) {
return directiveDefinition(graphQLDirective);
}
Expand Down
Loading