Thanks to visit codestin.com
Credit goes to GitHub.com

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
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
import graphql.PublicApi;
import graphql.schema.GraphQLEnumType;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLFieldsContainer;
import graphql.schema.GraphQLImplementingType;
import graphql.schema.GraphQLInputObjectField;
import graphql.schema.GraphQLInputObjectType;
import graphql.schema.GraphQLInterfaceType;
import graphql.schema.GraphQLNamedSchemaElement;
import graphql.schema.GraphQLNamedType;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;
Expand All @@ -24,6 +24,7 @@

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -45,7 +46,9 @@ public class FieldVisibilitySchemaTransformation {
private final Runnable afterTransformationHook;

public FieldVisibilitySchemaTransformation(VisibleFieldPredicate visibleFieldPredicate) {
this(visibleFieldPredicate, () -> {}, () -> {});
this(visibleFieldPredicate, () -> {
}, () -> {
});
}

public FieldVisibilitySchemaTransformation(VisibleFieldPredicate visibleFieldPredicate,
Expand Down Expand Up @@ -155,40 +158,85 @@ private static class FieldRemovalVisitor extends GraphQLTypeVisitorStub {
private final VisibleFieldPredicate visibilityPredicate;
private final Set<GraphQLType> removedTypes;

private final Set<GraphQLFieldDefinition> fieldDefinitionsToActuallyRemove = new LinkedHashSet<>();
private final Set<GraphQLInputObjectField> inputObjectFieldsToDelete = new LinkedHashSet<>();

private FieldRemovalVisitor(VisibleFieldPredicate visibilityPredicate,
Set<GraphQLType> removedTypes) {
this.visibilityPredicate = visibilityPredicate;
this.removedTypes = removedTypes;
}

@Override
public TraversalControl visitGraphQLFieldDefinition(GraphQLFieldDefinition definition,
TraverserContext<GraphQLSchemaElement> context) {
return visitField(definition, context);
public TraversalControl visitGraphQLObjectType(GraphQLObjectType objectType, TraverserContext<GraphQLSchemaElement> context) {
return visitFieldsContainer(objectType, context);
}

@Override
public TraversalControl visitGraphQLInputObjectField(GraphQLInputObjectField definition,
TraverserContext<GraphQLSchemaElement> context) {
return visitField(definition, context);
public TraversalControl visitGraphQLInterfaceType(GraphQLInterfaceType objectType, TraverserContext<GraphQLSchemaElement> context) {
return visitFieldsContainer(objectType, context);
}

private TraversalControl visitField(GraphQLNamedSchemaElement element,
TraverserContext<GraphQLSchemaElement> context) {

VisibleFieldPredicateEnvironment environment = new VisibleFieldPredicateEnvironmentImpl(
element, context.getParentNode());
if (!visibilityPredicate.isVisible(environment)) {
deleteNode(context);
private TraversalControl visitFieldsContainer(GraphQLFieldsContainer fieldsContainer, TraverserContext<GraphQLSchemaElement> context) {
boolean allFieldsDeleted = true;
for (GraphQLFieldDefinition fieldDefinition : fieldsContainer.getFieldDefinitions()) {
VisibleFieldPredicateEnvironment environment = new VisibleFieldPredicateEnvironmentImpl(
fieldDefinition, fieldsContainer);
if (!visibilityPredicate.isVisible(environment)) {
fieldDefinitionsToActuallyRemove.add(fieldDefinition);
removedTypes.add(fieldDefinition.getType());
} else {
allFieldsDeleted = false;
}
}
if (allFieldsDeleted) {
// we are deleting the whole interface type because all fields are supposed to be deleted
return deleteNode(context);
} else {
return TraversalControl.CONTINUE;
}
}

if (element instanceof GraphQLFieldDefinition) {
removedTypes.add(((GraphQLFieldDefinition) element).getType());
} else if (element instanceof GraphQLInputObjectField) {
removedTypes.add(((GraphQLInputObjectField) element).getType());
@Override
public TraversalControl visitGraphQLInputObjectType(GraphQLInputObjectType inputObjectType, TraverserContext<GraphQLSchemaElement> context) {
boolean allFieldsDeleted = true;
for (GraphQLInputObjectField inputField : inputObjectType.getFieldDefinitions()) {
VisibleFieldPredicateEnvironment environment = new VisibleFieldPredicateEnvironmentImpl(
inputField, inputObjectType);
if (!visibilityPredicate.isVisible(environment)) {
inputObjectFieldsToDelete.add(inputField);
removedTypes.add(inputField.getType());
} else {
allFieldsDeleted = false;
}
}
if (allFieldsDeleted) {
// we are deleting the whole input object type because all fields are supposed to be deleted
return deleteNode(context);
} else {
return TraversalControl.CONTINUE;
}

return TraversalControl.CONTINUE;
}

@Override
public TraversalControl visitGraphQLFieldDefinition(GraphQLFieldDefinition definition,
TraverserContext<GraphQLSchemaElement> context) {
if (fieldDefinitionsToActuallyRemove.contains(definition)) {
return deleteNode(context);
} else {
return TraversalControl.CONTINUE;
}
}

@Override
public TraversalControl visitGraphQLInputObjectField(GraphQLInputObjectField definition,
TraverserContext<GraphQLSchemaElement> context) {
if (inputObjectFieldsToDelete.contains(definition)) {
return deleteNode(context);
} else {
return TraversalControl.CONTINUE;
}
}
}

Expand Down Expand Up @@ -216,12 +264,12 @@ public TraversalControl visitGraphQLInterfaceType(GraphQLInterfaceType node,
public TraversalControl visitGraphQLType(GraphQLSchemaElement node,
TraverserContext<GraphQLSchemaElement> context) {
if (observedBeforeTransform.contains(node) &&
!observedAfterTransform.contains(node) &&
(node instanceof GraphQLObjectType ||
node instanceof GraphQLEnumType ||
node instanceof GraphQLInputObjectType ||
node instanceof GraphQLInterfaceType ||
node instanceof GraphQLUnionType)) {
!observedAfterTransform.contains(node) &&
(node instanceof GraphQLObjectType ||
node instanceof GraphQLEnumType ||
node instanceof GraphQLInputObjectType ||
node instanceof GraphQLInterfaceType ||
node instanceof GraphQLUnionType)) {

return deleteNode(context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ class FieldVisibilitySchemaTransformationTest extends Specification {
def visibilitySchemaTransformation = new FieldVisibilitySchemaTransformation({ environment ->
def directives = (environment.schemaElement as GraphQLDirectiveContainer).appliedDirectives
return directives.find({ directive -> directive.name == "private" }) == null
}, { -> callbacks << "before" }, { -> callbacks << "after"} )
}, { -> callbacks << "before" }, { -> callbacks << "after" })

GraphQLSchema schema = TestUtil.schema("""

Expand Down Expand Up @@ -1245,5 +1245,70 @@ class FieldVisibilitySchemaTransformationTest extends Specification {
then:
(restrictedSchema.getType("Account") as GraphQLObjectType).getFieldDefinition("billingStatus") == null
restrictedSchema.getType("BillingStatus") == null

}

def "remove all fields from a type which is referenced via additional types"() {
given:
GraphQLSchema schema = TestUtil.schema("""
directive @private on FIELD_DEFINITION
type Query {
foo: Foo
}
type Foo {
foo: String
toDelete: ToDelete @private
}
type ToDelete {
toDelete:String @private
}
""")

when:
schema.typeMap
def patchedSchema = schema.transform { builder ->
schema.typeMap.each { entry ->
def type = entry.value
if (type != schema.queryType && type != schema.mutationType && type != schema.subscriptionType) {
builder.additionalType(type)
}
}
}
GraphQLSchema restrictedSchema = visibilitySchemaTransformation.apply(patchedSchema)
then:
(restrictedSchema.getType("Foo") as GraphQLObjectType).getFieldDefinition("toDelete") == null
}

def "remove all fields from an input type which is referenced via additional types"() {
given:
GraphQLSchema schema = TestUtil.schema("""
directive @private on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
type Query {
foo(input: Input): String
}
input Input {
foo: String
toDelete:ToDelete @private
}
input ToDelete {
toDelete:String @private
}
""")

when:
schema.typeMap
def patchedSchema = schema.transform { builder ->
schema.typeMap.each { entry ->
def type = entry.value
if (type != schema.queryType && type != schema.mutationType && type != schema.subscriptionType) {
builder.additionalType(type)
}
}
}
GraphQLSchema restrictedSchema = visibilitySchemaTransformation.apply(patchedSchema)
then:
(restrictedSchema.getType("Input") as GraphQLInputObjectType).getFieldDefinition("toDelete") == null
}


}
Loading