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
18 changes: 16 additions & 2 deletions src/main/java/graphql/schema/GraphQLSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,18 @@ public static Builder newSchema(GraphQLSchema existingSchema) {
.query(existingSchema.getQueryType())
.mutation(existingSchema.getMutationType())
.subscription(existingSchema.getSubscriptionType())
.fieldVisibility(existingSchema.getFieldVisibility());
.fieldVisibility(existingSchema.getFieldVisibility())
.additionalDirectives(existingSchema.directives)
.additionalTypes(existingSchema.additionalTypes);
}

public static class Builder {
private GraphQLObjectType queryType;
private GraphQLObjectType mutationType;
private GraphQLObjectType subscriptionType;
private GraphqlFieldVisibility fieldVisibility = DEFAULT_FIELD_VISIBILITY;
private Set<GraphQLType> additionalTypes = Collections.emptySet();
private Set<GraphQLDirective> additionalDirectives = Collections.emptySet();

public Builder query(GraphQLObjectType.Builder builder) {
return query(builder.build());
Expand Down Expand Up @@ -166,8 +170,18 @@ public Builder fieldVisibility(GraphqlFieldVisibility fieldVisibility) {
return this;
}

public Builder additionalTypes(Set<GraphQLType> additionalTypes) {
this.additionalTypes = additionalTypes;
return this;
}

public Builder additionalDirectives(Set<GraphQLDirective> additionalDirectives) {
this.additionalDirectives = additionalDirectives;
return this;
}

public GraphQLSchema build() {
return build(Collections.emptySet(), Collections.emptySet());
return build(additionalTypes, additionalDirectives);
}

public GraphQLSchema build(Set<GraphQLType> additionalTypes) {
Expand Down
30 changes: 26 additions & 4 deletions src/main/java/graphql/schema/idl/RuntimeWiring.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package graphql.schema.idl;

import graphql.Assert;
import graphql.PublicApi;
import graphql.schema.DataFetcher;
import graphql.schema.GraphQLScalarType;
import graphql.schema.GraphQLSchema;
import graphql.schema.TypeResolver;
import graphql.schema.visibility.GraphqlFieldVisibility;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.UnaryOperator;

import static graphql.Assert.assertNotNull;
import static graphql.schema.visibility.DefaultGraphqlFieldVisibility.DEFAULT_FIELD_VISIBILITY;

/**
* A runtime wiring is a specification of data fetchers, type resolves and custom scalars that are needed
* to wire together a functional {@link GraphQLSchema}
Expand All @@ -24,14 +27,16 @@ public class RuntimeWiring {
private final Map<String, TypeResolver> typeResolvers;
private final WiringFactory wiringFactory;
private final Map<String, EnumValuesProvider> enumValuesProviders;
private final GraphqlFieldVisibility fieldVisibility;

private RuntimeWiring(Map<String, Map<String, DataFetcher>> dataFetchers, Map<String, DataFetcher> defaultDataFetchers, Map<String, GraphQLScalarType> scalars, Map<String, TypeResolver> typeResolvers, Map<String, EnumValuesProvider> enumValuesProviders, WiringFactory wiringFactory) {
private RuntimeWiring(Map<String, Map<String, DataFetcher>> dataFetchers, Map<String, DataFetcher> defaultDataFetchers, Map<String, GraphQLScalarType> scalars, Map<String, TypeResolver> typeResolvers, Map<String, EnumValuesProvider> enumValuesProviders, WiringFactory wiringFactory, GraphqlFieldVisibility fieldVisibility) {
this.dataFetchers = dataFetchers;
this.defaultDataFetchers = defaultDataFetchers;
this.scalars = scalars;
this.typeResolvers = typeResolvers;
this.wiringFactory = wiringFactory;
this.enumValuesProviders = enumValuesProviders;
this.fieldVisibility = fieldVisibility;
}

/**
Expand Down Expand Up @@ -69,6 +74,10 @@ public WiringFactory getWiringFactory() {
return wiringFactory;
}

public GraphqlFieldVisibility getFieldVisibility() {
return fieldVisibility;
}

@PublicApi
public static class Builder {
private final Map<String, Map<String, DataFetcher>> dataFetchers = new LinkedHashMap<>();
Expand All @@ -77,6 +86,7 @@ public static class Builder {
private final Map<String, TypeResolver> typeResolvers = new LinkedHashMap<>();
private final Map<String, EnumValuesProvider> enumValuesProviders = new LinkedHashMap<>();
private WiringFactory wiringFactory = new NoopWiringFactory();
private GraphqlFieldVisibility fieldVisibility = DEFAULT_FIELD_VISIBILITY;

private Builder() {
ScalarInfo.STANDARD_SCALARS.forEach(this::scalar);
Expand All @@ -90,7 +100,7 @@ private Builder() {
* @return this outer builder
*/
public Builder wiringFactory(WiringFactory wiringFactory) {
Assert.assertNotNull(wiringFactory, "You must provide a wiring factory");
assertNotNull(wiringFactory, "You must provide a wiring factory");
this.wiringFactory = wiringFactory;
return this;
}
Expand All @@ -107,6 +117,18 @@ public Builder scalar(GraphQLScalarType scalarType) {
return this;
}

/**
* This allows you to add a field visibility that will be associated with the schema
*
* @param fieldVisibility the new field visibility
*
* @return the runtime wiring builder
*/
public Builder fieldVisibility(GraphqlFieldVisibility fieldVisibility) {
this.fieldVisibility = assertNotNull(fieldVisibility);
return this;
}

/**
* This allows you to add a new type wiring via a builder
*
Expand Down Expand Up @@ -161,7 +183,7 @@ public Builder type(TypeRuntimeWiring typeRuntimeWiring) {
* @return the built runtime wiring
*/
public RuntimeWiring build() {
return new RuntimeWiring(dataFetchers, defaultDataFetchers, scalars, typeResolvers, enumValuesProviders, wiringFactory);
return new RuntimeWiring(dataFetchers, defaultDataFetchers, scalars, typeResolvers, enumValuesProviders, wiringFactory, fieldVisibility);
}

}
Expand Down
15 changes: 6 additions & 9 deletions src/main/java/graphql/schema/idl/SchemaGenerator.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
package graphql.schema.idl;

import graphql.Assert;
import graphql.GraphQL;
import graphql.GraphQLError;
import graphql.language.Argument;
import graphql.language.ArrayValue;
import graphql.language.BooleanValue;
import graphql.language.Comment;
import graphql.language.Directive;
import graphql.language.EnumTypeDefinition;
import graphql.language.EnumValue;
import graphql.language.FieldDefinition;
import graphql.language.FloatValue;
import graphql.language.InputObjectTypeDefinition;
import graphql.language.InputValueDefinition;
import graphql.language.IntValue;
import graphql.language.InterfaceTypeDefinition;
import graphql.language.Node;
import graphql.language.NullValue;
Expand Down Expand Up @@ -219,6 +215,7 @@ private GraphQLSchema makeExecutableSchemaImpl(BuildContext buildCtx) {

Set<GraphQLType> additionalTypes = buildAdditionalTypes(buildCtx);

schemaBuilder.fieldVisibility(buildCtx.getWiring().getFieldVisibility());
return schemaBuilder.build(additionalTypes);
}

Expand Down Expand Up @@ -540,30 +537,30 @@ private GraphQLArgument buildArgument(BuildContext buildCtx, InputValueDefinitio
private Object buildValue(Value value, GraphQLType requiredType) {
Object result = null;
if (requiredType instanceof GraphQLNonNull) {
requiredType = ((GraphQLNonNull)requiredType).getWrappedType();
requiredType = ((GraphQLNonNull) requiredType).getWrappedType();
}
if (requiredType instanceof GraphQLScalarType) {
result = ((GraphQLScalarType)requiredType).getCoercing().parseLiteral(value);
result = ((GraphQLScalarType) requiredType).getCoercing().parseLiteral(value);
} else if (value instanceof EnumValue && requiredType instanceof GraphQLEnumType) {
result = ((EnumValue) value).getName();
} else if (value instanceof ArrayValue && requiredType instanceof GraphQLList) {
ArrayValue arrayValue = (ArrayValue) value;
GraphQLType wrappedType = ((GraphQLList) requiredType).getWrappedType();
result = arrayValue.getValues().stream()
.map(item -> this.buildValue(item, wrappedType)).collect(Collectors.toList());
.map(item -> this.buildValue(item, wrappedType)).collect(Collectors.toList());
} else if (value instanceof ObjectValue && requiredType instanceof GraphQLInputObjectType) {
result = buildObjectValue((ObjectValue) value, (GraphQLInputObjectType) requiredType);
} else if (value != null && !(value instanceof NullValue)) {
Assert.assertShouldNeverHappen(
"cannot build value of " + requiredType.getName() + " from " + String.valueOf(value));
"cannot build value of " + requiredType.getName() + " from " + String.valueOf(value));
}
return result;
}

private Object buildObjectValue(ObjectValue defaultValue, GraphQLInputObjectType objectType) {
HashMap<String, Object> map = new LinkedHashMap<>();
defaultValue.getObjectFields().forEach(of -> map.put(of.getName(),
buildValue(of.getValue(), objectType.getField(of.getName()).getType())));
buildValue(of.getValue(), objectType.getField(of.getName()).getType())));
return map;
}

