Skip to content

Commit 51f611a

Browse files
committed
Merge branch 'api'
2 parents 54cf197 + 8e5c727 commit 51f611a

File tree

8 files changed

+545
-2
lines changed

8 files changed

+545
-2
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ python:
44
- 2.7
55
install:
66
- pip install pytest flake8
7-
- pip install -e .
7+
- pip install -e .[django]
88
script:
99
- py.test
1010
- flake8

graphql/api.py

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
from graphql import graphql as graphql_main
2+
import graphql.type
3+
4+
__all__ = ['Schema']
5+
6+
7+
class PublicType(object):
8+
pass
9+
10+
11+
def build_typeref(refspec):
12+
if isinstance(refspec, TypeRef):
13+
# already a TypeRef
14+
return refspec
15+
16+
if isinstance(refspec, basestring):
17+
# type name reference: Field('SomeType')
18+
return NameTypeRef(refspec)
19+
try:
20+
if issubclass(refspec, PublicType):
21+
# public type reference: Field(SomeType)
22+
return PublicTypeRef(refspec)
23+
except TypeError:
24+
# ignore if refspec is not a class
25+
pass
26+
27+
# internal GraphQL type reference: Field(String)
28+
return InternalTypeRef(refspec)
29+
30+
31+
class TypeRef(object):
32+
def resolve(self, schema):
33+
raise NotImplemented
34+
35+
36+
class NameTypeRef(TypeRef):
37+
def __init__(self, name):
38+
self.name = name
39+
40+
def resolve(self, schema):
41+
return schema._internal_types[self.name]
42+
43+
44+
class WrappingTypeRef(TypeRef):
45+
def __init__(self, wrapper_type, inner_typeref):
46+
self.wrapper_type = wrapper_type
47+
self.inner_typeref = inner_typeref
48+
49+
def resolve(self, schema):
50+
return self.wrapper_type(self.inner_typeref.resolve(schema))
51+
52+
@classmethod
53+
def factory(cls, wrapper_type):
54+
def factory_func(inner_typerefspec):
55+
return cls(wrapper_type, build_typeref(inner_typerefspec))
56+
return factory_func
57+
58+
59+
class PublicTypeRef(TypeRef):
60+
def __init__(self, public_type):
61+
self.public_type = public_type
62+
63+
def resolve(self, schema):
64+
return schema._public_types[self.public_type]
65+
66+
67+
class InternalTypeRef(TypeRef):
68+
def __init__(self, internal_type):
69+
self.internal_type = internal_type
70+
71+
def resolve(self, schema):
72+
return self.internal_type
73+
74+
75+
class LazyField(object):
76+
def __init__(self, typerefspec, args=None, resolver=None,
77+
deprecation_reason=None, description=None):
78+
self.typeref = build_typeref(typerefspec)
79+
self.args = args
80+
self.resolver = resolver
81+
self.deprecation_reason = deprecation_reason
82+
self.description = description
83+
84+
def resolve(self, schema):
85+
args = {}
86+
if self.args:
87+
for arg_name, arg in self.args.items():
88+
args[arg_name] = arg.resolve(schema)
89+
return graphql.type.GraphQLField(
90+
self.typeref.resolve(schema),
91+
args, self.resolver, self.deprecation_reason, self.description
92+
)
93+
94+
def __call__(self, resolver):
95+
# Called when used as decorator
96+
self.resolver = resolver
97+
return self
98+
99+
100+
class LazyArgument(object):
101+
def __init__(self, typerefspec, default_value=None, description=None):
102+
self.typeref = build_typeref(typerefspec)
103+
self.default_value = default_value
104+
self.description = description
105+
106+
def resolve(self, schema):
107+
return graphql.type.GraphQLArgument(
108+
self.typeref.resolve(schema),
109+
self.default_value, self.description
110+
)
111+
112+
113+
class Schema(object):
114+
String = InternalTypeRef(graphql.type.GraphQLString)
115+
Int = InternalTypeRef(graphql.type.GraphQLInt)
116+
Float = InternalTypeRef(graphql.type.GraphQLFloat)
117+
Boolean = InternalTypeRef(graphql.type.GraphQLBoolean)
118+
ID = InternalTypeRef(graphql.type.GraphQLID)
119+
120+
Field = LazyField
121+
Argument = LazyArgument
122+
EnumValue = graphql.type.GraphQLEnumValue
123+
124+
def __init__(self):
125+
self._internal_types = {}
126+
self._public_types = {}
127+
self._query_root = None
128+
self._mutation_root = None
129+
130+
# Define in the constructor to make functions unbound
131+
self.NonNull = WrappingTypeRef.factory(graphql.type.GraphQLNonNull)
132+
self.List = WrappingTypeRef.factory(graphql.type.GraphQLList)
133+
134+
self.EnumType = self._build_type_definer(self._define_enum)
135+
self.InterfaceType = self._build_type_definer(self._define_interface)
136+
self.UnionType = self._build_type_definer(self._define_union)
137+
self.ObjectType = self._build_type_definer(self._define_object)
138+
self.QueryRoot = self._build_type_definer(self._define_query_root)
139+
self.MutationRoot = self._build_type_definer(self._define_mutation_root)
140+
141+
def _define_enum(self, dct):
142+
values = {}
143+
for k, v in dct.items():
144+
if isinstance(v, self.EnumValue):
145+
values[k] = v
146+
return graphql.type.GraphQLEnumType(
147+
name=dct['__typename__'],
148+
values=values,
149+
description=dct.get('__doc__'),
150+
)
151+
152+
def _define_interface(self, dct):
153+
fields = {}
154+
for k, v in dct.items():
155+
if isinstance(v, self.Field):
156+
fields[k] = v
157+
return graphql.type.GraphQLInterfaceType(
158+
name=dct['__typename__'],
159+
fields=lambda: self._resolve_fields(fields),
160+
description=dct.get('__doc__'),
161+
)
162+
163+
def _define_union(self, dct):
164+
types = [self._public_types[public_type] for public_type in dct['types']]
165+
return graphql.type.GraphQLUnionType(
166+
name=dct['__typename__'],
167+
types=types,
168+
resolve_type=dct.get('resolve_type'),
169+
description=dct.get('__doc__'),
170+
)
171+
172+
def _define_object(self, dct):
173+
fields = {}
174+
for k, v in dct.items():
175+
if isinstance(v, self.Field):
176+
fields[k] = v
177+
interfaces = dct.get('__interfaces__')
178+
if interfaces:
179+
interfaces = [self._public_types[public_type] for public_type in interfaces]
180+
return graphql.type.GraphQLObjectType(
181+
name=dct['__typename__'],
182+
fields=lambda: self._resolve_fields(fields),
183+
interfaces=interfaces,
184+
description=dct.get('__doc__'),
185+
)
186+
187+
def _define_query_root(self, dct):
188+
assert not self._query_root
189+
internal_type = self._define_object(dct)
190+
self._query_root = internal_type
191+
return internal_type
192+
193+
def _define_mutation_root(self, dct):
194+
assert not self._mutation_root
195+
internal_type = self._define_object(dct)
196+
self._mutation_root = internal_type
197+
return internal_type
198+
199+
def _resolve_fields(self, fields):
200+
resolved = {}
201+
for k, v in fields.items():
202+
resolved[k] = v.resolve(self)
203+
return resolved
204+
205+
def _build_type_definer(self, internal_type_builder):
206+
class TypeDefinerMeta(type):
207+
def __init__(cls, name, bases, dct):
208+
self._define_type(cls, name, bases, dct, internal_type_builder)
209+
type.__init__(cls, name, bases, dct)
210+
211+
class TypeDefiner(PublicType):
212+
__metaclass__ = TypeDefinerMeta
213+
214+
return TypeDefiner
215+
216+
def _define_type(self, cls, name, bases, dct, internal_type_builder):
217+
if not bases or bases[-1] is PublicType:
218+
return
219+
if '__typename__' not in dct:
220+
dct['__typename__'] = name
221+
assert dct['__typename__'] not in self._internal_types
222+
internal_type = internal_type_builder(dct)
223+
self._internal_types[dct['__typename__']] = internal_type
224+
self._public_types[cls] = internal_type
225+
226+
def to_internal(self):
227+
return graphql.type.GraphQLSchema(self._query_root)
228+
229+
def execute(self, query, root=None, vars=None, operation_name=None):
230+
return graphql_main(self.to_internal(), query, root, vars, operation_name)

