Last active
December 2, 2017 01:21
-
-
Save vmagamedov/41a8c9f60d8cb8109e2441846ef6a06d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from functools import wraps | |
import re | |
from hiku.graph import GraphTransformer, apply | |
from hiku.graph import Field, Link, Graph, Root, Node, Option | |
from hiku.types import Sequence, TypeRef | |
from hiku.query import Field as QueryField | |
from hiku.engine import Engine | |
from hiku.result import denormalize | |
from hiku.executors.sync import SyncExecutor | |
from hiku.readers.graphql import read | |
class CamelCaseTransformer(GraphTransformer): | |
def __init__(self): | |
self._wrappers = {} | |
self._inverted = {} | |
def encode(self, name): | |
encoded = re.sub('(?:_)(.)', lambda m: m.group(1).upper(), name) | |
self._inverted[encoded] = name | |
return encoded | |
def decorate_field_func(self, func): | |
wrapper = self._wrappers.get(func) | |
if wrapper is None: | |
@wraps(func) | |
def wrapper(fields, *args): | |
fields = [QueryField(self._inverted[f.name], | |
(None if f.options is None | |
else {self._inverted[k]: v | |
for k, v in f.options.items()})) | |
for f in fields] | |
return func(fields, *args) | |
wrapper = self._wrappers[func] = wrapper | |
return wrapper | |
def decorate_link_func(self, func): | |
@wraps(func) | |
def wrapper(*args): | |
args = list(args) | |
args[-1] = {self._inverted[k]: v for k, v in args[-1].items()} | |
return func(*args) | |
return wrapper | |
def visit_link(self, obj): | |
func = self.decorate_link_func(obj.func) if obj.options else obj.func | |
requires = self.encode(obj.requires) if obj.requires else None | |
return Link(self.encode(obj.name), obj.type, func, | |
requires=requires, | |
options=[self.visit(op) for op in obj.options], | |
description=obj.description) | |
def visit_field(self, obj): | |
return Field(self.encode(obj.name), obj.type, | |
self.decorate_field_func(obj.func), | |
options=[self.visit(op) for op in obj.options], | |
description=obj.description) | |
def visit_option(self, obj): | |
return Option(self.encode(obj.name), obj.type, default=obj.default, | |
description=obj.description) | |
def foo_fields_loader(fields, ids): | |
data = {1: {'foo_field': 'foo value 1'}, | |
2: {'foo_field': 'foo value 2'}} | |
return [[data[i][f.name] for f in fields] for i in ids] | |
def root_fields_loader(fields): | |
data = {'root_field': 'root value with {root_option}'} | |
return [data[f.name].format(**f.options) for f in fields] | |
GRAPH = Graph([ | |
Node('foo', [ | |
Field('foo_field', None, foo_fields_loader), | |
]), | |
Root([ | |
Field('root_field', None, root_fields_loader, | |
options=[Option('root_option', None)]), | |
Link('get_foo', Sequence[TypeRef['foo']], | |
lambda options: options['foo_option'], requires=None, | |
options=[Option('foo_option', None)]), | |
]) | |
]) | |
GRAPH = apply(GRAPH, [CamelCaseTransformer()]) | |
engine = Engine(SyncExecutor()) | |
query = read(""" | |
{ | |
rootField(rootOption: 123) | |
getFoo(fooOption: [2, 1]) { | |
fooField | |
} | |
} | |
""") | |
result = engine.execute(GRAPH, query) | |
plain_result = denormalize(GRAPH, result, query) | |
assert plain_result == { | |
'rootField': 'root value with 123', | |
'getFoo': [ | |
{'fooField': 'foo value 2'}, | |
{'fooField': 'foo value 1'}, | |
] | |
} | |
print(plain_result) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment