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 all 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
251 changes: 251 additions & 0 deletions graphql_compiler/macros/__init__.py
@@ -1 +1,252 @@
# Copyright 2019-present Kensho Technologies, LLC.
from collections import namedtuple
from copy import copy

from graphql import parse
from graphql.language.ast import (
Directive, FieldDefinition, InterfaceTypeDefinition, ListType, Name, NamedType,
ObjectTypeDefinition
)
from graphql.language.printer import print_ast
from graphql.utils.build_ast_schema import build_ast_schema
from graphql.utils.schema_printer import print_schema
import six

from ..ast_manipulation import safe_parse_graphql
from ..compiler.subclass import compute_subclass_sets
from ..compiler.validation import validate_schema_and_query_ast
from ..exceptions import GraphQLValidationError
from ..schema import check_for_nondefault_directive_names
from .macro_edge import make_macro_edge_descriptor
from .macro_edge.directives import (
DIRECTIVES_ALLOWED_IN_MACRO_EDGE_DEFINITION, DIRECTIVES_REQUIRED_IN_MACRO_EDGE_DEFINITION,
MacroEdgeDirective
)
from .macro_expansion import expand_macros_in_query_ast
from .validation import (
check_macro_edge_for_definition_conflicts, check_macro_edge_for_reversal_definition_conflicts
)


MacroRegistry = namedtuple(
'MacroRegistry', (
# GraphQLSchema, created using the GraphQL library
'schema_without_macros',

# Optional dict of GraphQL interface or type -> GraphQL union.
# Used as a workaround for GraphQL's lack of support for
# inheritance across "types" (i.e. non-interfaces), as well as a
# workaround for Gremlin's total lack of inheritance-awareness.
# The key-value pairs in the dict specify that the "key" type
# is equivalent to the "value" type, i.e. that the GraphQL type or
# interface in the key is the most-derived common supertype
# of every GraphQL type in the "value" GraphQL union.
# Recursive expansion of type equivalence hints is not performed,
# and only type-level correctness of this argument is enforced.
# See README.md for more details on everything this parameter does.
# *****
# Be very careful with this option, as bad input here will
# lead to incorrect output queries being generated.
# *****
'type_equivalence_hints',

# Dict[str, Set[str]] mapping class names to the set of its subclass names.
# A class in this context means the name of a GraphQLObjectType,
# GraphQLUnionType or GraphQLInterface.
'subclass_sets',

# #################
# Macro edge info #
# #################
# List[MacroEdgeDescriptor] containing all defined macro edges
'macro_edges',

# Dict[str, Dict[str, MacroEdgeDescriptor]] mapping:
# class name -> (macro edge name -> MacroEdgeDescriptor)
# If a given macro edge is defined on a class X which has subclasses A and B,
# then this dict will contain entries for that macro edge for all of [X, A, B].
'macro_edges_at_class',

# Dict[str, Dict[str, MacroEdgeDescriptor]] mapping:
# class name -> (macro edge name -> MacroEdgeDescriptor)
# If a given macro edge has class X as a target, which has subclasses A and B,
# then this dict will contain entries for that macro edge for all of [X, A, B].
'macro_edges_to_class',

# ########################################################################
# Any other macro types we may add in the future belong under this line. #
# ########################################################################
)
)


def create_macro_registry(schema, type_equivalence_hints=None, subclass_sets=None):
"""Create and return a new empty macro registry."""
if subclass_sets is None:
subclass_sets = compute_subclass_sets(schema, type_equivalence_hints=type_equivalence_hints)

return MacroRegistry(
schema_without_macros=schema,
type_equivalence_hints=type_equivalence_hints,
subclass_sets=subclass_sets,
macro_edges=list(),
macro_edges_at_class=dict(),
macro_edges_to_class=dict())


