Skip to content

Commit 04b63e6

Browse files
committed
Improved query builder
1 parent 14e4ee6 commit 04b63e6

File tree

8 files changed

+560
-300
lines changed

8 files changed

+560
-300
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import collections
2+
import functools
3+
import logging
4+
import sys
5+
6+
from promise import Promise, promise_for_dict, promisify
7+
8+
from ...error import GraphQLError, GraphQLLocatedError
9+
from ...pyutils.default_ordered_dict import DefaultOrderedDict
10+
from ...pyutils.ordereddict import OrderedDict
11+
from ...type import (GraphQLEnumType, GraphQLInterfaceType, GraphQLList,
12+
GraphQLNonNull, GraphQLObjectType, GraphQLScalarType,
13+
GraphQLSchema, GraphQLUnionType)
14+
from ..base import (ExecutionContext, ExecutionResult, ResolveInfo, Undefined,
15+
collect_fields, default_resolve_fn, get_field_def,
16+
get_operation_root_type)
17+
from ..executors.sync import SyncExecutor
18+
from ..middleware import MiddlewareManager
19+
20+
from .resolver import type_resolver
21+
from .fragment import Fragment
22+
23+
logger = logging.getLogger(__name__)
24+
25+
26+
def is_promise(obj):
27+
return type(obj) == Promise
28+
29+
30+
def execute(schema, document_ast, root_value=None, context_value=None,
31+
variable_values=None, operation_name=None, executor=None,
32+
return_promise=False, middlewares=None):
33+
assert schema, 'Must provide schema'
34+
assert isinstance(schema, GraphQLSchema), (
35+
'Schema must be an instance of GraphQLSchema. Also ensure that there are ' +
36+
'not multiple versions of GraphQL installed in your node_modules directory.'
37+
)
38+
if middlewares:
39+
assert isinstance(middlewares, MiddlewareManager), (
40+
'middlewares have to be an instance'
41+
' of MiddlewareManager. Received "{}".'.format(middlewares)
42+
)
43+
44+
if executor is None:
45+
executor = SyncExecutor()
46+
47+
context = ExecutionContext(
48+
schema,
49+
document_ast,
50+
root_value,
51+
context_value,
52+
variable_values,
53+
operation_name,
54+
executor,
55+
middlewares
56+
)
57+
58+
def executor(resolve, reject):
59+
return resolve(execute_operation(context, context.operation, root_value))
60+
61+
def on_rejected(error):
62+
context.errors.append(error)
63+
return None
64+
65+
def on_resolve(data):
66+
return ExecutionResult(data=data, errors=context.errors)
67+
68+
promise = Promise(executor).catch(on_rejected).then(on_resolve)
69+
if return_promise:
70+
return promise
71+
context.executor.wait_until_finished()
72+
return promise.get()
73+
74+
75+
def execute_operation(exe_context, operation, root_value):
76+
type = get_operation_root_type(exe_context.schema, operation)
77+
execute_serially = operation.operation == 'mutation'
78+
79+
fragment = Fragment(type=type, selection_set=operation.selection_set, context=exe_context, execute_serially=execute_serially)
80+
resolver = type_resolver(type, lambda: root_value, fragment=fragment)
81+
resolved = resolver()
82+
return resolved
Lines changed: 104 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,73 @@
11
from functools import partial
22
from ...pyutils.cached_property import cached_property
33
from ..values import get_argument_values, get_variable_values
4+
from ..base import collect_fields
5+
from ...pyutils.default_ordered_dict import DefaultOrderedDict
46

57

