def test_flattened_converted_error(): with raises(TypeError): deserialize(Data3, {"attr": 0}) with raises(TypeError): serialize(Data3, Data3(Field2(0))) with raises(TypeError): deserialization_schema(Data3) with raises(TypeError): serialization_schema(Data3) with raises(TypeError): graphql_schema(query=[get_data3])
def test_default_conversion_type_name(): assert ( print_schema(graphql_schema(query=[b])) == """\ type Query { b: B! } type B { a: Int! } """ ) assert serialization_schema(B, all_refs=True) == { "$ref": "#/$defs/B", "$defs": { "B": {"$ref": "#/$defs/A"}, "A": { "type": "object", "properties": {"a": {"type": "integer"}}, "required": ["a"], "additionalProperties": False, }, }, "$schema": "http://json-schema.org/draft/2019-09/schema#", } assert serialization_schema(B) == { "type": "object", "properties": {"a": {"type": "integer"}}, "required": ["a"], "additionalProperties": False, "$schema": "http://json-schema.org/draft/2019-09/schema#", }
async def test_subscription(alias, conversion, error_handler, resolver): if alias is not None: sub_name = alias elif resolver is not None: sub_name = resolver.__name__ else: sub_name = events.__name__ if (alias, conversion, error_handler, resolver) == (None, None, Undefined, None): sub_op = events else: sub_op = Subscription(events, alias, conversion, None, error_handler, resolver=resolver) schema = graphql_schema(query=[hello], subscription=[sub_op], types=[Event]) sub_field = sub_name if resolver is not None: sub_field += "(dummy: Boolean)" sub_field += f": {'String' if conversion else 'Event'}" if error_handler is Undefined: sub_field += "!" schema_str = """\ type Event { name: String! } type Query { hello: String! } type Subscription { %s } """ assert print_schema(schema) == schema_str % sub_field sub_query = sub_name if conversion is None: sub_query += "{name}" subscription = await graphql.subscribe( schema, graphql.parse("subscription {%s}" % sub_query)) result = EVENTS if resolver: result = [s.capitalize() for s in result] if not conversion: result = [{"name": s} for s in result] assert [ev.data async for ev in subscription] == [{ sub_name: r } for r in result]
def test_annotated_schema(): assert (deserialization_schema(A) == serialization_schema(A) == { "$schema": "http://json-schema.org/draft/2019-09/schema#", "type": "object", "properties": { "a": { "type": "integer", "maximum": 10, "minimum": 0, "description": "field description", } }, "required": ["a"], "additionalProperties": False, }) assert (deserialization_schema(A, all_refs=True) == serialization_schema( A, all_refs=True) == { "$schema": "http://json-schema.org/draft/2019-09/schema#", "$ref": "#/$defs/A", "$defs": { "A": { "additionalProperties": False, "properties": { "a": { "$ref": "#/$defs/someInt", "description": "field description", "minimum": 0, } }, "required": ["a"], "type": "object", }, "someInt": { "description": "type description", "maximum": 10, "type": "integer", }, }, }) assert (print_schema(graphql_schema(query=[a])) == '''\ type Query { a: A! } type A { """field description""" a: someInt! } """type description""" scalar someInt ''')
def test_flattened_converted(): data2 = deserialize(Data2, {"attr": 0}) assert isinstance(data2.data_field2, Field2) and data2.data_field2.attr == 0 assert serialize(Data2, data2) == {"attr": 0} assert (deserialization_schema(Data) == serialization_schema(Data) == { "$schema": "http://json-schema.org/draft/2019-09/schema#", "type": "object", "allOf": [ { "type": "object", "additionalProperties": False }, { "type": "object", "properties": { "attr": { "type": "integer" } }, "required": ["attr"], "additionalProperties": False, }, ], "unevaluatedProperties": False, }) schema = graphql_schema(query=[get_data2]) assert graphql_sync(schema, "{getData2{attr}}").data == { "getData2": { "attr": 0 } } assert (print_schema(schema) == """\ type Query { getData2: Data2! } type Data2 { attr: Int! } """)
def test_resolver_default_parameter_not_serializable(tp, default): def resolver(arg=default) -> bool: return arg is default resolver.__annotations__["arg"] = tp # wraps in order to trigger the bug of get_type_hints with None default value resolver2 = wraps(resolver)(lambda arg=default: resolver(arg)) schema = graphql_schema(query=[resolver2]) assert ( print_schema(schema) == """\ type Query { resolver(arg: Int): Boolean! } """ ) assert ( graphql_sync(schema, "{resolver}").data == graphql_sync(schema, "{resolver(arg: null)}").data == {"resolver": True} )
def test_resolver_position(): assert serialization_schema(B) == { "type": "object", "properties": { "a": { "type": "integer" }, "b": { "type": "integer" }, "c": { "type": "integer" }, "d": { "type": "integer" }, "e": { "type": "integer" }, }, "required": ["a", "b", "c", "d", "e"], "additionalProperties": False, "$schema": "http://json-schema.org/draft/2019-09/schema#", } assert (print_schema(graphql_schema(query=[query])) == """\ type Query { query: B! } type B { a: Int! b: Int! c: Int! d: Int! e: Int! } """)
@dataclass class Foo: foo: int @dataclass class Bar: bar: int def foo_or_bar() -> Union[Foo, Bar]: ... # union_ref default value is made explicit here schema = graphql_schema(query=[foo_or_bar], union_name="Or".join) schema_str = """\ type Query { fooOrBar: FooOrBar! } union FooOrBar = Foo | Bar type Foo { foo: Int! } type Bar { bar: Int! } """
from graphql.utilities import print_schema from apischema import schema from apischema.graphql import graphql_schema class MyEnum(Enum): FOO = "FOO" BAR = "BAR" def echo(enum: MyEnum) -> MyEnum: return enum schema_ = graphql_schema(query=[echo], enum_schemas={MyEnum.FOO: schema(description="foo")}) schema_str = '''\ type Query { echo(enum: MyEnum!): MyEnum! } enum MyEnum { """foo""" FOO BAR } ''' assert print_schema(schema_) == schema_str assert graphql_sync(schema_, "{echo(enum: FOO)}").data == {"echo": "FOO"}
from dataclasses import dataclass from graphql import print_schema from apischema.graphql import Query, graphql_schema, resolver @dataclass class Foo: @resolver async def bar(self, arg: int = 0) -> str: ... async def get_foo() -> Foo: ... schema = graphql_schema(query=[Query(get_foo, alias="foo", error_handler=None)]) schema_str = """\ type Query { foo: Foo } type Foo { bar(arg: Int! = 0): String! } """ assert print_schema(schema) == schema_str
return USERS def posts() -> Collection[Post]: return POSTS def user(username: str) -> Optional[User]: for user in users(): if user.username == username: return user else: return None schema = graphql_schema(query=[users, user, posts], id_types={UUID}) schema_str = """\ type Query { users: [User!]! user(username: String!): User posts: [Post!]! } type User { id: ID! username: String! birthday: Date posts: [Post!]! } scalar Date
from typing import Optional, Union from graphql import graphql_sync from apischema import Undefined, UndefinedType from apischema.graphql import graphql_schema def arg_is_absent( arg: Optional[Union[int, UndefinedType]] = Undefined) -> bool: return arg is Undefined schema = graphql_schema(query=[arg_is_absent]) assert graphql_sync(schema, "{argIsAbsent(arg: null)}").data == { "argIsAbsent": False } assert graphql_sync(schema, "{argIsAbsent}").data == {"argIsAbsent": True}
# TOTAL FRAMES total_frames = len(path) # Capture the total length of the path # MAX FRAMES # Limit the consumed data by the max_frames argument if max_frames and (max_frames < len(path)): # Cap the frames by the max limit path = reduce_frames(dims, max_frames) # WARNING: path object is consumed after this statement chunk = path.consume(max_frames) return PointsResponse(chunk, total_frames) # Define the schema schema = graphql_schema(query=[validate_spec, get_points]) def reduce_frames(dims: List[Dimension], max_frames: int) -> Path: """Removes frames from a spec such that it produces a number that is closest to the max points value Args: dims (List[Dimension]): A dimension object created by a spec max_frames (int): The maximum number of frames the user wishes to be returned Returns: Path: A consumable object containing the expanded dimension with reduced frames """ # Calculate the total number of frames num_frames = 1
from apischema import serialize from apischema.graphql import graphql_schema, relay @dataclass class Faction(relay.Node[int]): name: str @classmethod def get_by_id(cls, id: int, info: graphql.GraphQLResolveInfo = None) -> "Faction": return [Faction(0, "Empire"), Faction(1, "Rebels")][id] schema = graphql_schema(query=[relay.node], types=relay.nodes()) some_global_id = Faction.get_by_id(0).global_id # Let's pick a global id ... assert some_global_id == relay.GlobalId("0", Faction) query = """ query factionName($id: ID!) { node(id: $id) { ... on Faction { name } } } """ assert graphql.graphql_sync( # ... and use it in a query schema, query, variable_values={
from dataclasses import dataclass from typing import Optional from uuid import UUID from graphql import print_schema from apischema.graphql import graphql_schema @dataclass class Foo: bar: UUID def foo() -> Optional[Foo]: ... # id_types={UUID} is equivalent to id_types=lambda t: t in {UUID} schema = graphql_schema(query=[foo], id_types={UUID}) schema_str = """\ type Query { foo: Foo } type Foo { bar: ID! } """ assert print_schema(schema) == schema_str
"items": { "type": "string" }, "uniqueItems": True }, }, "required": ["id", "name"], "additionalProperties": False, } # Define GraphQL operations def resources(tags: Collection[str] = None) -> Collection[Resource]: ... # Generate GraphQL schema schema = graphql_schema(query=[resources], id_types={UUID}) schema_str = """\ type Query { resources(tags: [String!]): [Resource!] } type Resource { id: ID! name: String! tags: [String!]! } """ assert print_schema(schema) == schema_str
@interface @dataclass class Bar: bar: int @dataclass class Foo(Bar): baz: str def bar() -> Bar: ... schema = graphql_schema(query=[bar], types=[Foo]) # type Foo would have not been present if Foo was not put in types schema_str = """\ type Foo implements Bar { bar: Int! baz: String! } interface Bar { bar: Int! } type Query { bar: Bar! } """
async def events() -> AsyncIterable[str]: yield "bonjour" yield "au revoir" @dataclass class Message: body: str # Message can also be used directly as a function schema = graphql_schema( query=[hello], subscription=[ Subscription(events, alias="messageReceived", resolver=Message) ], ) schema_str = """\ type Query { hello: String! } type Subscription { messageReceived: Message! } type Message { body: String! } """
@staticmethod def mutate( # mut_id is required because no default value faction_id: str, ship_name: str, mut_id: relay.ClientMutationId, ) -> "IntroduceShip": ... def hello() -> str: return "world" schema = graphql_schema(query=[hello], mutation=relay.mutations()) # clientMutationId field becomes non nullable in introduceShip types schema_str = """\ type Query { hello: String! } type Mutation { introduceShip(input: IntroduceShipInput!): IntroduceShipPayload! } type IntroduceShipPayload { ship: Ship! faction: Faction! clientMutationId: String! }
from dataclasses import dataclass from typing import Optional from uuid import UUID from graphql import graphql_sync from apischema.graphql import graphql_schema @dataclass class Foo: id: UUID def foo() -> Optional[Foo]: return Foo(UUID("58c88e87-5769-4723-8974-f9ec5007a38b")) schema = graphql_schema( query=[foo], id_types={UUID}, id_encoding=( lambda s: b64decode(s).decode(), lambda s: b64encode(s.encode()).decode(), ), ) assert graphql_sync(schema, "{foo{id}}").data == { "foo": {"id": "NThjODhlODctNTc2OS00NzIzLTg5NzQtZjllYzUwMDdhMzhi"} }
class Bar: baz: int @dataclass class Foo: @resolver async def bar(self, arg: int = 0) -> Bar: ... async def foo() -> Foo: ... schema = graphql_schema(query=[foo]) schema_str = """\ type Query { foo: Foo } type Foo { bar(arg: Int! = 0): Bar } type Bar { baz: Int! } """ assert print_schema(schema) == schema_str
import graphql from graphql import print_schema from apischema.graphql import graphql_schema def hello() -> str: return "world" async def events() -> AsyncIterable[str]: yield "bonjour" yield "au revoir" schema = graphql_schema(query=[hello], subscription=[events]) schema_str = """\ type Query { hello: String! } type Subscription { events: String! } """ assert print_schema(schema) == schema_str async def test(): subscription = await graphql.subscribe( schema, graphql.parse("subscription {events}"))
@dataclass class Faction: @resolver def ships( self, first: Optional[int], after: Optional[Cursor] ) -> Optional[Connection[Optional[Ship]]]: edges = [relay.Edge(Ship("X-Wing"), 0), relay.Edge(Ship("B-Wing"), 1)] return Connection(edges, relay.PageInfo.from_edges(edges)) def faction() -> Optional[Faction]: return Faction() schema = graphql_schema(query=[faction]) schema_str = """\ type Query { faction: Faction } type Faction { ships(first: Int, after: Int): ShipConnection } type ShipConnection { edges: [ShipEdge] pageInfo: PageInfo! } type ShipEdge {
from apischema.graphql import graphql_schema def foo(test: int) -> int: return test def bar(my_test: int) -> int: return my_test def baz(my_test: Optional[int]) -> int: return my_test or 1 schema = graphql_schema(query=[foo, bar, baz]) def test_no_alias_needed(): query = """ { foo(test: 4) } """ assert graphql_sync(schema, query).data == {"foo": 4} def test_aliased_parameter(): query = """ {
@dataclass class Bar: field: str class Foo(TaggedUnion): bar: Tagged[Bar] baz: Tagged[int] def query(foo: Foo) -> Foo: return foo schema = graphql_schema(query=[query]) schema_str = """\ type Query { query(foo: FooInput!): Foo! } type Foo { bar: Bar baz: Int } type Bar { field: String! } input FooInput {
class Concat(Drawing): left: Drawing right: Drawing async def points(self) -> AsyncIterable[float]: async for point in self.left.points(): yield point async for point in self.right.points(): yield point def echo(drawing: Drawing = None) -> Optional[Drawing]: return drawing drawing_schema = graphql_schema(query=[echo]) assert (graphql.utilities.print_schema(drawing_schema) == """\ type Query { echo(drawing: DrawingInput): Drawing } type Drawing { Line: Line Concat: Concat } type Line { start: Float! stop: Float! step: Float! }