Skip to content

Commit

Permalink
Switch to GraphQL-core 3.2
Browse files Browse the repository at this point in the history
  • Loading branch information
Cito committed Apr 15, 2022
1 parent 97ee4df commit 4c69f4f
Show file tree
Hide file tree
Showing 18 changed files with 122 additions and 266 deletions.
137 changes: 72 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,47 +63,53 @@ from graphql_relay import connection_definitions
### Connections

Helper functions are provided for both building the GraphQL types
for connections and for implementing the `resolver` method for fields
for connections and for implementing the `resolve` method for fields
returning those types.

- `connection_args` returns the arguments that fields should provide when
they return a connection type.
they return a connection type that supports bidirectional pagination.
- `forward_connection_args` returns the arguments that fields should provide when
they return a connection type that only supports forward pagination.
- `backward_connection_args` returns the arguments that fields should provide when
they return a connection type that only supports backward pagination.
- `connection_definitions` returns a `connection_type` and its associated
`edgeType`, given a name and a node type.
- `connection_from_array` is a helper method that takes an array and the
arguments from `connection_args`, does pagination and filtering, and returns
an object in the shape expected by a `connection_type`'s `resolver` function.
an object in the shape expected by a `connection_type`'s `resolve` function.
- `cursor_for_object_in_connection` is a helper method that takes an array and a
member object, and returns a cursor for use in the mutation payload.
- `offset_to_cursor` takes the index of a member object in an array
and returns an opaque cursor for use in the mutation payload.
- `cursor_to_offset` takes an opaque cursor (created with `offset_to_cursor`)
and returns the corresponding array index.

An example usage of these methods from the [test schema](tests/star_wars_schema.py):

```python
ship_edge, ship_connection = connection_definitions('Ship', shipType)
ship_edge, ship_connection = connection_definitions(ship_type, "Ship")

factionType = GraphQLObjectType(
name='Faction',
description='A faction in the Star Wars saga',
faction_type = GraphQLObjectType(
name="Faction",
description="A faction in the Star Wars saga",
fields=lambda: {
'id': global_id_field('Faction'),
'name': GraphQLField(
GraphQLString,
description='The name of the faction.',
),
'ships': GraphQLField(
"id": global_id_field("Faction"),
"name": GraphQLField(GraphQLString, description="The name of the faction."),
"ships": GraphQLField(
ship_connection,
description='The ships used by the faction.',
description="The ships used by the faction.",
args=connection_args,
resolve=lambda faction, _info, **args: connection_from_array(
[getShip(ship) for ship in faction.ships], args),
)
[get_ship(ship) for ship in faction.ships], args
),
),
},
interfaces=[node_interface]
interfaces=[node_interface],
)
```

