-
Notifications
You must be signed in to change notification settings - Fork 177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bidirectional scalars #24
Changes from 9 commits
9b6acb2
d4ae34b
a5ef336
769bd69
6d6a089
2f2402d
8697673
9bcfa17
ab25793
a83d7e9
a457266
21455d2
aa1cde2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,16 +24,24 @@ def add_resolve_functions_to_schema(schema: GraphQLSchema, resolvers: dict): | |
if isinstance(type_object, GraphQLObjectType): | ||
add_resolve_functions_to_object(type_name, type_object, resolvers) | ||
if isinstance(type_object, GraphQLScalarType): | ||
add_resolve_function_to_scalar(type_name, type_object, resolvers) | ||
add_resolve_functions_to_scalar(type_name, type_object, resolvers) | ||
|
||
|
||
def add_resolve_functions_to_object(name: str, obj: GraphQLObjectType, resolvers: dict): | ||
type_resolver = resolvers.get(name, {}) | ||
type_resolvers = resolvers.get(name, {}) | ||
for field_name, field_object in obj.fields.items(): | ||
field_resolver = type_resolver.get(field_name, default_resolver) | ||
field_resolver = type_resolvers.get(field_name, default_resolver) | ||
field_object.resolver = field_resolver | ||
|
||
|
||
def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict): | ||
serializer = resolvers.get(name, obj.serialize) | ||
def add_resolve_functions_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict): | ||
scalar_resolvers = resolvers.get(name, {}) | ||
|
||
serializer = scalar_resolvers.get("serializer", obj.serialize) | ||
obj.serialize = serializer | ||
|
||
parse_literal = scalar_resolvers.get("parse_literal", obj.parse_literal) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we could default to calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This requires further study. Imho its worth pursuing in separate task. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
obj.parse_literal = parse_literal | ||
|
||
parse_value = scalar_resolvers.get("parse_value", obj.parse_value) | ||
obj.parse_value = parse_value |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
from datetime import date, datetime | ||
|
||
from graphql import graphql | ||
from graphql.language.ast import StringValue | ||
|
||
from ariadne import make_executable_schema | ||
|
||
type_defs = """ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to use ALL_CAPITAL_WITH_UNDERSCORE convention for constants, just like pep8 suggests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have strong opinion either way, but I've been looking at |
||
scalar DateReadOnly | ||
scalar DateInput | ||
|
||
type Query { | ||
testSerialize: DateReadOnly! | ||
testInput(value: DateInput!): Boolean! | ||
} | ||
""" | ||
|
||
|
||
def serialize(date): | ||
return date.strftime("%Y-%m-%d") | ||
|
||
|
||
def parse_literal(ast): | ||
if isinstance(ast, StringValue): | ||
salwator marked this conversation as resolved.
Show resolved
Hide resolved
|
||
formatted_date = ast.value | ||
try: | ||
parsed_datetime = datetime.strptime(formatted_date, "%Y-%m-%d") | ||
return parsed_datetime.date() | ||
except ValueError: | ||
pass | ||
return None | ||
|
||
|
||
def parse_value(formatted_date): | ||
try: | ||
parsed_datetime = datetime.strptime(formatted_date, "%Y-%m-%d") | ||
return parsed_datetime.date() | ||
except (TypeError, ValueError): | ||
return None | ||
|
||
|
||
def resolve_test_serialize(*_): | ||
return date.today() | ||
|
||
|
||
def resolve_test_input(*_, value): | ||
assert value == date.today() | ||
return True | ||
|
||
|
||
resolvers = { | ||
"Query": {"testSerialize": resolve_test_serialize, "testInput": resolve_test_input}, | ||
"DateReadOnly": {"serializer": serialize}, | ||
"DateInput": {"parse_literal": parse_literal, "parse_value": parse_value}, | ||
} | ||
|
||
schema = make_executable_schema(type_defs, resolvers) | ||
|
||
|
||
def test_serialize_date_obj_to_date_str(): | ||
result = graphql(schema, "{ testSerialize }") | ||
assert result.errors is None | ||
assert result.data == {"testSerialize": date.today().strftime("%Y-%m-%d")} | ||
|
||
|
||
def test_parse_literal_valid_str_ast_to_date_instance(): | ||
test_input = date.today().strftime("%Y-%m-%d") | ||
salwator marked this conversation as resolved.
Show resolved
Hide resolved
|
||
result = graphql(schema, '{ testInput(value: "%s") }' % test_input) | ||
assert result.errors is None | ||
assert result.data == {"testInput": True} | ||
|
||
|
||
def test_parse_literal_invalid_str_ast_to_date_instance(): | ||
salwator marked this conversation as resolved.
Show resolved
Hide resolved
|
||
test_input = "invalid string" | ||
result = graphql(schema, '{ testInput(value: "%s") }' % test_input) | ||
assert result.errors is not None | ||
assert str(result.errors[0]).splitlines() == [ | ||
'Argument "value" has invalid value "invalid string".', | ||
'Expected type "DateInput", found "invalid string".', | ||
] | ||
|
||
|
||
def test_parse_literal_invalid_int_ast_errors(): | ||
test_input = 123 | ||
result = graphql(schema, "{ testInput(value: %s) }" % test_input) | ||
assert result.errors is not None | ||
assert str(result.errors[0]).splitlines() == [ | ||
'Argument "value" has invalid value 123.', | ||
'Expected type "DateInput", found 123.', | ||
] | ||
|
||
|
||
parametrized_query = """ | ||
query parseValueTest($value: DateInput!) { | ||
testInput(value: $value) | ||
} | ||
""" | ||
|
||
|
||
def test_parse_value_valid_date_str_returns_date_instance(): | ||
variables = {"value": date.today().strftime("%Y-%m-%d")} | ||
result = graphql(schema, parametrized_query, variables=variables) | ||
assert result.errors is None | ||
assert result.data == {"testInput": True} | ||
|
||
|
||
def test_parse_value_invalid_str_errors(): | ||
variables = {"value": "invalid string"} | ||
result = graphql(schema, parametrized_query, variables=variables) | ||
assert result.errors is not None | ||
assert str(result.errors[0]).splitlines() == [ | ||
'Variable "$value" got invalid value "invalid string".', | ||
'Expected type "DateInput", found "invalid string".', | ||
] | ||
|
||
|
||
def test_parse_value_invalid_value_type_int_errors(): | ||
variables = {"value": 123} | ||
result = graphql(schema, parametrized_query, variables=variables) | ||
assert result.errors is not None | ||
assert str(result.errors[0]).splitlines() == [ | ||
'Variable "$value" got invalid value 123.', | ||
'Expected type "DateInput", found 123.', | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick:
parse_*
are imperative verbs,serializer
is a noun.