graphql/contrib/__init__.py

Whitespace-only changes.

graphql/contrib/django/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from graphql.api import Schema
2+
from django.db import models
3+
4+
5+
class DjangoSchema(Schema):
6+
def __init__(self):
7+
super(DjangoSchema, self).__init__()
8+
9+
def _define_object(self, dct):
10+
# Override
11+
model = dct.get('__model__')
12+
if model:
13+
for field in model._meta.get_fields():
14+
if field.is_relation:
15+
pass # TODO
16+
else:
17+
if isinstance(field, models.CharField):
18+
dct[field.name] = self.Field(self.String)
19+
# ... TODO
20+
return super(DjangoSchema, self)._define_object(dct)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def run_tests(self):
4949
install_requires=[],
5050
tests_require=['pytest>=2.7.2'],
5151
extras_require={
52-
'django': [],
52+
'django': ['Django>=1.8.0,<1.9'],
5353
},
5454

5555
cmdclass={'test': PyTest},

tests/contrib_django/test_django.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import django
2+
from django.conf import settings
3+
from graphql.contrib.django import DjangoSchema
4+
5+
settings.configure(
6+
DATABASES={
7+
'INSTALLED_APPS': [
8+
'graphql',
9+
],
10+
'default': {
11+
'ENGINE': 'django.db.backends.sqlite3',
12+
}
13+
}
14+
)
15+
16+
from django.db import models
17+
18+
class Human(models.Model):
19+
name = models.CharField()
20+
21+
class Meta:
22+
app_label = 'graphql'
23+
24+
django.setup()
25+
26+
27+
def test_auto_definition():
28+
gql = DjangoSchema()
29+
30+
class HumanType(gql.ObjectType):
31+
__typename__ = 'Human'
32+
__model__ = Human
33+
34+
class QueryRoot(gql.QueryRoot):
35+
@gql.Field(gql.List(gql.NonNull(HumanType)))
36+
def humans(self, *args, **kwargs):
37+
return [Human(name='hi')]
38+
39+
result = gql.execute('''{
40+
humans { name }
41+
}''')
42+
assert not result.errors
43+
assert result.data == {
44+
'humans': [
45+
{'name': 'hi'}
46+
]
47+
}

0 commit comments

Comments
 (0)