Skip to content

Commit

Permalink
Update filter handler functions to use FilterOperationInfo objects.
Browse files Browse the repository at this point in the history
  • Loading branch information
obi1kenobi committed Oct 24, 2017
1 parent da6e03c commit f50fc3a
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 136 deletions.
24 changes: 7 additions & 17 deletions graphql_compiler/compiler/compiler_frontend.py
Expand Up @@ -72,8 +72,8 @@
validate_vertex_field_directive_interactions)
from .filters import process_filter_directive
from .helpers import (FoldScopeLocation, Location, get_ast_field_name, get_field_type_from_schema,
get_uniquely_named_objects_by_name, is_vertex_field_name,
strip_non_null_from_type, validate_safe_string)
get_uniquely_named_objects_by_name, get_vertex_field_type,
is_vertex_field_name, strip_non_null_from_type, validate_safe_string)


# The OutputMetadata will have the following types for its members:
Expand Down Expand Up @@ -357,17 +357,7 @@ def _compile_vertex_ast(schema, current_schema_type, ast,
inner_location = location.navigate_to_subpath(field_name)
validate_context_for_visiting_vertex_field(inner_location, context)

# The field itself is of type GraphQLList, and this is
# what get_field_type_from_schema returns.
# We care about what the type *inside* the list is,
# i.e., the type on the other side of the edge (hence .of_type).
# Validation guarantees that the field must exist in the schema.
edge_schema_type = get_field_type_from_schema(current_schema_type, field_name)
if not isinstance(strip_non_null_from_type(edge_schema_type), GraphQLList):
raise AssertionError(u'Found an edge whose schema type was not GraphQLList: '
u'{} {} {}'.format(current_schema_type, field_name,
edge_schema_type))
field_schema_type = edge_schema_type.of_type
field_schema_type = get_vertex_field_type(current_schema_type, field_name)

inner_unique_directives = get_unique_directives(field_ast)
validate_vertex_field_directive_interactions(inner_location, inner_unique_directives)
Expand Down Expand Up @@ -519,7 +509,8 @@ def _compile_ast_node_to_ir(schema, current_schema_type, ast, location, context)
fields = _get_fields(ast)
vertex_fields, property_fields = fields
fragment = _get_inline_fragment(ast)
local_filters_directives = get_local_filter_directives(ast, vertex_fields)
filter_operations = get_local_filter_directives(
ast, current_schema_type, vertex_fields)

# We don't support type coercion while at the same time selecting fields.
# Either there are no fields, or there is no fragment, otherwise we raise a compilation error.
Expand All @@ -542,10 +533,9 @@ def _compile_ast_node_to_ir(schema, current_schema_type, ast, location, context)
u'{} {}'.format(location, property_fields))

# step 1: apply local filter, if any
for filter_directive in local_filters_directives:
for filter_operation_info in filter_operations:
basic_blocks.append(
process_filter_directive(schema, current_schema_type,
ast, context, filter_directive))
process_filter_directive(filter_operation_info, context))

if location.field is not None:
# The location is at a property, compile the property data following P-steps.
Expand Down
55 changes: 44 additions & 11 deletions graphql_compiler/compiler/directive_helpers.py
@@ -1,10 +1,13 @@
# Copyright 2017 Kensho Technologies, Inc.
"""Helper functions for dealing with GraphQL directives."""

from graphql.language.ast import InlineFragment
import six

from ..exceptions import GraphQLCompilationError
from .filters import is_filter_with_outer_scope_vertex_field_operator
from .helpers import (FilterOperationInfo, get_ast_field_name, get_ast_field_name_or_none,
get_vertex_field_type, is_vertex_field_type)


ALLOWED_DUPLICATED_DIRECTIVES = frozenset({'filter'})
Expand Down Expand Up @@ -49,7 +52,7 @@ def get_unique_directives(ast):
return result


def get_local_filter_directives(ast, inner_vertex_fields):
def get_local_filter_directives(ast, current_schema_type, inner_vertex_fields):
"""Get all filter directives that apply to the current field.
This helper abstracts away the fact that some vertex field filtering operators apply on the
Expand All @@ -59,34 +62,59 @@ def get_local_filter_directives(ast, inner_vertex_fields):
Args:
ast: a GraphQL AST object for which to load local filters, from the graphql library
current_schema_type: GraphQLType, the schema type at the current AST location
inner_vertex_fields: a list of inner AST objects representing vertex fields that are within
the current field. If currently processing a property field (i.e.
there are no inner vertex fields), this argument may be set to None.
Returns:
list of filter directive objects
list of FilterOperationInfo objects.
If the field_ast field is of type InlineFragment, the field_name field is set to None.
"""
result = []
if ast.directives: # it'll be None if the AST has no directives at that node
for directive_obj in ast.directives:
if directive_obj.name.value != 'filter':
continue

# Of all filters that appear *on the field itself*, only the ones that apply
# to the outer scope are not considered "local" and are not to be returned.
if not is_filter_with_outer_scope_vertex_field_operator(directive_obj):
result.append(directive_obj)
if directive_obj.name.value == 'filter':
filtered_field_name = get_ast_field_name_or_none(ast)
if is_filter_with_outer_scope_vertex_field_operator(directive_obj):
# We found a filter that affects the outer scope vertex. Let's make sure
# we are at a vertex field. If we are actually at a property field,
# that is a compilation error.
if not is_vertex_field_type(current_schema_type):
raise GraphQLCompilationError(
u'Found disallowed filter on a property field: {} {} '
u'{}'.format(directive_obj, current_schema_type, filtered_field_name))
elif isinstance(ast, InlineFragment):
raise GraphQLCompilationError(
u'Found disallowed filter on a type coercion: {} '
u'{}'.format(directive_obj, current_schema_type))
else:
# The filter is valid and non-local, since it is applied at this AST node
# but affects the outer scope vertex field. Skip over it.
pass
else:
operation = FilterOperationInfo(
directive=directive_obj, field_name=filtered_field_name,
field_type=current_schema_type, field_ast=ast)
result.append(operation)

if inner_vertex_fields: # allow the argument to be None
for inner_ast in inner_vertex_fields:
for directive_obj in inner_ast.directives:
if directive_obj.name.value != 'filter':
continue

# Of all filters that appear on an inner vertex field, only the ones that apply
# to the outer scope are "local" to the outer field and therefore to be returned.
if is_filter_with_outer_scope_vertex_field_operator(directive_obj):
result.append(directive_obj)
# The inner AST must not be an InlineFragment, so it must have a field name.
filtered_field_name = get_ast_field_name(inner_ast)
filtered_field_type = get_vertex_field_type(
current_schema_type, filtered_field_name)

operation = FilterOperationInfo(
directive=directive_obj, field_name=filtered_field_name,
field_type=filtered_field_type, field_ast=inner_ast)
result.append(operation)

return result

Expand All @@ -112,6 +140,11 @@ def validate_root_vertex_directives(root_ast):
directives_present_at_root = set()
for directive_obj in root_ast.directives:
directive_name = directive_obj.name.value

if is_filter_with_outer_scope_vertex_field_operator(directive_obj):
raise GraphQLCompilationError(u'Found a filter directive with an operator that is not'
u'allowed on the root vertex: {}'.format(directive_obj))

directives_present_at_root.add(directive_name)

disallowed_directives = directives_present_at_root & VERTEX_DIRECTIVES_PROHIBITED_ON_ROOT
Expand Down

0 comments on commit f50fc3a

Please sign in to comment.