Expand Down
63 changes: 63 additions & 0 deletions src/test/groovy/graphql/schema/GraphQLSchemaTest.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package graphql.schema

import graphql.ExecutionInput
import graphql.GraphQL
import graphql.TestUtil
import graphql.schema.idl.RuntimeWiring
import spock.lang.Specification

class GraphQLSchemaTest extends Specification {

def "#698 interfaces copied as expected"() {

def idl = """
type Query {
foo: Node
}

interface Node {
id: String
}

type Foo implements Node {
id: String
}
"""

RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
.type("Query", { wiring ->
wiring.dataFetcher("foo", { env ->
Map<String, Object> map = new HashMap<>()
map.put("id", "abc")
return map
})
})
.type("Node", { wiring ->
wiring.typeResolver({ env -> (GraphQLObjectType) env.getSchema().getType("Foo") })
})
.build()

def existingSchema = TestUtil.schema(idl, runtimeWiring)


GraphQLSchema schema = GraphQLSchema.newSchema(existingSchema).build()

expect:
assert 0 == runQuery(existingSchema).getErrors().size()
assert 0 == runQuery(schema).getErrors().size()
}


def runQuery(GraphQLSchema schema) {
GraphQL graphQL = GraphQL.newGraphQL(schema)
.build()

ExecutionInput executionInput = ExecutionInput.newExecutionInput()
.query("{foo {id}}")
.build()

return graphQL
.executeAsync(executionInput)
.join()
}
}
30 changes: 30 additions & 0 deletions src/test/groovy/graphql/schema/idl/SchemaGeneratorTest.groovy
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package graphql.schema.idl