def register_macro_edge(macro_registry, macro_edge_graphql, macro_edge_args):
"""Add the new macro edge descriptor to the provided MacroRegistry object, mutating it.

In order to register a new macro edge, the following properties must be true:
Copy link
Contributor

Choose a reason for hiding this comment

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

I added the explicit set of macro edge validity rules here. Let me know if I missed any.

- The macro edge, with the addition of any output value, must become a valid query. This ensures
that it is compliant with the schema, supplies values for all runtime and tagged parameters,
and obeys all other rules imposed by the compiler.
- The macro edge must not contain any directives that are prohibited in macro edge definitions.
- The macro edge will become a new vertex field on its base type, and therefore must be named
a vertex field name (prefixed with "out_" or "in_").
- Any class together with its subclasses may have defined at most one macro edge with that name.
- Any class together with its subclasses must be the target of at most one macro edge with
that name.
- For any macro edge named out_X (similarly, in_Y) defined at type A and with target type B,
the reversed macro edge in_X (similarly, out_Y) defined at type B and with target type A
either already exists, or could be defined without violating any of the above rules.

Args:
macro_registry: MacroRegistry object containing macro descriptors, where the new
macro edge descriptor should be added.
macro_edge_graphql: string, GraphQL defining how the new macro edge should be expanded
macro_edge_args: dict mapping strings to any type, containing any arguments the macro edge
requires in order to function.
"""
# The below function will validate that the macro edge in question is valid in isolation,
# when considered only against the macro-less schema. After geting this result,
# we simply need to check the macro edge descriptor against other artifacts in the macro system
# that might also cause conflicts.
macro_descriptor = make_macro_edge_descriptor(
macro_registry.schema_without_macros, macro_registry.subclass_sets,
macro_edge_graphql, macro_edge_args,
type_equivalence_hints=macro_registry.type_equivalence_hints)

# Ensure there's no conflict with macro edges defined on subclasses and superclasses.
check_macro_edge_for_definition_conflicts(macro_registry, macro_descriptor)

# Ensure there's no conflict between existing macro edges and the (hypothetical) reversed
# macro edge of the one being defined.
check_macro_edge_for_reversal_definition_conflicts(macro_registry, macro_descriptor)

for subclass_name in macro_registry.subclass_sets[macro_descriptor.base_class_name]:
macro_registry.macro_edges_at_class.setdefault(
subclass_name, dict())[macro_descriptor.macro_edge_name] = macro_descriptor

for subclass_name in macro_registry.subclass_sets[macro_descriptor.target_class_name]:
macro_registry.macro_edges_to_class.setdefault(
subclass_name, dict())[macro_descriptor.macro_edge_name] = macro_descriptor

macro_registry.macro_edges.append(macro_descriptor)


def get_schema_with_macros(macro_registry):
"""Get a new GraphQLSchema with fields where macro edges can be used.

Preconditions:
1. No macro in the registry has the same name as a field on the vertex where it applies.
2. Members of a union type do not have outgoing macros with the same name.

An easy way to satisfy the preconditions is to create the macro_registry using
create_macro_registry, and only update it with register_macro_edge, which does all
the necessary validation.

Postconditions:
1. Every GraphQLQuery that uses macros from this registry appropriately should
successfully type-check against the schema generated from this function.
2. A GraphQLQuery that uses macros not present in the registry, or uses valid
macros but on types they are not defined at should fail schema validation with
the schema generated from this function.
3. This function is total -- A valid macro registry should not fail to create a
GraphQL schema with macros.

Args:
macro_registry: MacroRegistry object containing a schema and macro descriptors
we want to add to the schema.

Returns:
GraphQLSchema with additional fields where macroe edges can be used.
"""
# The easiest way to manipulate the schema is through its AST. The easiest
# way to get an AST is to print it and parse it.
schema_ast = parse(print_schema(macro_registry.schema_without_macros))

definitions_by_name = {}
for definition in schema_ast.definitions:
if isinstance(definition, (ObjectTypeDefinition, InterfaceTypeDefinition)):
definitions_by_name[definition.name.value] = definition

