diff --git a/src/main/java/graphql/schema/GraphQLSchema.java b/src/main/java/graphql/schema/GraphQLSchema.java index 4604365db2..afa80993c1 100644 --- a/src/main/java/graphql/schema/GraphQLSchema.java +++ b/src/main/java/graphql/schema/GraphQLSchema.java @@ -125,7 +125,9 @@ 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 { @@ -133,6 +135,8 @@ public static class Builder { private GraphQLObjectType mutationType; private GraphQLObjectType subscriptionType; private GraphqlFieldVisibility fieldVisibility = DEFAULT_FIELD_VISIBILITY; + private Set additionalTypes = Collections.emptySet(); + private Set additionalDirectives = Collections.emptySet(); public Builder query(GraphQLObjectType.Builder builder) { return query(builder.build()); @@ -166,8 +170,18 @@ public Builder fieldVisibility(GraphqlFieldVisibility fieldVisibility) { return this; } + public Builder additionalTypes(Set additionalTypes) { + this.additionalTypes = additionalTypes; + return this; + } + + public Builder additionalDirectives(Set additionalDirectives) { + this.additionalDirectives = additionalDirectives; + return this; + } + public GraphQLSchema build() { - return build(Collections.emptySet(), Collections.emptySet()); + return build(additionalTypes, additionalDirectives); } public GraphQLSchema build(Set additionalTypes) { diff --git a/src/main/java/graphql/schema/idl/RuntimeWiring.java b/src/main/java/graphql/schema/idl/RuntimeWiring.java index a95f5e5004..bc937a6387 100644 --- a/src/main/java/graphql/schema/idl/RuntimeWiring.java +++ b/src/main/java/graphql/schema/idl/RuntimeWiring.java @@ -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} @@ -24,14 +27,16 @@ public class RuntimeWiring { private final Map typeResolvers; private final WiringFactory wiringFactory; private final Map enumValuesProviders; + private final GraphqlFieldVisibility fieldVisibility; - private RuntimeWiring(Map> dataFetchers, Map defaultDataFetchers, Map scalars, Map typeResolvers, Map enumValuesProviders, WiringFactory wiringFactory) { + private RuntimeWiring(Map> dataFetchers, Map defaultDataFetchers, Map scalars, Map typeResolvers, Map 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; } /** @@ -69,6 +74,10 @@ public WiringFactory getWiringFactory() { return wiringFactory; } + public GraphqlFieldVisibility getFieldVisibility() { + return fieldVisibility; + } + @PublicApi public static class Builder { private final Map> dataFetchers = new LinkedHashMap<>(); @@ -77,6 +86,7 @@ public static class Builder { private final Map typeResolvers = new LinkedHashMap<>(); private final Map enumValuesProviders = new LinkedHashMap<>(); private WiringFactory wiringFactory = new NoopWiringFactory(); + private GraphqlFieldVisibility fieldVisibility = DEFAULT_FIELD_VISIBILITY; private Builder() { ScalarInfo.STANDARD_SCALARS.forEach(this::scalar); @@ -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; } @@ -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 * @@ -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); } } diff --git a/src/main/java/graphql/schema/idl/SchemaGenerator.java b/src/main/java/graphql/schema/idl/SchemaGenerator.java index 84fba2b593..72cafe3cb7 100644 --- a/src/main/java/graphql/schema/idl/SchemaGenerator.java +++ b/src/main/java/graphql/schema/idl/SchemaGenerator.java @@ -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; @@ -219,6 +215,7 @@ private GraphQLSchema makeExecutableSchemaImpl(BuildContext buildCtx) { Set additionalTypes = buildAdditionalTypes(buildCtx); + schemaBuilder.fieldVisibility(buildCtx.getWiring().getFieldVisibility()); return schemaBuilder.build(additionalTypes); } @@ -540,22 +537,22 @@ 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; } @@ -563,7 +560,7 @@ private Object buildValue(Value value, GraphQLType requiredType) { private Object buildObjectValue(ObjectValue defaultValue, GraphQLInputObjectType objectType) { HashMap 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; } diff --git a/src/test/groovy/graphql/schema/GraphQLSchemaTest.groovy b/src/test/groovy/graphql/schema/GraphQLSchemaTest.groovy new file mode 100644 index 0000000000..f602653d37 --- /dev/null +++ b/src/test/groovy/graphql/schema/GraphQLSchemaTest.groovy @@ -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 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() + } +} diff --git a/src/test/groovy/graphql/schema/idl/SchemaGeneratorTest.groovy b/src/test/groovy/graphql/schema/idl/SchemaGeneratorTest.groovy index 54df348cb8..4379230666 100644 --- a/src/test/groovy/graphql/schema/idl/SchemaGeneratorTest.groovy +++ b/src/test/groovy/graphql/schema/idl/SchemaGeneratorTest.groovy @@ -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 @@ -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 @@ -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 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 + + } } \ No newline at end of file