import graphql.schema.GraphQLEnumType
import graphql.schema.GraphQLFieldDefinition
import graphql.schema.GraphQLFieldsContainer
import graphql.schema.GraphQLInputObjectType
import graphql.schema.GraphQLInterfaceType
import graphql.schema.GraphQLList
Expand All @@ -11,6 +13,7 @@ import graphql.schema.GraphQLType
import graphql.schema.GraphQLUnionType
import graphql.schema.idl.errors.NotAnInputTypeError
import graphql.schema.idl.errors.NotAnOutputTypeError
import graphql.schema.visibility.GraphqlFieldVisibility
import spock.lang.Specification

import java.util.function.UnaryOperator
Expand Down Expand Up @@ -1005,4 +1008,31 @@ class SchemaGeneratorTest extends Specification {
arg["str"] instanceof String
arg["num"] instanceof Integer
}

def "field visibility is used"() {
def spec = """
type Query {
field : String
}
"""

GraphqlFieldVisibility fieldVisibility = new GraphqlFieldVisibility() {
@Override
List<GraphQLFieldDefinition> getFieldDefinitions(GraphQLFieldsContainer fieldsContainer) {
return null
}

@Override
GraphQLFieldDefinition getFieldDefinition(GraphQLFieldsContainer fieldsContainer, String fieldName) {
return null
}
}

def schema = schema(spec, RuntimeWiring.newRuntimeWiring().fieldVisibility(fieldVisibility).build())

expect:

schema.getFieldVisibility() == fieldVisibility

}
}