for class_name, macros_for_class in six.iteritems(macro_registry.macro_edges_at_class):
for macro_edge_name, macro_edge_descriptor in six.iteritems(macros_for_class):
list_type_at_target = ListType(NamedType(Name(macro_edge_descriptor.target_class_name)))
arguments = []
directives = [Directive(Name(MacroEdgeDirective.name))]
definitions_by_name[class_name].fields.append(FieldDefinition(
Name(macro_edge_name), arguments, list_type_at_target, directives=directives))

return build_ast_schema(schema_ast)


def get_schema_for_macro_definition(schema):
"""Return a schema with macro definition directives added in.

Preconditions:
1. All compiler-supported and GraphQL-default directives have their default behavior.

This returned schema can be used to validate macro definitions, and support GraphQL
macro editors, enabling them to autocomplete on the @macro_edge_definition and
@macro_edge_target directives. Some directives that are disallowed in macro edge definitions,
like @output and @output_source, will be removed from the directives list.

Args:
schema: GraphQLSchema over which we want to write macros

Returns:
GraphQLSchema usable for writing macros. Modifying this schema is undefined behavior.

Raises:
AssertionError, if the schema contains directive names that are non-default.
"""
macro_definition_schema = copy(schema)
macro_definition_schema_directives = schema.get_directives()
check_for_nondefault_directive_names(macro_definition_schema_directives)
macro_definition_schema_directives += DIRECTIVES_REQUIRED_IN_MACRO_EDGE_DEFINITION
# Remove disallowed directives from directives list
macro_definition_schema_directives = list(set(macro_definition_schema_directives) &
set(DIRECTIVES_ALLOWED_IN_MACRO_EDGE_DEFINITION))

# pylint: disable=protected-access
macro_definition_schema._directives = macro_definition_schema_directives
# pylint: enable=protected-access
return macro_definition_schema


def perform_macro_expansion(macro_registry, graphql_with_macro, graphql_args):
"""Return a new GraphQL query string and args, after expanding any encountered macros.

Args:
macro_registry: MacroRegistry, the registry of macro descriptors used for expansion
graphql_with_macro: string, GraphQL query that potentially requires macro expansion
graphql_args: dict mapping strings to any type, containing the arguments for the query

Returns:
tuple (new_graphql_string, new_graphql_args) containing the rewritten GraphQL query and
its new args, after macro expansion. If the input GraphQL query contained no macros,
the returned values are guaranteed to be identical to the input query and args.
"""
query_ast = safe_parse_graphql(graphql_with_macro)
schema_with_macros = get_schema_with_macros(macro_registry)
validation_errors = validate_schema_and_query_ast(schema_with_macros, query_ast)
if validation_errors:
raise GraphQLValidationError(u'The provided GraphQL input does not validate: {} {}'
.format(graphql_with_macro, validation_errors))

new_query_ast, new_args = expand_macros_in_query_ast(macro_registry, query_ast, graphql_args)
new_graphql_string = print_ast(new_query_ast)

return new_graphql_string, new_args
59 changes: 14 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 Expand Up @@ -374,6 +329,20 @@ def merge_selection_sets(selection_set_a, selection_set_b):
u'same edge {} twice, which is disallowed.'
.format(field_name))

# TODO(predrag): Find a way to avoid this situation by making the rewriting smarter.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is caught by tests marked xfail. That annotation means that the tests are expected to fail (with a specific error), and if they fail differently or not at all, that should be treated as a test error and reported. When we address this TODO, we can remove the xfail annotation on those tests and everything should work fine.

field_a_has_tag_directive = any((
directive.name.value == TagDirective.name
for directive in field_a.directives
))
field_b_has_tag_directive = any((
directive.name.value == TagDirective.name
for directive in field_b.directives
))
if field_a_has_tag_directive and field_b_has_tag_directive:
raise GraphQLCompilationError(u'Macro edge expansion results in field {} having two '
u'@tag directives, which is disallowed.'
.format(field_name))

merged_field = copy(field_a)
merged_field.directives = list(chain(field_a.directives, field_b.directives))
common_selection_dict[field_name] = merged_field
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)