def test_record_option_type_errors(): check_option_errors( [Option('lawing', Record[{'tingent': Integer}])], {'lawing': {'tingent': 123}}, [], ) check_option_errors( [Option('lawing', Record[{'tingent': Integer}])], {'lawing': Invalid()}, ['Invalid value for option "{field}:lawing", ' '"Invalid" instead of Record[{{\'tingent\': Integer}}]'], ) check_option_errors( [Option('lawing', Record[{'tingent': Integer}])], {'lawing': {}}, ['Invalid value for option "{field}:lawing", ' 'missing fields: tingent'], ) check_option_errors( [Option('lawing', Record[{'tingent': Integer}])], {'lawing': {Invalid(): 1}}, ['Invalid value for option "{field}:lawing", ' 'unknown fields: <invalid>'], ) check_option_errors( [Option('lawing', Record[{'tingent': Integer}])], {'lawing': {'tingent': Invalid()}}, ['Invalid value for option "{field}:lawing", ' '"Invalid" instead of Integer'], )
def test_mutation_type(): data_types = { 'Foo': Record[{ 'bar': Integer, }], } query_graph = Graph([Root([ Field('getFoo', Integer, _noop, options=[ Option('getterArg', TypeRef['Foo']), ]), ])], data_types) mutation_graph = Graph(query_graph.nodes + [Root([ Field('setFoo', Integer, _noop, options=[ Option('setterArg', TypeRef['Foo']), ]), ])], data_types=query_graph.data_types) assert introspect(query_graph, mutation_graph) == _schema([ _type('Query', 'OBJECT', fields=[ _field('getFoo', _non_null(_INT), args=[ _ival('getterArg', _non_null(_iobj('IOFoo'))), ]), ]), _type('Mutation', 'OBJECT', fields=[ _field('setFoo', _non_null(_INT), args=[ _ival('setterArg', _non_null(_iobj('IOFoo'))), ]), ]), _type('Foo', 'OBJECT', fields=[ _field('bar', _non_null(_INT)), ]), _type('IOFoo', 'INPUT_OBJECT', inputFields=[ _ival('bar', _non_null(_INT)), ]), ], with_mutation=True)
def test_introspection_query(): graph = Graph([ Node('flexed', [ Field('yari', Boolean, _noop, options=[ Option('membuka', Sequence[String], default=['frayed']), Option('modist', Optional[Integer], default=None, description='callow'), ]), ]), Node('decian', [ Field('dogme', Integer, _noop), Link('clarkia', Sequence[TypeRef['flexed']], _noop, requires=None), ]), Root([ Field('_cowered', String, _noop), Field('entero', Float, _noop), Link('toma', Sequence[TypeRef['decian']], _noop, requires=None), ]), ]) assert introspect(graph) == _schema([ _type('flexed', 'OBJECT', fields=[ _field('yari', _non_null(_BOOL), args=[ _ival('membuka', _seq_of(_STR), defaultValue='["frayed"]'), _ival('modist', _INT, defaultValue='null', description='callow'), ]), ]), _type('decian', 'OBJECT', fields=[ _field('dogme', _non_null(_INT)), _field('clarkia', _seq_of(_obj('flexed'))), ]), _type('Query', 'OBJECT', fields=[ _field('entero', _non_null(_FLOAT)), _field('toma', _seq_of(_obj('decian'))), ]), ])
def test_optional_option_type_errors(): check_option_errors( [Option('lawing', Optional[Integer])], {'lawing': None}, [], ) check_option_errors( [Option('lawing', Optional[Integer])], {'lawing': Invalid()}, ['Invalid value for option "{field}:lawing", ' '"Invalid" instead of Integer'], )
def test_conflicting_fields(): x_data = {'xN': {'a': 42}} @listify def x_fields(fields, ids): for i in ids: yield ['{}-{}'.format(x_data[i][f.name], f.options['k']) for f in fields] graph = Graph([ Node('X', [ Field('a', None, x_fields, options=[Option('k', Integer)]), ]), Root([ Link('x1', TypeRef['X'], lambda: 'xN', requires=None), Link('x2', TypeRef['X'], lambda: 'xN', requires=None), ]), ]) result = execute(graph, q.Node([ q.Link('x1', q.Node([q.Field('a', options={'k': 1})])), q.Link('x2', q.Node([q.Field('a', options={'k': 2})])), ])) check_result(result, { 'x1': {'a': '42-1'}, 'x2': {'a': '42-2'}, })
def test_sequence_option_type_errors(): check_option_errors( [Option('lawing', Sequence[Integer])], {'lawing': [123]}, [], ) check_option_errors( [Option('lawing', Sequence[Integer])], {'lawing': Invalid()}, ['Invalid value for option "{field}:lawing", ' '"Invalid" instead of Sequence[Integer]'], ) check_option_errors( [Option('lawing', Sequence[Integer])], {'lawing': [Invalid()]}, ['Invalid value for option "{field}:lawing", ' '"Invalid" instead of Integer'], )
def test_field_option_missing(): graph = Graph([ Root([ Field('poofy', None, Mock(), options=[Option('mohism', None)]), ]), ]) with pytest.raises(TypeError) as err: execute(graph, build([Q.poofy])) err.match('^Required option "mohism" for Field\(\'poofy\', ' '(.*) was not provided$')
def test_missing_options(): check_option_errors( [Option('lawing', Integer)], {}, ['Required option "{field}:lawing" is not specified'], ) check_option_errors( [Option('lawing', Integer, default=1)], {}, [], ) check_option_errors( [Option('lawing', Optional[Integer])], {}, ['Required option "{field}:lawing" is not specified'], ) check_option_errors( [Option('lawing', Optional[Integer], default=None)], {}, [], )
def test_link_option_missing(): graph = Graph([ Node('slices', [ Field('papeete', None, Mock()), ]), Root([ Link('eclairs', Sequence[TypeRef['slices']], Mock(), requires=None, options=[Option('nocks', None)]), ]), ]) with pytest.raises(TypeError) as err: execute(graph, build([Q.eclairs[Q.papeete]])) err.match(r'^Required option "nocks" for Link\(\'eclairs\', ' r'(.*) was not provided$')
def test_link_contain_invalid_types(): check_errors( [ Node('foo', []), Node('bar', [ Field('id', None, _fields_func), Link('baz', Sequence[TypeRef['foo']], _link_func, requires='id', options=[Option('size', None), 1]), ]), ], [('Invalid types provided as link "bar.baz" options: {!r}' .format(int))], )
def test_unsupported_option(): result = introspect( Graph([ Root([ Field('huke', Integer, field_func, options=[Option('orel', Optional[Any])]), Field('terapin', Integer, field_func), ]), ])) assert result['__schema']['types'][-1]['name'] == 'Root' assert [f['name'] for f in result['__schema']['types'][-1]['fields']] == \ ['terapin']
def test_any_in_option(): graph = Graph([ Root([ Field('get', None, None, options=[ Option('foo', Mapping[String, Any]), ]), ]), ]) assert validate(graph, q.Node([ q.Field('get', options={'foo': {u'key': 1}}), ])) == [] assert validate(graph, q.Node([ q.Field('get', options={'foo': 'bar'}), ])) == ['Invalid value for option "root.get:foo", ' '"str" instead of Mapping[String, Any]']
def test_mapping_option_type_errors(): check_option_errors( [Option('lawing', Mapping[Integer, String])], {'lawing': {123: u"oik"}}, [], ) check_option_errors( [Option('lawing', Mapping[Integer, String])], {'lawing': Invalid()}, ['Invalid value for option "{field}:lawing", ' '"Invalid" instead of Mapping[Integer, String]'], ) check_option_errors( [Option('lawing', Mapping[Integer, String])], {'lawing': {Invalid(): u"oik"}}, ['Invalid value for option "{field}:lawing", ' '"Invalid" instead of Integer'], ) check_option_errors( [Option('lawing', Mapping[Integer, String])], {'lawing': {123: Invalid()}}, ['Invalid value for option "{field}:lawing", ' '"Invalid" instead of String'], )
def test_unsupported_option_type(): graph = Graph([ Root([ Field('huke', Integer, _noop, options=[Option('orel', Optional[Any])]), Field('terapin', Integer, _noop), ]), ]) assert introspect(graph) == _schema([ _type('Query', 'OBJECT', fields=[ _field('huke', _non_null(_INT), args=[ _ival('orel', _ANY), ]), _field('terapin', _non_null(_INT)), ]), ])
def test_conflicting_links(): data = { 'yA': {'a': 1, 'b': 2}, 'yB': {'a': 3, 'b': 4}, 'yC': {'a': 5, 'b': 6}, } x2y = {'xN': ['yA', 'yB', 'yC']} @listify def y_fields(fields, ids): for i in ids: yield [data[i][f.name] for f in fields] @listify def x_to_y_link(ids, options): for i in ids: yield [y for y in x2y[i] if y not in options['exclude']] graph = Graph([ Node('Y', [ Field('a', None, y_fields), Field('b', None, y_fields), ]), Node('X', [ Field('id', None, id_field), Link('y', Sequence[TypeRef['Y']], x_to_y_link, requires='id', options=[Option('exclude', None)]), ]), Root([ Link('x1', TypeRef['X'], lambda: 'xN', requires=None), Link('x2', TypeRef['X'], lambda: 'xN', requires=None), ]), ]) result = execute(graph, q.Node([ q.Link('x1', q.Node([ q.Link('y', q.Node([q.Field('a')]), options={'exclude': ['yA']}), ])), q.Link('x2', q.Node([ q.Link('y', q.Node([q.Field('b')]), options={'exclude': ['yC']}), ])), ])) check_result(result, { 'x1': {'y': [{'a': 3}, {'a': 5}]}, 'x2': {'y': [{'b': 2}, {'b': 4}]}, })
def test_typeref_in_option(): data_types = { 'Foo': Record[{ 'key': Integer, }], } graph = Graph([ Root([ Field('get', None, None, options=[Option('foo', TypeRef['Foo'])]), ]), ], data_types=data_types) assert validate(graph, q.Node([ q.Field('get', options={'foo': {'key': 1}}), ])) == [] assert validate(graph, q.Node([ q.Field('get', options={'foo': {'key': '1'}}), ])) == ['Invalid value for option "root.get:foo", "str" instead of Integer']
def test_distinct_by_options_links(): graph = Graph([ Node('X', [ Field('a', None, None), ]), Root([ Link('x', TypeRef['X'], None, requires=None, options=[Option('e', Optional[Integer], default=None)]), ]), ]) errors = validate(graph, q.Node([ q.Link('x', q.Node([q.Field('a')])), q.Link('x', q.Node([q.Field('a')]), options={'e': 1}), ])) assert errors == [ 'Found distinct fields with the same resulting name "x" for the ' 'node "root"' ]
def test_invalid_names(): graph = Graph([ Node('Baz-Baz', [ Field('bzz-bzz', Integer, _noop), ]), Root([ Field('foo-foo', Integer, _noop, options=[Option('bar-bar', Integer)]), Link('baz-baz', Sequence[TypeRef['Baz-Baz']], _noop, requires='foo-foo'), ]), ]) with pytest.raises(ValueError) as err: apply(graph, [GraphQLIntrospection(graph)]) assert err.match('bzz-bzz') assert err.match('foo-foo') assert err.match('bar-bar') assert err.match('baz-baz') assert err.match('Baz-Baz')
def test_scalar_option_type_errors(): check_option_errors([Option('lawing', Boolean)], {'lawing': True}, []) check_option_errors([Option('lawing', Boolean)], {'lawing': Invalid()}, [ 'Invalid value for option "{field}:lawing", ' '"Invalid" instead of Boolean' ]) check_option_errors([Option('lawing', Integer)], {'lawing': 123}, []) check_option_errors([Option('lawing', Integer)], {'lawing': Invalid()}, [ 'Invalid value for option "{field}:lawing", ' '"Invalid" instead of Integer' ]) check_option_errors([Option('lawing', String)], {'lawing': u"raundon"}, []) check_option_errors([Option('lawing', String)], {'lawing': Invalid()}, [ 'Invalid value for option "{field}:lawing", ' '"Invalid" instead of String' ])
Field('id', Integer, cart_resolver), Field('status', TypeRef['Status'], cart_resolver), Link('items', Sequence[TypeRef['CartItem']], link_cart_items, requires='id') ], directives=[Key('id')]), Node('CartItem', [ Field('id', Integer, cart_item_resolver), Field('cart_id', Integer, cart_item_resolver), Field( 'name', String, cart_item_resolver, directives=[Deprecated('do not use')] ), Field('photo', Optional[String], lambda: None, options=[ Option('width', Integer), Option('height', Integer), ]), ]), Root([ Link( 'cart', Optional[TypeRef['Cart']], ids_resolver, requires=None, options=[ Option('id', Integer) ], ), ]), ], data_types=data_types)
from hiku.engine import Engine, pass_context, Context from hiku.result import denormalize from hiku.builder import build, Q from hiku.executors.sync import SyncExecutor from .base import check_result, ANY, Mock @listify def id_field(fields, ids): for i in ids: yield [i for _ in fields] OPTION_BEHAVIOUR = [ (Option('op', None), {'op': 1812}, {'op': 1812}), (Option('op', None, default=None), {}, {'op': None}), (Option('op', None, default=None), {'op': 2340}, {'op': 2340}), (Option('op', None, default=3914), {}, {'op': 3914}), (Option('op', None, default=4254), {'op': None}, {'op': None}), (Option('op', None, default=1527), {'op': 8361}, {'op': 8361}), ] def execute(graph, query_, ctx=None): engine = Engine(SyncExecutor()) return engine.execute(graph, query_, ctx=ctx) def test_context(): ctx = Context({'foo': 'bar'})
GRAPH = Graph([ Node('hooted', []), Root([ # simple Field('robby', None, _), # complex Field('wounded', Optional[Record[{'attr': Integer}]], _), Field('annuals', Record[{'attr': Integer}], _), Field('hialeah', Sequence[Record[{'attr': Integer}]], _), Field('val', Optional[TypeRef['Val']], _), # nested records Field('rlyeh', Record[{'cthulhu': Record[{'fhtagn': Integer}]}], _), # with options Field('motown', None, _, options=[Option('prine', None)]), Field('nyerere', None, _, options=[Option('epaule', None, default=1)]), Field('wreche', None, _, options=[Option('cierra', Integer)]), Field('hunter', None, _, options=[Option('fried', Integer, default=1)]), Field('tapioca', None, _, options=[Option('arbour', Optional[Integer], default=None)]), # simple Link('amyls', Sequence[TypeRef['hooted']], _, requires=None), # with options Link('ferrous', Sequence[TypeRef['hooted']], _, requires=None, options=[Option('cantab', None)]), Link('knesset', Sequence[TypeRef['hooted']], _, requires=None, options=[Option('ceases', None, default=1)]), Link('pouria', Sequence[TypeRef['hooted']], _, requires=None, options=[Option('flunk', Integer)]),
Field('status', String, cart_resolver), Link('items', Sequence[TypeRef['CartItem']], link_cart_items, requires='id') ], directives=[Key('id')]), Node('CartItem', [ Field('id', Integer, cart_item_resolver), Field('cart_id', Integer, cart_item_resolver), Field('name', String, cart_item_resolver), Field('photo', Optional[String], lambda: None, options=[ Option('width', Integer), Option('height', Integer), ]), ]), Root([ Link( 'cart', Optional[TypeRef['Cart']], direct_link_id, requires=None, options=[Option('id', Integer)], ), ]), ]) app = Flask(__name__)
sg_y = SubGraph(_GRAPH, 'y') # TODO: refactor GRAPH = Graph([ Node('x1', [ Field('id', None, sg_x), Field('a', None, sg_x), Field('f', None, sg_x.c(S.f1)), Field('foo', None, sg_x.c(foo(S.this, S.this.y))), Field('bar', None, sg_x.c(bar(S.this))), Field('baz', None, sg_x.c(baz(S.this.y))), Field('buz', None, sg_x.c(buz(S.this, S.size)), options=[Option('size', None, default=None)]), Field('buz2', None, sg_x.c(buz(S.this, S.size)), options=[Option('size', None, default=100)]), Field('buz3', None, sg_x.c(buz(S.this, S.size)), options=[Option('size', None)]), ]), Node('y1', [ Field('id', None, sg_y), Field('c', None, sg_y), Field('f', None, sg_y.c(S.f2)), Field('foo', None, sg_y.c(each(S.x, S.this.xs, foo(S.x, S.this)))), Field('bar', None, sg_y.c(each(S.x, S.this.xs, bar(S.x)))),
sg_y = SubGraph(_GRAPH, 'y') # TODO: refactor GRAPH = Graph([ Node('x1', [ Field('id', None, sg_x), Field('a', None, sg_x), Field('f', None, sg_x.c(S.f1)), Field('foo', None, sg_x.c(foo(S.this, S.this.y))), Field('bar', None, sg_x.c(bar(S.this))), Field('baz', None, sg_x.c(baz(S.this.y))), Field('buz', None, sg_x.c(buz(S.this, S.size)), options=[Option('size', None, default=None)]), Field('buz2', None, sg_x.c(buz(S.this, S.size)), options=[Option('size', None, default=100)]), Field('buz3', None, sg_x.c(buz(S.this, S.size)), options=[Option('size', None)]), Field('with_option', None, query_x1, options=[Option('opt', None)]), ]), Node('y1', [ Field('id', None, sg_y), Field('c', None, sg_y), Field('f', None, sg_y.c(S.f2)), Field('foo', None, sg_y.c(each(S.x, S.this.xs, foo(S.x, S.this)))),
_GRAPH = Graph([ Node('flexed', [ Field('yari', Boolean, field_func), ]), ]) flexed_sg = SubGraph(_GRAPH, 'flexed') GRAPH = Graph([ Node('flexed', [ Field('yari', Boolean, flexed_sg, options=[ Option('membuka', Sequence[String], default=['frayed']), Option('modist', Optional[Integer], default=None, description='callow'), ]), ]), Node('decian', [ Field('dogme', Integer, field_func), Link('clarkia', Sequence[TypeRef['flexed']], link_func, requires=None), ]), Root([ Field('cowered', String, field_func), Field('entero', Float, field_func), Link('toma', Sequence[TypeRef['decian']], link_func, requires=None), ]),
def test_field_option_unknown(): test_field_option_valid(Option('inked', None), { 'inked': 2340, 'unknown': 8775 }, {'inked': 2340})
import re import pytest from hiku import query from hiku.graph import Graph, Node, Field, Link, Option, Root from hiku.types import Record, Sequence, Integer, Optional, TypeRef from hiku.engine import Engine, pass_context, Context from hiku.builder import build, Q from hiku.executors.sync import SyncExecutor from .base import reqs_eq_patcher, check_result, ANY, Mock OPTION_BEHAVIOUR = [ (Option('op', None), { 'op': 1812 }, { 'op': 1812 }), (Option('op', None, default=None), {}, { 'op': None }), (Option('op', None, default=None), { 'op': 2340 }, { 'op': 2340 }), (Option('op', None, default=3914), {}, { 'op': 3914 }), (Option('op', None, default=4254), {
def test_link_option_unknown(): test_link_option_valid(Option('oleic', None), { 'oleic': 2340, 'unknown': 8775 }, {'oleic': 2340})
}], 'Data': Record[{ 'point': TypeRef['Point'], }], } QUERY_GRAPH = Graph([ Root([ Field('value', String, value_func), ]), ], data_types=DATA_TYPES) MUTATION_GRAPH = Graph(QUERY_GRAPH.nodes + [ Root([ Field('action', Boolean, action_func, options=[Option('data', TypeRef['Data'])]), ]), ], data_types=DATA_TYPES) async def handle_graphql(request): data = await request.json() result = await request.app['graphql-endpoint'].dispatch(data) return web.json_response(result) def main(): logging.basicConfig() app = web.Application() app.add_routes([ web.post('/graphql', handle_graphql),