8+
from ...type import GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType
9+
10+
11+
def get_base_type(type):
12+
if isinstance(type, (GraphQLList, GraphQLNonNull)):
13+
return get_base_type(type.of_type)
14+
return type
15+
16+
17+
def get_resolvers(context, type, selection_set):
18+
from .resolver import field_resolver
19+
subfield_asts = DefaultOrderedDict(list)
20+
visited_fragment_names = set()
21+
if selection_set:
22+
subfield_asts = collect_fields(
23+
context, type, selection_set,
24+
subfield_asts, visited_fragment_names
25+
)
26+
27+
resolvers = []
28+
for response_name, field_asts in subfield_asts.items():
29+
field_ast = field_asts[0]
30+
field_name = field_ast.name.value
31+
field_def = type.fields[field_name]
32+
field_base_type = get_base_type(field_def.type)
33+
field_fragment = None
34+
if isinstance(field_base_type, GraphQLObjectType):
35+
field_fragment = Fragment(
36+
type=field_base_type,
37+
selection_set=field_ast.selection_set,
38+
context=context
39+
)
40+
elif isinstance(field_base_type, (GraphQLInterfaceType, GraphQLUnionType)):
41+
field_fragment = AbstractFragment(
42+
abstract_type=field_base_type,
43+
selection_set=field_ast.selection_set,
44+
context=context
45+
)
46+
resolver = field_resolver(field_def, fragment=field_fragment)
47+
args = get_argument_values(
48+
field_def.args,
49+
field_ast.arguments,
50+
context and context.variable_values
51+
)
52+
resolver = partial(resolver, args=args)
53+
resolvers.append((response_name, resolver))
54+
return resolvers
55+
656

757
class Fragment(object):
8-
def __init__(self, type, field_asts, field_fragments=None, execute_serially=False):
58+
def __init__(self, type, selection_set, context=None, execute_serially=False):
959
self.type = type
10-
self.field_asts = field_asts
11-
self.field_fragments = field_fragments or {}
12-
self.variable_values = {}
60+
self.selection_set = selection_set
1361
self.execute_serially = execute_serially
62+
self.context = context
1463

1564
@cached_property
1665
def partial_resolvers(self):
17-
from .resolver import field_resolver
18-
resolvers = []
19-
for field_ast in self.field_asts:
20-
field_name = field_ast.name.value
21-
field_def = self.type.fields[field_name]
22-
field_fragment = self.field_fragments.get(field_name)
23-
resolver = field_resolver(field_def, fragment=field_fragment)
24-
args = get_argument_values(
25-
field_def.args,
26-
field_ast.arguments,
27-
self.variable_values
28-
)
29-
resolver = partial(resolver, args=args)
30-
resolvers.append((field_name, resolver))
31-
return resolvers
66+
return get_resolvers(
67+
self.context,
68+
self.type,
69+
self.selection_set
70+
)
3271

