Repository: mirumee/ariadne

Issue:
If parse_literal is not present try fallbacking to `parse_value(ast.value)`
Following idea was brought up in discussion for #24:

> Maybe we could default to calling parse_value with ast.value when only one function is provided?

This requires further study. `IntValue`, `StringValue` and friends are obvious to deal with, but but complex types like `ListValue` may require some extra unpacking magic.

Still, if it is possible to pull off, it could be an excellent convenience for developers creating custom scalars, saving the need for potentially maintaining two very simiiar implementations, one doing `isinstance(value, basestr)` and other `isinstance(value, StringValue)`.


Discussion:
Another idea: for simplicity reasons we could only unwrap basic types, and still error on anything else, forcing the dev to handle more complex types (if they wish to do so).

Resolving patch:
diff --git a/ariadne/scalars.py b/ariadne/scalars.py
--- a/ariadne/scalars.py
+++ b/ariadne/scalars.py
@@ -1,5 +1,11 @@
 from typing import Optional, cast
 
+from graphql.language.ast import (
+    BooleanValueNode,
+    FloatValueNode,
+    IntValueNode,
+    StringValueNode,
+)
 from graphql.type import (
     GraphQLNamedType,
     GraphQLScalarLiteralParser,
@@ -36,6 +42,8 @@ def set_serializer(self, f: GraphQLScalarSerializer) -> GraphQLScalarSerializer:
 
     def set_value_parser(self, f: GraphQLScalarValueParser) -> GraphQLScalarValueParser:
         self._parse_value = f
+        if not self._parse_literal:
+            self._parse_literal = create_default_literal_parser(f)
         return f
 
     def set_literal_parser(
@@ -70,3 +78,15 @@ def validate_graphql_type(self, graphql_type: Optional[GraphQLNamedType]) -> Non
                 "%s is defined in the schema, but it is instance of %s (expected %s)"
                 % (self.name, type(graphql_type).__name__, GraphQLScalarType.__name__)
             )
+
+
+SCALAR_AST_NODES = (BooleanValueNode, FloatValueNode, IntValueNode, StringValueNode)
+
+
+def create_default_literal_parser(
+    value_parser: GraphQLScalarValueParser
+) -> GraphQLScalarLiteralParser:
+    def default_literal_parser(ast):
+        return value_parser(ast.value)
+
+    return default_literal_parser


Test patch:
diff --git a/tests/test_custom_scalars.py b/tests/test_custom_scalars.py
--- a/tests/test_custom_scalars.py
+++ b/tests/test_custom_scalars.py
@@ -12,10 +12,12 @@
 type_defs = """
     scalar DateReadOnly
     scalar DateInput
+    scalar ScalarWithDefaultParser
 
     type Query {
         testSerialize: DateReadOnly!
         testInput(value: DateInput!): Boolean!
+        testInputValueType(value: ScalarWithDefaultParser!): String!
     }
 """
 
@@ -33,6 +35,11 @@ def resolve_test_input(*_, value):
     return True
 
 
+@query.field("testInputValueType")
+def resolve_test_input_type(*_, value):
+    return value
+
+
 datereadonly = ScalarType("DateReadOnly")
 
 
@@ -60,7 +67,21 @@ def parse_date_literal(ast):
     return parsed_datetime.date()
 
 
-schema = make_executable_schema(type_defs, [query, datereadonly, dateinput])
+scalar_with_default_parser = ScalarType("ScalarWithDefaultParser")
+
+
+@scalar_with_default_parser.value_parser
+def parse_value_from_default_literal_parser(value):
+    print(value)
+    if not isinstance(value, (str, bool)):
+        raise ValueError()
+
+    return type(value).__name__
+
+
+schema = make_executable_schema(
+    type_defs, [query, datereadonly, dateinput, scalar_with_default_parser]
+)
 
 
 def test_attempt_bind_scalar_to_undefined_type_raises_error():
@@ -146,39 +167,39 @@ def test_attempt_deserialize_wrong_type_variable_raises_error():
 
 def test_scalar_serializer_can_be_set_on_initialization():
     schema = build_schema(type_defs)
-    scalar = ScalarType("DateReadOnly", serializer=serialize_date)
+    scalar = ScalarType("DateInput", serializer=serialize_date)
     scalar.bind_to_schema(schema)
 
-    schema_scalar = schema.type_map.get("DateReadOnly")
+    schema_scalar = schema.type_map.get("DateInput")
     assert schema_scalar.serialize is serialize_date
 
 
 def test_scalar_serializer_can_be_set_with_setter():
     schema = build_schema(type_defs)
-    scalar = ScalarType("DateReadOnly")
+    scalar = ScalarType("DateInput")
     scalar.set_serializer(serialize_date)
     scalar.bind_to_schema(schema)
 
-    schema_scalar = schema.type_map.get("DateReadOnly")
+    schema_scalar = schema.type_map.get("DateInput")
     assert schema_scalar.serialize is serialize_date
 
 
 def test_scalar_value_parser_can_be_set_on_initialization():
     schema = build_schema(type_defs)
-    scalar = ScalarType("DateReadOnly", value_parser=parse_date_str)
+    scalar = ScalarType("DateInput", value_parser=parse_date_str)
     scalar.bind_to_schema(schema)
 
-    schema_scalar = schema.type_map.get("DateReadOnly")
+    schema_scalar = schema.type_map.get("DateInput")
     assert schema_scalar.parse_value is parse_date_str
 
 
 def test_scalar_value_parser_can_be_set_with_setter():
     schema = build_schema(type_defs)
-    scalar = ScalarType("DateReadOnly")
+    scalar = ScalarType("DateInput")
     scalar.set_value_parser(parse_date_str)
     scalar.bind_to_schema(schema)
 
-    schema_scalar = schema.type_map.get("DateReadOnly")
+    schema_scalar = schema.type_map.get("DateInput")
     assert schema_scalar.parse_value is parse_date_str
 
 
@@ -199,3 +220,56 @@ def test_scalar_literal_parser_can_be_set_with_setter():
 
     schema_scalar = schema.type_map.get("DateInput")
     assert schema_scalar.parse_literal is parse_date_literal
+
+
+def test_setting_scalar_value_parser_sets_default_literal_parsers_if_none_is_set():
+    schema = build_schema(type_defs)
+    scalar = ScalarType("DateInput")
+    scalar.set_value_parser(parse_date_str)
+    scalar.bind_to_schema(schema)
+
+    schema_scalar = schema.type_map.get("DateInput")
+    assert schema_scalar.parse_value is parse_date_str
+    assert schema_scalar.parse_literal
+
+
+def test_setting_scalar_value_parser_doesnt_override_already_set_literal_parser():
+    schema = build_schema(type_defs)
+    scalar = ScalarType("DateInput")
+    scalar.set_literal_parser(parse_date_literal)
+    scalar.set_value_parser(parse_date_str)
+    scalar.bind_to_schema(schema)
+
+    schema_scalar = schema.type_map.get("DateInput")
+    assert schema_scalar.parse_value is parse_date_str
+    assert schema_scalar.parse_literal is parse_date_literal
+
+
+def test_literal_string_is_deserialized_by_default_parser():
+    result = graphql_sync(schema, '{ testInputValueType(value: "test") }')
+    assert result.errors is None
+    assert result.data == {"testInputValueType": "str"}
+
+
+def test_literal_int_is_deserialized_by_default_parser():
+    result = graphql_sync(schema, "{ testInputValueType(value: 123) }")
+    assert result.errors is None
+    assert result.data == {"testInputValueType": "str"}
+
+
+def test_literal_float_is_deserialized_by_default_parser():
+    result = graphql_sync(schema, "{ testInputValueType(value: 1.5) }")
+    assert result.errors is None
+    assert result.data == {"testInputValueType": "str"}
+
+
+def test_literal_bool_true_is_deserialized_by_default_parser():
+    result = graphql_sync(schema, "{ testInputValueType(value: true) }")
+    assert result.errors is None
+    assert result.data == {"testInputValueType": "bool"}
+
+
+def test_literal_bool_false_is_deserialized_by_default_parser():
+    result = graphql_sync(schema, "{ testInputValueType(value: false) }")
+    assert result.errors is None
+    assert result.data == {"testInputValueType": "bool"}