diff --git a/graphql/execution/base.py b/graphql/execution/base.py index 21d7378c..fb1723b8 100644 --- a/graphql/execution/base.py +++ b/graphql/execution/base.py @@ -298,10 +298,11 @@ def get_field_entry_key(node): class ResolveInfo(object): __slots__ = ('field_name', 'field_asts', 'return_type', 'parent_type', - 'schema', 'fragments', 'root_value', 'operation', 'variable_values', 'context', 'path') + 'schema', 'fragments', 'root_value', 'operation', 'variable_values', + 'context', 'path', 'document_path') def __init__(self, field_name, field_asts, return_type, parent_type, - schema, fragments, root_value, operation, variable_values, context, path): + schema, fragments, root_value, operation, variable_values, context, path, document_path): self.field_name = field_name self.field_asts = field_asts self.return_type = return_type @@ -313,6 +314,7 @@ def __init__(self, field_name, field_asts, return_type, parent_type, self.variable_values = variable_values self.context = context self.path = path + self.document_path = document_path def default_resolve_fn(source, info, **args): diff --git a/graphql/execution/executor.py b/graphql/execution/executor.py index 31428ee6..7136b9d5 100644 --- a/graphql/execution/executor.py +++ b/graphql/execution/executor.py @@ -200,6 +200,7 @@ def catch_error(error): def resolve_field(exe_context, parent_type, source, field_asts, parent_info): field_ast = field_asts[0] field_name = field_ast.name.value + field_document_name = field_ast.alias.value if field_ast.alias else field_name field_def = get_field_def(exe_context.schema, parent_type, field_name) if not field_def: @@ -233,7 +234,8 @@ def resolve_field(exe_context, parent_type, source, field_asts, parent_info): operation=exe_context.operation, variable_values=exe_context.variable_values, context=context, - path=parent_info.path+[field_name] if parent_info else [field_name] + path=parent_info.path + [field_name] if parent_info else [field_name], + document_path=parent_info.document_path + [field_document_name] if parent_info else [field_document_name], ) executor = exe_context.executor @@ -251,6 +253,7 @@ def resolve_field(exe_context, parent_type, source, field_asts, parent_info): def subscribe_field(exe_context, parent_type, source, field_asts): field_ast = field_asts[0] field_name = field_ast.name.value + field_document_name = field_ast.alias.value if field_ast.alias else field_name field_def = get_field_def(exe_context.schema, parent_type, field_name) if not field_def: @@ -284,7 +287,8 @@ def subscribe_field(exe_context, parent_type, source, field_asts): operation=exe_context.operation, variable_values=exe_context.variable_values, context=context, - path=[field_name] + path=[field_name], + document_path=[field_document_name] ) executor = exe_context.executor @@ -419,16 +423,26 @@ def complete_list_value(exe_context, return_type, field_asts, info, result): completed_results = [] contains_promise = False - index = 0 - path = info.path[:] - for item in result: - info.path = path + [index] - completed_item = complete_value_catching_error(exe_context, item_type, field_asts, info, item) + for index, item in enumerate(result): + item_info = ResolveInfo( + info.field_name, + info.field_asts, + info.return_type, + info.parent_type, + info.schema, + info.fragments, + info.root_value, + info.operation, + info.variable_values, + info.context, + info.path + [index], + info.document_path + [index] + ) + completed_item = complete_value_catching_error(exe_context, item_type, field_asts, item_info, item) if not contains_promise and is_thenable(completed_item): contains_promise = True completed_results.append(completed_item) - index += 1 return Promise.all(completed_results) if contains_promise else completed_results diff --git a/graphql/execution/tests/test_executor.py b/graphql/execution/tests/test_executor.py index cc107362..55ad62e8 100644 --- a/graphql/execution/tests/test_executor.py +++ b/graphql/execution/tests/test_executor.py @@ -745,9 +745,11 @@ def __init__(self, uid, width, height): class PathCollectorMiddleware(object): def __init__(self): self.paths = [] + self.document_paths = [] def resolve(self, _next, root, info, *args, **kwargs): self.paths.append(info.path) + self.document_paths.append(info.document_path) return _next(root, info, *args, **kwargs) request = ''' @@ -757,13 +759,13 @@ def resolve(self, _next, root, info, *args, **kwargs): ...articleFields author { id - name + fullName: name } }, } fragment articleFields on Article { title, - body, + content: body, hidden, } ''' @@ -778,19 +780,19 @@ def resolve(self, _next, root, info, *args, **kwargs): { "id": "1", "title": "My Article 1", - "body": "This is a post", + "content": "This is a post", "author": { "id": "123", - "name": "John Smith" + "fullName": "John Smith" } }, { "id": "2", "title": "My Article 2", - "body": "This is a post", + "content": "This is a post", "author": { "id": "123", - "name": "John Smith" + "fullName": "John Smith" } }, ], @@ -813,3 +815,19 @@ def resolve(self, _next, root, info, *args, **kwargs): ['feed', 1, 'author', 'name'] ] + traversed_document_paths = paths_middleware.document_paths + assert traversed_document_paths == [ + ['feed'], + ['feed', 0, 'id'], + ['feed', 0, 'title'], + ['feed', 0, 'content'], + ['feed', 0, 'author'], + ['feed', 1, 'id'], + ['feed', 1, 'title'], + ['feed', 1, 'content'], + ['feed', 1, 'author'], + ['feed', 0, 'author', 'id'], + ['feed', 0, 'author', 'fullName'], + ['feed', 1, 'author', 'id'], + ['feed', 1, 'author', 'fullName'] + ]