Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Macro system #356

Merged
merged 121 commits into from Sep 6, 2019
Merged
Show file tree
Hide file tree
Changes from 113 commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
e74ab48
Add a skeleton for the macro expansion system. (#181)
obi1kenobi Feb 5, 2019
06c672b
Add a few helpers for macro validation. (#183)
obi1kenobi Feb 6, 2019
11c6705
Add a few core macro edge validation steps. (#186)
obi1kenobi Feb 7, 2019
3259797
Add more validation, together with the new 2-directive macro format. …
obi1kenobi Feb 11, 2019
690b435
Test invalid edge macros (#188)
bojanserafimov Feb 11, 2019
b1480b5
Bugfix for macro validation, and more validation checks. (#189)
obi1kenobi Feb 11, 2019
03f7d9a
Move test_macro_expansion_errors to test_macro_validation (#192)
bojanserafimov Feb 11, 2019
302760a
Subclasses (#185)
bojanserafimov Feb 12, 2019
304a086
Deduplicate parsing and validation code, fix a schema/type checking b…
obi1kenobi Feb 12, 2019
15c5c49
Add partial implementation of macro edge expansion. (#194)
obi1kenobi Feb 13, 2019
8026ed5
Test macro edge expansion (#190)
bojanserafimov Feb 13, 2019
8b04e66
Add subclass_sets argument to macro expansion (#195)
bojanserafimov Feb 14, 2019
d853488
Add and fix macro expansion tests (#196)
bojanserafimov Feb 14, 2019
30d5460
Keep the @macro_edge_target in the macro descriptor (#197)
bojanserafimov Feb 14, 2019
ec471b1
Expand specific macro (#198)
bojanserafimov Feb 14, 2019
6a71a00
Address comments on expand_specific_macro (#200)
bojanserafimov Feb 25, 2019
5cf2564
Compile macro to ir (#199)
bojanserafimov Feb 26, 2019
cc9343c
Disallow @macro_edge_target on union type (#203)
bojanserafimov Feb 26, 2019
3281eed
Type coercion at @macro_edge_target (#202)
bojanserafimov Feb 27, 2019
f9f5bb9
Macro edge validation for use of directives (#205)
bojanserafimov Mar 20, 2019
7a26634
Check macro argument types (#206)
bojanserafimov Mar 21, 2019
a2c66a9
Fix tests (#226)
bojanserafimov Mar 21, 2019
0f70206
Merge master into macro_system, resolving conflicts (#227)
bojanserafimov Mar 21, 2019
44bb12d
Add schema, hints, and subclass_sets to macro_registry
Mar 25, 2019
eb32f95
Fix lint
Mar 25, 2019
a565504
Deepcopy schema
Mar 25, 2019
190c6cd
Add schema_without_macros to macro_registry (#230)
bojanserafimov Mar 25, 2019
0652e2d
WIP
Mar 26, 2019
2219f51
Implement
Mar 26, 2019
9c2aef4
Merge branch 'macro_system' into implement_schema_with_macros
Mar 26, 2019
8cab638
Raise NotIpmlementedError
Mar 26, 2019
e335d00
Address comments
Mar 26, 2019
02885b9
Merge pull request #232 from kensho-technologies/implement_schema_wit…
pmantica10 Mar 26, 2019
31daca1
Make macro edge schema fields point to list types (#233)
bojanserafimov Mar 27, 2019
a6a72bc
Fix docstring in validation (#236)
bojanserafimov Mar 29, 2019
e2458c4
Fix macro on superclass (#235)
bojanserafimov Mar 29, 2019
5049c64
Fix slow subclass sets computation (#276)
bojanserafimov May 13, 2019
6bceccb
Add test and fix broken tests in macro expansion (#275)
bojanserafimov May 13, 2019
96b5fad
Remove stale TODOs (#278)
bojanserafimov May 13, 2019
bfe174f
Validate macro edge does not exist on sub/superclass (#277)
bojanserafimov May 13, 2019
8e4865e
Only allow supported directives at macro expansion
May 13, 2019
05fb5bf
Tidy up
May 13, 2019
eb18e2f
Lint
May 14, 2019
515bda9
Add tests for directives on expansion
May 14, 2019
66a3c4f
Merge pull request #282 from kensho-technologies/macro_expansion_dire…
pmantica10 May 15, 2019
2b92547
Add macro edge target validation (#279)
bojanserafimov May 15, 2019
81a73c9
Add macro system documentation
May 16, 2019
394241c
Add macro registry example
May 16, 2019
71d1cdc
Fix example
May 16, 2019
8c9e5a2
Fix link
May 16, 2019
b981548
Address some nits
May 16, 2019
8de182c
Add example with macro runtime parameters
May 16, 2019
bdd74c6
Add code example
May 17, 2019
c009b22
Edit example
May 17, 2019
f9b7f72
Address nits
May 17, 2019
37bd6a6
Merge pull request #293 from kensho-technologies/edge_macro_readme
pmantica10 May 17, 2019
d09ef49
Merge branch 'master' into merge_master_into_edge_macros
May 20, 2019
6c3d676
Isort
May 20, 2019
9eaaa36
Merge branch 'master' into merge_master_into_edge_macros
May 20, 2019
48ef812
Merge pull request #303 from kensho-technologies/merge_master_into_ed…
pmantica10 May 20, 2019
3472b15
Macro tag names (#281)
bojanserafimov May 21, 2019
e9a82b0
Remove stale test (#307)
bojanserafimov May 21, 2019
aefd4ec
Add Macro edge allowed/disallowed/required directive lists to directi…
May 23, 2019
23a8ab0
Nit
May 23, 2019
840100c
Nit
May 23, 2019
93a8123
Merge branch 'macro_system' of github.com:ReaLNeroM/graphql-compiler …
May 23, 2019
de12d3b
Merge branch 'macro_system' of github.com:ReaLNeroM/graphql-compiler …
May 23, 2019
6257975
Merge branch 'macro_system' of github.com:ReaLNeroM/graphql-compiler …
May 23, 2019
344f4d5
Address comments, Validator compares directives
May 24, 2019
aabdaba
Revert directive comparison including args, location
May 24, 2019
4c0da02
Rename optional directive list to better reflect usage
May 24, 2019
5dca567
Address comments, replace OPTIONAL_UNRESTRICTED directive list with j…
May 24, 2019
d0af3f6
Add allowed directive list, remove optional directive list
May 24, 2019
a75a17c
Merge pull request #316 from ReaLNeroM/macro_system
pmantica10 May 25, 2019
e33e954
Schema for macro definitions (#319)
May 29, 2019
01bfe47
Add TODO where get_schema_for_macro_definition should be used
May 29, 2019
f728a8a
Nit
vmaksimovski May 30, 2019
2ed4dda
Merge pull request #328 from ReaLNeroM/macro_system
pmantica10 May 30, 2019
aa11a36
Merge master to macro system (#352)
bojanserafimov Jun 11, 2019
8989492
Make sure the tag is not reordered afer the filter (#322)
bojanserafimov Jun 12, 2019
ca64318
Merge master into macro system (#355)
bojanserafimov Jun 14, 2019
095e818
Merge branch 'master' into macro_system
Jun 14, 2019
76d7908
Fix bug in macro expansion with pro-forma fields (#415)
bojanserafimov Jul 22, 2019
9365587
Remove add transitive closure (#443)
pmantica10 Aug 1, 2019
75bfeba
Merge branch 'master' into macro_system
obi1kenobi Aug 2, 2019
62e22c6
Make "macro_edge" usable in SDL field definitions.
obi1kenobi Aug 2, 2019
9c3a496
Merge branch 'master' into macro_system
obi1kenobi Aug 5, 2019
2e7bbff
Break out README changes into their own branch, for easier review.
obi1kenobi Aug 5, 2019
193bcb1
Delint.
obi1kenobi Aug 5, 2019
6420551
Merge branch 'master' into macro_system
obi1kenobi Aug 7, 2019
147acc5
Merge branch 'master' into macro_system
obi1kenobi Aug 7, 2019
3678436
Integrate AST helpers refactor.
obi1kenobi Aug 7, 2019
2f59a36
Delint.
obi1kenobi Aug 7, 2019
2d87ce8
Merge branch 'master' into macro_system
obi1kenobi Aug 19, 2019
667a6ae
Merge branch 'master' into macro_system
obi1kenobi Aug 26, 2019
e474fd4
Fix cross-module import. Allow deprecated directive.
obi1kenobi Aug 26, 2019
6fcf0e0
Refactor validation, adding stricter checks and better error messages.
obi1kenobi Aug 26, 2019
80b2efd
Disallow `@fold` directives on macro edges.
obi1kenobi Aug 26, 2019
18e284d
Disallow `@output_source` directives on macro edges as well.
obi1kenobi Aug 26, 2019
bdb24bf
Only allow filter directives at macro expansion points.
obi1kenobi Aug 27, 2019
88193ba
Improve error message for unsupported directive on macro edge.
obi1kenobi Aug 27, 2019
860d6c0
Refactor macro edge expansion code to move it into its own module.
obi1kenobi Aug 28, 2019
1c75da0
Merge branch 'master' into macro_system
obi1kenobi Aug 28, 2019
1316dff
Merge branch 'master' into macro_system
obi1kenobi Aug 28, 2019
587e20a
Remove dead code from helpers file.
obi1kenobi Aug 29, 2019
fdc6dc8
Mark tests of unimplemented functionality with pytest xfail mark.
obi1kenobi Sep 3, 2019
d75dbba
Add validation for schema conflicts of reverse macro edges.
obi1kenobi Sep 4, 2019
d52614a
Add validation error message.
obi1kenobi Sep 4, 2019
784d469
Add more reverse macro edge validation logic.
obi1kenobi Sep 4, 2019
353e4f9
Add macro edge target class name to macro edge descriptor.
obi1kenobi Sep 4, 2019
da2e97c
Tweak test cases to ensure all validation error messages are hit.
obi1kenobi Sep 4, 2019
dca96f9
Delint.
obi1kenobi Sep 4, 2019
33708bb
Merge branch 'master' into macro_system
obi1kenobi Sep 4, 2019
5ff443b
Refactor cross-macro validation code.
obi1kenobi Sep 6, 2019
14ac93f
Merge branch 'master' into macro_system
obi1kenobi Sep 6, 2019
7b914e5
Delint.
obi1kenobi Sep 6, 2019
9fb1e1b
Explicitly error out on duplicate tags during expansion.
obi1kenobi Sep 6, 2019
b558389
Add macro validity rules description.
obi1kenobi Sep 6, 2019
0f677b5
Fix typo.
obi1kenobi Sep 6, 2019
4f05624
Remove stale TODO.
obi1kenobi Sep 6, 2019
5384e33
Refactor and deduplicate validation code.
obi1kenobi Sep 6, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
410 changes: 410 additions & 0 deletions graphql_compiler/macros/__init__.py

Large diffs are not rendered by default.

45 changes: 0 additions & 45 deletions graphql_compiler/macros/macro_edge/ast_rewriting.py
Expand Up @@ -238,51 +238,6 @@ def remove_directives_from_ast(ast, directive_names_to_omit):
return new_ast


def omit_ast_from_ast_selections(ast, ast_to_omit):
Copy link
Contributor

Choose a reason for hiding this comment

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

This function was never used, so I deleted it.

"""Return an equivalent AST to the input, but with the specified AST omitted if it appears.

Args:
ast: GraphQL library AST object, such as a Field, InlineFragment, or OperationDefinition
ast_to_omit: GraphQL library AST object, the *exact same* object that should be omitted.
This function uses reference equality, since deep equality can get expensive.

Returns:
GraphQL library AST object, equivalent to the input one, with all instances of
the specified AST omitted. If the specified AST does not appear in the input AST,
the returned object is the exact same object as the input.
"""
if not isinstance(ast, (Field, InlineFragment, OperationDefinition)):
return ast

if ast.selection_set is None:
return ast

made_changes = False

selections_to_keep = []
for selection_ast in ast.selection_set.selections:
if selection_ast is ast_to_omit:
# Drop the current selection.
made_changes = True
else:
new_selection_ast = omit_ast_from_ast_selections(selection_ast, ast_to_omit)
if new_selection_ast is not selection_ast:
# The current selection contained the AST to omit, and was altered as a result.
made_changes = True
selections_to_keep.append(new_selection_ast)

if not made_changes:
return ast

new_ast = copy(ast)
if not selections_to_keep:
new_ast.selection_set = None
else:
new_ast.selection_set = SelectionSet(selections_to_keep)

return new_ast


def find_target_and_copy_path_to_it(ast):
"""Copy the AST objects on the path to the target, returning the copied AST and the target AST.

Expand Down
24 changes: 13 additions & 11 deletions graphql_compiler/macros/macro_edge/descriptor.py
Expand Up @@ -8,25 +8,27 @@

MacroEdgeDescriptor = namedtuple(
'MacroEdgeDescriptor', (
'base_class_name', # str, name of GraphQL type where the macro edge is defined.
# The macro edge exists at this type and all of its subtypes.
'macro_edge_name', # str, name of the vertex field corresponding to this macro edge.
# Should start with "out_" or "in_", per GraphQL compiler convention.
'expansion_ast', # GraphQL AST object defining how the macro edge
# should be expanded starting from its base type. The
# selections must be merged (on both endpoints of the
# macro edge) with the user-supplied GraphQL input.
'macro_args', # Dict[str, Any] containing any arguments required by the macro
'base_class_name', # str, name of GraphQL type where the macro edge is defined.
# The macro edge exists at this type and all of its subtypes.
'target_class_name', # str, the name of the GraphQL type that the macro edge points to.
Copy link
Contributor

Choose a reason for hiding this comment

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

Storing the target class name made a lot of validation code simpler, and means that we don't have to keep recomputing this by walking the macro definition ASTs in situations like generating the schema with macro edges included.

'macro_edge_name', # str, name of the vertex field corresponding to this macro edge.
# Should start with "out_" or "in_", per GraphQL compiler convention.
'expansion_ast', # GraphQL AST object defining how the macro edge
# should be expanded starting from its base type. The
# selections must be merged (on both endpoints of the
# macro edge) with the user-supplied GraphQL input.
'macro_args', # Dict[str, Any] containing any arguments required by the macro
)
)


def create_descriptor_from_ast_and_args(class_name, macro_edge_name,
def create_descriptor_from_ast_and_args(class_name, target_class_name, macro_edge_name,
macro_definition_ast, macro_edge_args):
"""Remove macro edge definition directive, and return a MacroEdgeDescriptor."""
if not is_vertex_field_name(macro_edge_name):
raise AssertionError(u'Received illegal macro edge name: {}'.format(macro_edge_name))

directives_to_remove = {MacroEdgeDefinitionDirective}
new_ast = remove_directives_from_ast(macro_definition_ast, directives_to_remove)
return MacroEdgeDescriptor(class_name, macro_edge_name, new_ast, macro_edge_args)
return MacroEdgeDescriptor(class_name, target_class_name, macro_edge_name,
new_ast, macro_edge_args)
239 changes: 239 additions & 0 deletions graphql_compiler/macros/macro_edge/expansion.py
@@ -0,0 +1,239 @@
# Copyright 2019-present Kensho Technologies, LLC.
from graphql.language.ast import Field, InlineFragment, SelectionSet
import six

from ...ast_manipulation import get_ast_field_name
from ...exceptions import GraphQLCompilationError
from ...schema import FilterDirective, is_vertex_field_name
from .ast_rewriting import find_target_and_copy_path_to_it, merge_selection_sets, replace_tag_names
from .ast_traversal import get_all_tag_names
from .directives import MacroEdgeTargetDirective
from .name_generation import generate_disambiguations


def _ensure_directives_on_macro_edge_are_supported(macro_edge_field):
"""Raise GraphQLCompilationError if an unsupported directive is used on the macro edge."""
macro_name = get_ast_field_name(macro_edge_field)
directives_supported_at_macro_expansion = frozenset({
FilterDirective.name,
})
for directive in macro_edge_field.directives:
directive_name = directive.name.value
if directive_name not in directives_supported_at_macro_expansion:
raise GraphQLCompilationError(
u'Encountered a {} directive applied to the {} macro edge, which is '
u'not currently supported by the macro system. Please alter your query to not use '
u'unsupported directives on macro edges. Supported directives are: {}'
.format(directive_name, macro_name,
set(directives_supported_at_macro_expansion)))


def _merge_selection_into_target(subclass_sets, target_ast, target_class_name, selection_ast):
"""Add the selections, directives, and coercions from the selection_ast to the target_ast.

Mutate the target_ast, merging into it everything from the selection_ast. If the target
is at a type coercion and the selection_ast starts with a type coercion, combine them
into one coercion that preserves the semantics while avoiding nested coercions,
which are disallowed.

For details on how fields and directives are merged, see merge_selection_sets().

Args:
subclass_sets: dict mapping class names to the set of names of their subclasses
target_ast: AST at the @macro_edge_target directive
target_class_name: str, the name of the GraphQL type to which the macro edge points
selection_ast: AST to merge inside the target. Required to have a nonempty selection set.
"""
if selection_ast.selection_set is None or not selection_ast.selection_set.selections:
raise AssertionError(u'Precondition violated. selection_ast is expected to be nonempty {}'
.format(selection_ast))

# Remove @macro_edge_target directive.
new_target_directives = [
directive
for directive in target_ast.directives
if directive.name.value != MacroEdgeTargetDirective.name
]
if len(target_ast.directives) != len(new_target_directives) + 1:
raise AssertionError(u'Expected the target_ast to contain a single @macro_edge_target '
u'directive, but that was unexpectedly not the case: '
u'{} {}'.format(target_ast, new_target_directives))
target_ast.directives = new_target_directives

# See if there's a type coercion in the selection_ast.
coercion = None
for selection in selection_ast.selection_set.selections:
if isinstance(selection, InlineFragment):
if len(selection_ast.selection_set.selections) != 1:
raise GraphQLCompilationError(u'Found selections outside type coercion. '
u'Please move them inside the coercion. '
u'Error near field name: {}'
.format(get_ast_field_name(selection_ast)))
else:
coercion = selection

continuation_ast = selection_ast

# Deal with type coercions immediately within the macro edge, if any.
if coercion is not None:
coercion_class = coercion.type_condition.name.value

# Ensure the coercion is valid. It may only go to a subtype of the type of the vertex field
# created by the macro edge, where we allow subtypes to be defined by subclass_sets
# to work around the limitations of the GraphQL type system. If the user's coercion
# is to a subtype of the macro edge target's type, then this is a narrowing conversion and
# we simply add the user's coercion, or replace any existing coercion if one is present.
if coercion_class != target_class_name:
if coercion_class not in subclass_sets.get(target_class_name, set()):
raise GraphQLCompilationError(
u'Attempting to use a type coercion to coerce a value of type {field_type} '
u'(from field named {field_name}) to incompatible type {coercion_type}, which '
u'is not a subtype of {field_type}. Only coercions to a subtype are allowed.'
.format(field_type=target_class_name,
coercion_type=coercion_class,
field_name=get_ast_field_name(selection_ast)))

continuation_ast = coercion
if isinstance(target_ast, InlineFragment):
# The macro edge definition has a type coercion as well, replace it with the user's one.
target_ast.type_condition = coercion.type_condition
else:
# No coercion in the macro edge definition,
# slip the user's type coercion inside the target AST.
new_coercion = InlineFragment(
coercion.type_condition, target_ast.selection_set, directives=[])
target_ast.selection_set = SelectionSet([new_coercion])
target_ast = new_coercion

# Merge the continuation into the target
target_ast.directives += continuation_ast.directives
target_ast.selection_set = merge_selection_sets(
target_ast.selection_set, continuation_ast.selection_set)


def _expand_specific_macro_edge(subclass_sets, target_class_name, macro_ast, selection_ast):
"""Produce a tuple containing the new replacement selection AST, and a list of extra selections.

Args:
subclass_sets: dict mapping class names to the set of names of its subclasses
target_class_name: str, the name of the GraphQL type to which the macro edge points
macro_ast: AST GraphQL object defining the macro edge. Originates from
the "expansion_ast" key from a MacroEdgeDescriptor, though potentially sanitized.
selection_ast: GraphQL AST object containing the selection that is relying on a macro edge.

Returns:
tuple of:
- replacement_selection_ast: GraphQL AST object to replace the given selection_ast
- sibling_prefix_selections: list of GraphQL AST objects describing the selections
to be added somewhere in the same scope but before the replacement_selection_ast.
- sibling_suffix_selections: list of GraphQL AST objects describing the selections
to be added somewhere in the same scope but after the replacement_selection_ast.
Since the replacemet_selection_ast is a vertex field, and vertex fields always
go after property fields, these selections are all vertex fields.
"""
replacement_selection_ast = None
sibling_prefix_selections = []
sibling_suffix_selections = []

# TODO(bojanserafimov): Remove macro tags if the user has tagged the same field.

for macro_selection in macro_ast.selection_set.selections:
new_ast, target_ast = find_target_and_copy_path_to_it(macro_selection)
if target_ast is None:
if replacement_selection_ast is None:
sibling_prefix_selections.append(macro_selection)
else:
sibling_suffix_selections.append(macro_selection)
else:
if replacement_selection_ast is not None:
raise AssertionError(u'Found multiple @macro_edge_target directives. This means '
u'the macro definition is invalid, and should never happen '
u'as it should have been caught during validation. Macro AST: '
u'{}'.format(macro_ast))
replacement_selection_ast = new_ast
_merge_selection_into_target(
subclass_sets, target_ast, target_class_name, selection_ast)

if replacement_selection_ast is None:
raise AssertionError(u'Found no @macro_edge_target directives in macro selection set. {}'
.format(macro_ast))

return replacement_selection_ast, sibling_prefix_selections, sibling_suffix_selections


def _merge_non_overlapping_dicts(merge_target, new_data):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

tech debt week: move to top-level utils

"""Produce the merged result of two dicts that are supposed to not overlap."""
result = dict(merge_target)

for key, value in six.iteritems(new_data):
if key in merge_target:
raise AssertionError(u'Overlapping key "{}" found in dicts that are supposed '
u'to not overlap. Values: {} {}'
.format(key, merge_target[key], value))

result[key] = value

return result


# ############
# Public API #
# ############

def expand_potential_macro_edge(macro_registry, current_schema_type, ast, query_args, tag_names):
"""Expand the macro edge at the provided field, if it refers to a macro edge.

Args:
macro_registry: MacroRegistry, the registry of macro descriptors used for expansion
current_schema_type: GraphQL type object describing the current type at the given AST node
ast: GraphQL AST object that potentially requires macro expansion
query_args: dict mapping strings to any type, containing the arguments for the query
tag_names: set of names of tags currently in use. The set is mutated in this function.

Returns:
tuple (new_ast, new_query_args, sibling_prefix_selections, sibling_suffix_selections)
It contains a potentially-rewritten GraphQL AST object and its matching args, as well as
any sibling selections (lists of fields existing in the same scope as the new_ast)
that should be added either before (prefix) or after (suffix) the appearance of new_ast
in its scope. If no changes were made (e.g. if the AST was not a macro edge), the new_ast
and new_query_args values are guaranteed to be the exact same objects as the input ones,
whereas the prefix and suffix sibling selections values are guaranteed to be empty lists.
"""
no_op_result = (ast, query_args, [], [])

macro_edges_at_this_type = macro_registry.macro_edges_at_class.get(
current_schema_type.name, dict())

# If the input AST isn't a Field, it can't be a macro edge. Nothing to be done.
if not isinstance(ast, Field):
return no_op_result

# If the field isn't a vertex field, it can't be a macro edge. Nothing to be done.
field_name = get_ast_field_name(ast)
if not is_vertex_field_name(field_name):
return no_op_result

# If the vertex field isn't a macro edge, there's nothing to be done.
macro_edge_descriptor = macro_edges_at_this_type.get(field_name, None)
if macro_edge_descriptor is None:
return no_op_result

# We're dealing with a macro edge. Ensure its use is legal.
_ensure_directives_on_macro_edge_are_supported(ast)

# Sanitize the macro, making sure it doesn't use any taken tag names.
macro_tag_names = get_all_tag_names(macro_edge_descriptor.expansion_ast)
name_change_map = generate_disambiguations(tag_names, macro_tag_names)
tag_names.update(name_change_map.values())
sanitized_macro_ast = replace_tag_names(
name_change_map, macro_edge_descriptor.expansion_ast)
tag_names.update(name_change_map.values())

new_ast, prefix_selections, suffix_selections = _expand_specific_macro_edge(
macro_registry.subclass_sets, macro_edge_descriptor.target_class_name,
sanitized_macro_ast, ast)
# TODO(predrag): Write a test that makes sure we've chosen names for filter arguments that
Copy link
Contributor

Choose a reason for hiding this comment

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

Hoping to address this during our tech debt pass later this month.

# do not overlap with user's filter arguments.
new_query_args = _merge_non_overlapping_dicts(query_args, macro_edge_descriptor.macro_args)

return (new_ast, new_query_args, prefix_selections, suffix_selections)
21 changes: 21 additions & 0 deletions graphql_compiler/macros/macro_edge/reversal.py
@@ -0,0 +1,21 @@
# Copyright 2019-present Kensho Technologies, LLC.
from ...schema import INBOUND_EDGE_FIELD_PREFIX, OUTBOUND_EDGE_FIELD_PREFIX


# ############
# Public API #
# ############

def make_reverse_macro_edge_name(macro_edge_name):
if macro_edge_name.startswith(INBOUND_EDGE_FIELD_PREFIX):
raw_edge_name = macro_edge_name[len(INBOUND_EDGE_FIELD_PREFIX):]
prefix = OUTBOUND_EDGE_FIELD_PREFIX
elif macro_edge_name.startswith(OUTBOUND_EDGE_FIELD_PREFIX):
raw_edge_name = macro_edge_name[len(OUTBOUND_EDGE_FIELD_PREFIX):]
prefix = INBOUND_EDGE_FIELD_PREFIX
else:
raise AssertionError(u'Unreachable condition reached: {}'.format(macro_edge_name))

reversed_macro_edge_name = prefix + raw_edge_name

return reversed_macro_edge_name