Repository: mirumee/ariadne (python)

Issue:
Create `resolve_to` utility
Python variables are usually named using the `snake_case`, whereas majority of JS written today uses the `camelCase`. Ariadne will need to provide a way to resolve one to another.

The idea is to create `resolver_for` function taking single `name` argument and returning resolver for specified attr/key name, enabling **explicit** mapping as such:

```
resolvers_map = {
    "User": {
        "lastVisitedOn": resolve_to("last_visited_on")
    }
}
```

Other advantage of having such utility would be arbitrary field names:

```
resolvers_map = {
    "User": {
        "lastVisitedOn": resolve_to("last_visit")
    }
}
```

Pull request:
Add explicit resolver mappings
Closes #21 

Patch:
diff --git a/ariadne/__init__.py b/ariadne/__init__.py
index 67b841b..28603d8 100644
--- a/ariadne/__init__.py
+++ b/ariadne/__init__.py
@@ -1,10 +1,11 @@
 from .build_schema import build_schema_from_type_definitions
 from .executable_schema import make_executable_schema
-from .resolvers import add_resolve_functions_to_schema, default_resolver
+from .resolvers import add_resolve_functions_to_schema, default_resolver, resolve_to
 
 __all__ = [
     "add_resolve_functions_to_schema",
     "build_schema_from_type_definitions",
     "default_resolver",
     "make_executable_schema",
+    "resolve_to",
 ]
diff --git a/ariadne/resolvers.py b/ariadne/resolvers.py
index c48be6b..388e3ad 100644
--- a/ariadne/resolvers.py
+++ b/ariadne/resolvers.py
@@ -2,10 +2,21 @@ from graphql import GraphQLObjectType, GraphQLScalarType, GraphQLSchema
 from graphql.execution.base import ResolveInfo
 
 
-def default_resolver(parent, info: ResolveInfo):
+def resolve_parent_field(parent, name: str):
     if isinstance(parent, dict):
-        return parent.get(info.field_name)
-    return getattr(parent, info.field_name, None)
+        return parent.get(name)
+    return getattr(parent, name, None)
+
+
+def default_resolver(parent, info: ResolveInfo):
+    return resolve_parent_field(parent, info.field_name)
+
+
+def resolve_to(name: str):
+    def resolver(parent, *_):
+        return resolve_parent_field(parent, name)
+
+    return resolver
 
 
 def add_resolve_functions_to_schema(schema: GraphQLSchema, resolvers: dict):


Test patch:
diff --git a/tests/test_queries.py b/tests/test_queries.py
index c80be6d..cfff208 100644
--- a/tests/test_queries.py
+++ b/tests/test_queries.py
@@ -3,7 +3,7 @@ from unittest.mock import Mock
 
 from graphql import graphql
 
-from ariadne import make_executable_schema
+from ariadne import make_executable_schema, resolve_to
 
 
 def test_query_default_scalar():
@@ -172,3 +172,49 @@ def test_query_with_input():
     result = graphql(schema, "{ test(data: { value: 4 }) }")
     assert result.errors is None
     assert result.data == {"test": 42}
+
+
+def test_mapping_resolver():
+    type_defs = """
+        type Query {
+            user: User
+        }
+
+        type User {
+            firstName: String
+        }
+    """
+
+    resolvers = {
+        "Query": {"user": lambda *_: {"first_name": "Joe"}},
+        "User": {"firstName": resolve_to("first_name")},
+    }
+
+    schema = make_executable_schema(type_defs, resolvers)
+
+    result = graphql(schema, "{ user { firstName } }")
+    assert result.errors is None
+    assert result.data == {"user": {"firstName": "Joe"}}
+
+
+def test_mapping_resolver_to_object_attribute():
+    type_defs = """
+        type Query {
+            user: User
+        }
+
+        type User {
+            firstName: String
+        }
+    """
+
+    resolvers = {
+        "Query": {"user": lambda *_: Mock(first_name="Joe")},
+        "User": {"firstName": resolve_to("first_name")},
+    }
+
+    schema = make_executable_schema(type_defs, resolvers)
+
+    result = graphql(schema, "{ user { firstName } }")
+    assert result.errors is None
+    assert result.data == {"user": {"firstName": "Joe"}}