3372
def resolver(self, resolver, *args, **kwargs):
3473
root = resolver(*args, **kwargs)
@@ -40,7 +79,51 @@ def resolver(self, resolver, *args, **kwargs):
4079
def __eq__(self, other):
4180
return isinstance(other, Fragment) and (
4281
other.type == self.type and
43-
other.field_asts == self.field_asts and
44-
other.field_fragments == self.field_fragments and
82+
other.selection_set == self.selection_set and
83+
other.context == self.context and
4584
other.execute_serially == self.execute_serially
4685
)
86+
87+
88+
class AbstractFragment(object):
89+
def __init__(self, abstract_type, selection_set, context=None, execute_serially=False):
90+
self.abstract_type = abstract_type
91+
self.selection_set = selection_set
92+
self.context = context
93+
# self.execute_serially = execute_serially # Technically impossible
94+
self._type_resolvers = {}
95+
96+
@cached_property
97+
def possible_types(self):
98+
return self.context.schema.get_possible_types(self.abstract_type)
99+
100+
def get_type_resolvers(self, type):
101+
if type not in self._type_resolvers:
102+
assert type in self.possible_types
103+
self._type_resolvers[type] = get_resolvers(
104+
self.context,
105+
type,
106+
self.selection_set
107+
)
108+
return self._type_resolvers[type]
109+
110+
def resolve_type(self, result):
111+
return_type = self.abstract_type
112+
context = self.context.context_value
113+
info = None
114+
115+
if return_type.resolve_type:
116+
runtime_type = return_type.resolve_type(result, context, info)
117+
else:
118+
for type in self.possible_types:
119+
if callable(type.is_type_of) and type.is_type_of(result, context, info):
120+
return type
121+
122+
def resolver(self, resolver, *args, **kwargs):
123+
root = resolver(*args, **kwargs)
124+
_type = self.resolve_type(root)
125+
126+
return {
127+
field_name: field_resolver(root)
128+
for field_name, field_resolver in self.get_type_resolvers(_type)
129+
}
Lines changed: 69 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,91 @@
1-
# -*- coding: utf-8 -*-
2-
from ...error import GraphQLError
3-
from ...language import ast
4-
from ...pyutils.default_ordered_dict import DefaultOrderedDict
5-
from ...type.definition import GraphQLInterfaceType, GraphQLUnionType
6-
from ...type.directives import GraphQLIncludeDirective, GraphQLSkipDirective
7-
from ...type.introspection import (SchemaMetaFieldDef, TypeMetaFieldDef,
8-
TypeNameMetaFieldDef)
9-
from ...utils.type_from_ast import type_from_ast
10-
from ..values import get_argument_values, get_variable_values
1+
# # -*- coding: utf-8 -*-
2+
# from ...error import GraphQLError
3+
# from ...language import ast
4+
# from ...pyutils.default_ordered_dict import DefaultOrderedDict
5+
# from ...type.definition import GraphQLInterfaceType, GraphQLUnionType
6+
# from ...type.directives import GraphQLIncludeDirective, GraphQLSkipDirective
7+
# from ...type.introspection import (SchemaMetaFieldDef, TypeMetaFieldDef,
8+
# TypeNameMetaFieldDef)
9+
# from ...utils.type_from_ast import type_from_ast
10+
# from ..values import get_argument_values, get_variable_values
1111

12-
from ...type import (GraphQLEnumType, GraphQLInterfaceType, GraphQLList,
13-
GraphQLNonNull, GraphQLObjectType, GraphQLScalarType,
14-
GraphQLSchema, GraphQLUnionType)
15-
from ..base import (ExecutionContext, ExecutionResult, ResolveInfo, Undefined,
16-
collect_fields, default_resolve_fn, get_field_def,
17-
get_operation_root_type)
12+
# from ...type import (GraphQLEnumType, GraphQLInterfaceType, GraphQLList,
13+
# GraphQLNonNull, GraphQLObjectType, GraphQLScalarType,
14+
# GraphQLSchema, GraphQLUnionType)
15+
# from ..base import (ExecutionContext, ExecutionResult, ResolveInfo, Undefined,
16+
# collect_fields, default_resolve_fn, get_field_def,
17+
# get_operation_root_type)
1818

19-
from .fragment import Fragment
19+
# from .fragment import Fragment
2020

2121

22-
def get_base_type(type):
23-
if isinstance(type, (GraphQLList, GraphQLNonNull)):
24-
return get_base_type(type.of_type)
25-
return type
22+
# def get_base_type(type):
23+
# if isinstance(type, (GraphQLList, GraphQLNonNull)):
24+
# return get_base_type(type.of_type)
25+
# return type
2626

2727

28-
class QueryBuilder(object):
28+
# class QueryBuilder(object):
2929

30-
__slots__ = 'schema', 'operations', 'fragments'
30+
# __slots__ = 'schema', 'operations', 'fragments'
3131

32-
def __init__(self, schema, document_ast):
33-
operations = {}
34-
fragments = {}
32+
# def __init__(self, schema, document_ast):
33+
# operations = {}
34+
# fragments = {}
3535