This shows adding a `ships` field to the `Faction` object that is a connection.
It uses `connection_definitions('Ship', shipType)` to create the connection
It uses `connection_definitions(ship_type, "Ship")` to create the connection
type, adds `connection_args` as arguments on this function, and then implements
the resolver function by passing the array of ships and the arguments to
`connection_from_array`.
Expand Down Expand Up @@ -131,40 +137,49 @@ An example usage of these methods from the [test schema](tests/star_wars_schema.
```python
def get_node(global_id, _info):
type_, id_ = from_global_id(global_id)
if type_ == 'Faction':
return getFaction(id_)
elif type_ == 'Ship':
return getShip(id_)
else:
return None
if type_ == "Faction":
return get_faction(id_)
if type_ == "Ship":
return get_ship(id_)
return None # pragma: no cover

def get_node_type(obj, _info, _type):
if isinstance(obj, Faction):
return factionType
else:
return shipType
return faction_type.name
return ship_type.name

node_interface, node_field = node_definitions(get_node, get_node_type)
node_interface, node_field = node_definitions(get_node, get_node_type)[:2]

factionType = GraphQLObjectType(
name= 'Faction',
description= 'A faction in the Star Wars saga',
fields= lambda: {
'id': global_id_field('Faction'),
faction_type = GraphQLObjectType(
name="Faction",
description="A faction in the Star Wars saga",
fields=lambda: {
"id": global_id_field("Faction"),
"name": GraphQLField(GraphQLString, description="The name of the faction."),
"ships": GraphQLField(
ship_connection,
description="The ships used by the faction.",
args=connection_args,
resolve=lambda faction, _info, **args: connection_from_array(
[get_ship(ship) for ship in faction.ships], args
),
),
},
interfaces= [node_interface]
interfaces=[node_interface],
)

queryType = GraphQLObjectType(
name= 'Query',
fields= lambda: {
'node': node_field
}
query_type = GraphQLObjectType(
name="Query",
fields=lambda: {
"rebels": GraphQLField(faction_type, resolve=lambda _obj, _info: get_rebels()),
"empire": GraphQLField(faction_type, resolve=lambda _obj, _info: get_empire()),
"node": node_field,
},
)
```

This uses `node_definitions` to construct the `Node` interface and the `node`
field; it uses `from_global_id` to resolve the IDs passed in in the implementation
field; it uses `from_global_id` to resolve the IDs passed in the implementation
of the function mapping ID to object. It then uses the `global_id_field` method to
create the `id` field on `Faction`, which also ensures implements the
`node_interface`. Finally, it adds the `node` field to the query type, using the
Expand All @@ -184,43 +199,35 @@ An example usage of these methods from the [test schema](tests/star_wars_schema.

```python
class IntroduceShipMutation:

def __init__(self, shipId, factionId, clientMutationId=None):
self.shipId = shipId
self.factionId = factionId
self.clientMutationId = clientMutationId

def mutate_and_get_payload(_info, shipName, factionId):
newShip = createShip(shipName, factionId)
return IntroduceShipMutation(shipId=newShip.id, factionId=factionId)
def mutate_and_get_payload(_info, shipName, factionId, **_input):
new_ship = create_ship(shipName, factionId)
return IntroduceShipMutation(shipId=new_ship.id, factionId=factionId)

shipMutation = mutation_with_client_mutation_id(
'IntroduceShip',
ship_mutation = mutation_with_client_mutation_id(
"IntroduceShip",
input_fields={
'shipName': GraphQLInputField(
GraphQLNonNull(GraphQLString)
),
'factionId': GraphQLInputField(
GraphQLNonNull(GraphQLID)
)
"shipName": GraphQLInputField(GraphQLNonNull(GraphQLString)),
"factionId": GraphQLInputField(GraphQLNonNull(GraphQLID)),
},
output_fields={
'ship': GraphQLField(
shipType,
resolve=lambda payload, _info: getShip(payload.shipId)
"ship": GraphQLField(
ship_type, resolve=lambda payload, _info: get_ship(payload.shipId)
),
"faction": GraphQLField(
faction_type, resolve=lambda payload, _info: get_faction(payload.factionId)
),
'faction': GraphQLField(
factionType,
resolve=lambda payload, _info: getFaction(payload.factionId)
)
},
mutate_and_get_payload=mutate_and_get_payload
mutate_and_get_payload=mutate_and_get_payload,
)

mutationType = GraphQLObjectType(
'Mutation',
fields=lambda: {
'introduceShip': shipMutation
}
mutation_type = GraphQLObjectType(
"Mutation", fields=lambda: {"introduceShip": ship_mutation}
)
```

Expand Down Expand Up @@ -268,5 +275,5 @@ Python versions and perform all additional source code checks.
You can also restrict tox to an individual environment, like this:

```sh
poetry run tox -e py37
poetry run tox -e py39
```
8 changes: 4 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ packages = [

[tool.poetry.dependencies]
python = "^3.6"
graphql-core = "~3.1"
graphql-core = "~3.2"
typing-extensions = { version = "^4.1", python = "<3.8" }

[tool.poetry.dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"Programming Language :: Python :: Implementation :: PyPy",
],
install_requires=[
"graphql-core>=3.1,<3.2",
"typing-extensions>=4,<5; python_version < '3.8'",
"graphql-core>=3.2,<3.3",
"typing-extensions>=4.1,<5; python_version < '3.8'",
],
python_requires=">=3.6,<4",
packages=find_packages("src"),
Expand Down
9 changes: 0 additions & 9 deletions src/graphql_relay/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
# Helper for creating mutations with client mutation IDs
from .mutation.mutation import (
mutation_with_client_mutation_id,
resolve_maybe_thunk,
MutationFn,
MutationFnWithoutArgs,
NullResult,
Expand All @@ -53,13 +52,6 @@
# Utilities for creating global IDs in systems that don't have them
from .node.node import from_global_id, global_id_field, to_global_id, ResolvedGlobalId

# Deprecated functions from older graphql-relay-py versions
# noinspection PyProtectedMember,PyUnresolvedReferences,PyDeprecation
from .connection.array_connection import ( # noqa: F401
connection_from_list,
connection_from_list_slice,
)

__version__ = version
__version_info__ = version_info
__version_js__ = version_js
Expand Down Expand Up @@ -99,7 +91,6 @@
"page_info_type",
"plural_identifying_root_field",
"ResolvedGlobalId",
"resolve_maybe_thunk",
"SizedSliceable",
"to_global_id",
"version",
Expand Down
61 changes: 0 additions & 61 deletions src/graphql_relay/connection/array_connection.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import warnings
from typing import Any, Iterator, Optional, Sequence

try:
Expand Down Expand Up @@ -71,33 +70,6 @@ def connection_from_array(
)


def connection_from_list(
data: Sequence,
args: Optional[ConnectionArguments] = None,
connection_type: ConnectionConstructor = Connection,
edge_type: EdgeConstructor = Edge,
pageinfo_type: PageInfoConstructor = PageInfo,
) -> ConnectionType:
"""Deprecated alias for connection_from_array.
We're now using the JavaScript terminology in Python as well, since list
is too narrow a type and there is no other really appropriate type name.
"""
warnings.warn(
"connection_from_list() has been deprecated."
" Please use connection_from_array() instead.",
DeprecationWarning,
stacklevel=2,
)
return connection_from_array_slice(
data,
args,
connection_type=connection_type,
edge_type=edge_type,
page_info_type=pageinfo_type,
)


def connection_from_array_slice(
array_slice: SizedSliceable,
args: Optional[ConnectionArguments] = None,
Expand Down Expand Up @@ -183,39 +155,6 @@ def connection_from_array_slice(
)


def connection_from_list_slice(
list_slice: Sequence,
args: Optional[ConnectionArguments] = None,
connection_type: ConnectionConstructor = Connection,
edge_type: EdgeConstructor = Edge,
pageinfo_type: PageInfoConstructor = PageInfo,
slice_start: int = 0,
list_length: int = 0,
list_slice_length: Optional[int] = None,
) -> ConnectionType:
"""Deprecated alias for connection_from_array_slice.
We're now using the JavaScript terminology in Python as well, since list
is too narrow a type and there is no other really appropriate type name.
"""
warnings.warn(
"connection_from_list_slice() has been deprecated."
" Please use connection_from_array_slice() instead.",
DeprecationWarning,
stacklevel=2,
)
return connection_from_array_slice(
list_slice,
args,
slice_start=slice_start,
array_length=list_length,
array_slice_length=list_slice_length,
connection_type=connection_type,
edge_type=edge_type,
page_info_type=pageinfo_type,
)


PREFIX = "arrayconnection:"


Expand Down
7 changes: 0 additions & 7 deletions src/graphql_relay/connection/arrayconnection.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@
SizedSliceable,
)

# Deprecated functions from older graphql-relay-py versions
# noinspection PyProtectedMember,PyUnresolvedReferences,PyDeprecation
from .array_connection import ( # noqa: F401
connection_from_list,
connection_from_list_slice,
)

warnings.warn(
"The 'arrayconnection' module is deprecated. "
"Functions should be imported from the top-level package instead.",
Expand Down
Loading

0 comments on commit 4c69f4f

Please sign in to comment.