36-
for definition in document_ast.definitions:
37-
if isinstance(definition, ast.OperationDefinition):
38-
operation_name = definition.name.value
39-
operations[operation_name] = definition
36+
# for definition in document_ast.definitions:
37+
# if isinstance(definition, ast.OperationDefinition):
38+
# operation_name = definition.name.value
39+
# operations[operation_name] = definition
4040

41-
elif isinstance(definition, ast.FragmentDefinition):
42-
fragment_name = definition.name.value
43-
fragments[fragment_name] = definition
41+
# elif isinstance(definition, ast.FragmentDefinition):
42+
# fragment_name = definition.name.value
43+
# fragments[fragment_name] = definition
4444

45-
else:
46-
raise GraphQLError(
47-
u'GraphQL cannot execute a request containing a {}.'.format(definition.__class__.__name__),
48-
definition
49-
)
45+
# else:
46+
# raise GraphQLError(
47+
# u'GraphQL cannot execute a request containing a {}.'.format(definition.__class__.__name__),
48+
# definition
49+
# )
5050

51-
if not operations:
52-
raise GraphQLError('Must provide an operation.')
51+
# if not operations:
52+
# raise GraphQLError('Must provide an operation.')
5353

54-
self.fragments = fragments
55-
self.schema = schema
56-
self.operations = {}
54+
# self.fragments = fragments
55+
# self.schema = schema
56+
# self.operations = {}
5757

58-
for operation_name, operation in operations.items():
59-
self.operations[operation_name] = fragment_operation(self.schema, operation)
58+
# for operation_name, operation in operations.items():
59+
# self.operations[operation_name] = fragment_operation(self.schema, operation)
6060

61-
def get_operation_fragment(self, operation_name):
62-
return self.operations[operation_name]
61+
# def get_operation_fragment(self, operation_name):
62+
# return self.operations[operation_name]
6363

6464

65-
def fragment_operation(schema, operation):
66-
field_asts = operation.selection_set.selections
67-
root_type = get_operation_root_type(schema, operation)
68-
execute_serially = operation.operation == 'mutation'
69-
return generate_fragment(root_type, field_asts, execute_serially)
65+
# def fragment_operation(schema, operation):
66+
# field_asts = operation.selection_set.selections
67+
# root_type = get_operation_root_type(schema, operation)
68+
# execute_serially = operation.operation == 'mutation'
69+
# return generate_fragment(root_type, field_asts, execute_serially)
7070

7171

72-
def generate_fragment(type, field_asts, execute_serially=False):
73-
field_fragments = {}
74-
for field_ast in field_asts:
75-
field_name = field_ast.name.value
76-
field_def = type.fields[field_name]
77-
field_base_type = get_base_type(field_def.type)
78-
if not isinstance(field_base_type, GraphQLObjectType):
79-
continue
80-
field_fragments[field_name] = generate_fragment(field_base_type, field_ast.selection_set.selections)
72+
# def generate_fragment(type, field_asts, execute_serially=False):
73+
# field_fragments = {}
74+
# for field_ast in field_asts:
75+
# field_name = field_ast.name.value
76+
# field_def = type.fields[field_name]
77+
# field_base_type = get_base_type(field_def.type)
78+
# if not isinstance(field_base_type, GraphQLObjectType):
79+
# continue
80+
# field_fragments[field_name] = generate_fragment(field_base_type, field_ast.selection_set.selections)
8181

82-
return Fragment(
83-
type,
84-
field_asts,
85-
field_fragments,
86-
execute_serially=execute_serially
87-
)
82+
# return Fragment(
83+
# type,
84+
# field_asts,
85+
# field_fragments,
86+
# execute_serially=execute_serially
87+
# )
8888

8989

90-
def build_query(schema, document_ast):
91-
static_context = QueryBuilder(schema, document_ast)
90+
# def build_query(schema, document_ast):
91+
# static_context = QueryBuilder(schema, document_ast)

0 commit comments

Comments
 (0)