Repository: tartiflette/tartiflette (python)

Issue:
Reliable way to retrieve argument a directive is applied to
## Issue
There's currently no reliable way to retrieve the name of the argument a directive is applied to on directives methods.
For `on_argument_execution`, `argument_node` is available, but when a default value is provided in the schema and no value is passed at execution time, `argument_node` is `None`.
For `on_post_input_coercion`, there's simply no way of retrieving the name of the argument.

For example, considering this schema:
```graphql
directive @myValidator on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION

input FooBarInput {
  bar: String @myValidator
}

type Query {
  foo(bar: Int = 1 @myValidator): String
}

type Mutation {
  foobar(input: FooBarInput!): Boolean
}
```

With this query:
```graphql
{
  foo
}
```

When the directive is applied, `argument_node` of `on_argument_execution` will return `None`, as the method argument is only populated if a value has been provided in the query.

With this query:
```graphql
{
  mutation FooBar($input: FooBarInput!) {
    foobar(input: $input)
  }
}
```

When the directive is applied, there's no way, in `on_post_input_coercion`, to precisely know the name of the argument the directive was applied to.

## Proposition
- First of all, `argument_node` from `on_argument_execution` should be marked as optional in the method's signature, as it is not populated in every case
- Provide a way to reliably be able to retrieve the information related to the argument the directive is applied to by passing `InputValueDefinition` to both methods

Pull request description:
ISSUE-365 - Forward InputValueDefinitionNode to on_argument_execution hook
Closes #365.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8f2829d..b30cbaf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
 ## [Released]
 
 - [1.x.x]
+  - [1.2.x]
+    - [1.2.0](./changelogs/1.2.0.md) - 2019-04-30
   - [1.1.x]
     - [1.1.3](./changelogs/1.1.3.md) - 2020-02-18
     - [1.1.2](./changelogs/1.1.2.md) - 2020-02-05
diff --git a/changelogs/1.2.0.md b/changelogs/1.2.0.md
new file mode 100644
index 0000000..1d83cab
--- /dev/null
+++ b/changelogs/1.2.0.md
@@ -0,0 +1,63 @@
+# [1.2.0] - 2020-04-30
+
+## Added
+
+- [ISSUE-363](https://github.com/dailymotion/tartiflette/issues/363) - Add an optional `query_cache_decorator` argument at engine initialisation allowing to forward a custom decorator to use to cache query parsing.
+- [ISSUE-362](https://github.com/dailymotion/tartiflette/issues/362) - Add an optional `json_loader` argument to engine creation APIs so json loader can be customized.
+- [ISSUE-361](https://github.com/dailymotion/tartiflette/issues/361) - Add an optional `custom_default_arguments_coercer` argument at engine initialisation to override the callable used to coerce arguments.
+- [ISSUE-361](https://github.com/dailymotion/tartiflette/issues/361) - Add an optional `arguments_coercer` to `@Directive`, `@Subscription` & `@Resolver` decorator to override the callable used to coerce arguments on the decorated directive/field.
+
+## Changed
+
+- [ISSUE-356](https://github.com/dailymotion/tartiflette/issues/362) - Removed dependencies on `flex` and `bison` for installing Tartiflette. `cmake` is still necessary.
+- [ISSUE-361](https://github.com/dailymotion/tartiflette/issues/361) - Coerce lists (input, literal, output) synchronously to avoid creation of too many `asyncio` tasks.
+- [ISSUE-365](https://github.com/dailymotion/tartiflette/issues/365) - Forward the `InputValueDefinitionNode` to the `on_argument_execution` hook.
+  > Note: this brings a break changes from previous versions, to upgrade to
+  > this version you'll have to update your `on_argument_execution` methods:
+  ````patch
+  @Directive("MyDirective")
+  class MyDirective:
+      async def on_argument_execution(
+          self,
+          directive_args: Dict[str, Any],
+          next_directive: Callable,
+          parent_node: Union["FieldNode", "DirectiveNode"],
+  +       argument_definition_node: "InputValueDefinitionNode",
+  -       argument_node: "ArgumentNode",
+  +       argument_node: Optional["ArgumentNode"],
+          value: Any,
+          ctx: Optional[Any],
+      ) -> Any:
+          # Write your business logic here
+  -       return next_directive(parent_node, argument_node, value, ctx)
+  +       return next_directive(parent_node, argument_definition_node, argument_node, value, ctx)
+  ````
+
+## Fixed
+
+- [ISSUE-370](https://github.com/dailymotion/tartiflette/issues/370) - Fix EnumValue uniqueness in schema definition validation rule. It should now throw the correct error in the correct case.
+
+    ```graphql
+    enum anEnum {
+        A
+        A
+        B
+    }
+    ```
+
+    Will throw a `GraphQLSchemaError` exception at `engine` build time. You can't have duplicates values.
+
+    But now:
+    ```graphql
+    type X {
+        afield:String
+    }
+
+    enum anEnum {
+        Value1
+        X
+    }
+    ```
+    Doesn't throw a `GraphQLSchemaError` for the use of `X` as an `EnumValue`. This was a buggy schema error detection
+- [ISSUE-372](https://github.com/dailymotion/tartiflette/issues/372) - Fix SDL Validation, Now `ObjectFollowInterface` validator validate field arguments and allows for field type to be `covariant` of the interface defined type.
+- Typing on the documentation related to the `argument_node` argument on the `on_argument_execution` directive hook.
diff --git a/changelogs/next.md b/changelogs/next.md
index 94723af..f13a4ff 100644
--- a/changelogs/next.md
+++ b/changelogs/next.md
@@ -1,41 +1,1 @@
 # [Next]
-
-## Added
-
-- [ISSUE-363](https://github.com/dailymotion/tartiflette/issues/363) - Add an optional `query_cache_decorator` argument at engine initialisation allowing to forward a custom decorator to use to cache query parsing.
-- [ISSUE-362](https://github.com/dailymotion/tartiflette/issues/362) - Add an optional `json_loader` argument to engine creation APIs so json loader can be customized.
-- [ISSUE-361](https://github.com/dailymotion/tartiflette/issues/361) - Add an optional `custom_default_arguments_coercer` argument at engine initialisation to override the callable used to coerce arguments.
-- [ISSUE-361](https://github.com/dailymotion/tartiflette/issues/361) - Add an optional `arguments_coercer` to `@Directive`, `@Subscription` & `@Resolver` decorator to override the callable used to coerce arguments on the decorated directive/field.
-
-## Changed
-
-- [ISSUE-356](https://github.com/dailymotion/tartiflette/issues/362) - Removed dependancies on `flex` and `bison` for installing Tartiflette. `cmake` is still necessary.
-- [ISSUE-361](https://github.com/dailymotion/tartiflette/issues/361) - Coerce lists (input, literal, output) synchronously to avoid creation of too many `asyncio` tasks.
-
-## Fixed
-
-- [ISSUE-370](https://github.com/dailymotion/tartiflette/issues/370) - Fix EnumValue uniqueness in schema definition validation rule. It should now throw the correct error in the correct case.
-
-    ```graphql
-    enum anEnum {
-        A
-        A
-        B
-    }
-    ```
-
-    Will throw a `GraphQLSchemaError` exception at `engine` build time. You can't have duplicates values.
-
-    But now:
-    ```graphql
-    type X {
-        afield:String
-    }
-
-    enum anEnum {
-        Value1
-        X
-    }
-    ```
-    Doesn't throw a `GraphQLSchemaError` for the use of `X` as an `EnumValue`. This was a buggy schema error detection
-- [ISSUE-372](https://github.com/dailymotion/tartiflette/issues/372) - Fix SDL Validation, Now `ObjectFollowInterface` validator validate field arguments and allows for field type to be `covariant` of the interface defined type.
diff --git a/docs/api/directive.md b/docs/api/directive.md
index d69ce17..cb1d194 100644
--- a/docs/api/directive.md
+++ b/docs/api/directive.md
@@ -44,14 +44,17 @@ class MyDirective:
         directive_args: Dict[str, Any],
         next_directive: Callable,
         parent_node: Union["FieldNode", "DirectiveNode"],
-        argument_node: "ArgumentNode",
+        argument_definition_node: "InputValueDefinitionNode",
+        argument_node: Optional["ArgumentNode"],
         value: Any,
         ctx: Optional[Any],
     ) -> Any:
         ######################
         # Add your code here #
         ######################
-        return await next_directive(parent_node, argument_node, value, ctx)
+        return await next_directive(
+            parent_node, argument_definition_node, argument_node, value, ctx
+        )
 
     async def on_post_input_coercion(
         self,
diff --git a/docs/migration-guides/migration-guide-0-0-to-1-0.md b/docs/migration-guides/migration-guide-0-0-to-1-0.md
index 3464c3b..176c3a0 100644
--- a/docs/migration-guides/migration-guide-0-0-to-1-0.md
+++ b/docs/migration-guides/migration-guide-0-0-to-1-0.md
@@ -256,7 +256,7 @@ class MyDirective:
 -       ctx: Optional[Any],
 -       info: "Info",
 +       parent_node: Union["FieldNode", "DirectiveNode"],
-+       argument_node: "ArgumentNode",
++       argument_node: Optional["ArgumentNode"],
 +       value: Any,
 +       ctx: Optional[Any],
     ) -> Any:
diff --git a/docs/tutorial/extend-with-directives.md b/docs/tutorial/extend-with-directives.md
index 99b3fbd..09cd0f1 100644
--- a/docs/tutorial/extend-with-directives.md
+++ b/docs/tutorial/extend-with-directives.md
@@ -54,14 +54,17 @@ class RateLimiting:
         directive_args: Dict[str, Any],
         next_directive: Callable,
         parent_node: Union["FieldNode", "DirectiveNode"],
-        argument_node: "ArgumentNode",
+        argument_definition_node: "InputValueDefinitionNode",
+        argument_node: Optional["ArgumentNode"],
         value: Any,
         ctx: Optional[Any],
     ) -> Any:
         ######################
         # Add your code here #
         ######################
-        return await next_directive(parent_node, argument_node, value, ctx)
+        return await next_directive(
+            parent_node, argument_definition_node, argument_node, value, ctx
+        )
 
     async def on_post_input_coercion(
         self,
diff --git a/setup.py b/setup.py
index 5e88066..3e8a49c 100644
--- a/setup.py
+++ b/setup.py
@@ -60,7 +60,7 @@ _TEST_REQUIRE = [
 
 _BENCHMARK_REQUIRE = ["pytest-benchmark==3.2.3"]
 
-_VERSION = "1.1.3"
+_VERSION = "1.2.0"
 
 _PACKAGES = find_packages(exclude=["tests*"])
 
diff --git a/tartiflette/coercers/argument.py b/tartiflette/coercers/argument.py
index e09f210..ed18797 100644
--- a/tartiflette/coercers/argument.py
+++ b/tartiflette/coercers/argument.py
@@ -118,5 +118,10 @@ async def argument_coercer(
         return coercion_result
 
     return await directives(
-        node, argument_node, value, ctx, context_coercer=ctx
+        node,
+        argument_definition.definition,
+        argument_node,
+        value,
+        ctx,
+        context_coercer=ctx,
     )
diff --git a/tartiflette/utils/directives.py b/tartiflette/utils/directives.py
index 47c65b1..ffe0561 100644
--- a/tartiflette/utils/directives.py
+++ b/tartiflette/utils/directives.py
@@ -83,7 +83,8 @@ async def introspection_directives_executor(
 
 async def default_argument_execution_directive(
     parent_node: Union["FieldNode", "DirectiveNode"],
-    argument_node: "ArgumentNode",
+    argument_definition_node: "InputValueDefinitionNode",
+    argument_node: Optional["ArgumentNode"],
     value: Any,
     *args,
     **kwargs,
@@ -92,9 +93,11 @@ async def default_argument_execution_directive(
     Default callable to use to wrap with directives on `on_argument_execution`
     hook name.
     :param parent_node: the parent AST node related to the executed argument
+    :param argument_definition_node: the input value definition AST node
     :param argument_node: the AST argument node executed
     :param value: the coerced value of the argument
     :type parent_node: Union[FieldNode, DirectiveNode]
+    :type argument_definition_node: InputValueDefinitionNode
     :type argument_node: ArgumentNode
     :type value: Any
     :return: the coerced value of the argument


Test patch:
diff --git a/tests/functional/coercers/common.py b/tests/functional/coercers/common.py
index df0f3a3..d2f3b5e 100644
--- a/tests/functional/coercers/common.py
+++ b/tests/functional/coercers/common.py
@@ -29,11 +29,14 @@ class DebugDirective:
         directive_args: Dict[str, Any],
         next_directive: Callable,
         parent_node: Union["FieldNode", "DirectiveNode"],
-        argument_node: "ArgumentNode",
+        argument_definition_node: "InputValueDefinitionNode",
+        argument_node: Optional["ArgumentNode"],
         value: Any,
         ctx: Optional[Any],
     ):
-        result = await next_directive(parent_node, argument_node, value, ctx)
+        result = await next_directive(
+            parent_node, argument_definition_node, argument_node, value, ctx
+        )
         # print("@adebug:", directive_args.get("message"))
         return result
 
@@ -56,11 +59,14 @@ class LowercaseDirective:
         directive_args: Dict[str, Any],
         next_directive: Callable,
         parent_node: Union["FieldNode", "DirectiveNode"],
-        argument_node: "ArgumentNode",
+        argument_definition_node: "InputValueDefinitionNode",
+        argument_node: Optional["ArgumentNode"],
         value: Any,
         ctx: Optional[Any],
     ):
-        result = await next_directive(parent_node, argument_node, value, ctx)
+        result = await next_directive(
+            parent_node, argument_definition_node, argument_node, value, ctx
+        )
         if isinstance(result, str):
             return result.lower()
         if isinstance(result, list):
@@ -95,11 +101,14 @@ class IncrementDirective:
         directive_args: Dict[str, Any],
         next_directive: Callable,
         parent_node: Union["FieldNode", "DirectiveNode"],
-        argument_node: "ArgumentNode",
+        argument_definition_node: "InputValueDefinitionNode",
+        argument_node: Optional["ArgumentNode"],
         value: Any,
         ctx: Optional[Any],
     ):
-        result = await next_directive(parent_node, argument_node, value, ctx)
+        result = await next_directive(
+            parent_node, argument_definition_node, argument_node, value, ctx
+        )
         if isinstance(result, (int, float)):
             return result + directive_args["step"]
         if isinstance(result, list):
@@ -138,11 +147,14 @@ class ConcatenateDirective:
         directive_args: Dict[str, Any],
         next_directive: Callable,
         parent_node: Union["FieldNode", "DirectiveNode"],
-        argument_node: "ArgumentNode",
+        argument_definition_node: "InputValueDefinitionNode",
+        argument_node: Optional["ArgumentNode"],
         value: Any,
         ctx: Optional[Any],
     ):
-        result = await next_directive(parent_node, argument_node, value, ctx)
+        result = await next_directive(
+            parent_node, argument_definition_node, argument_node, value, ctx
+        )
         return (
             result + directive_args["with"]
             if isinstance(result, str)
@@ -171,11 +183,14 @@ class MapToValueDirective:
         directive_args: Dict[str, Any],
         next_directive: Callable,
         parent_node: Union["FieldNode", "DirectiveNode"],
-        argument_node: "ArgumentNode",
+        argument_definition_node: "InputValueDefinitionNode",
+        argument_node: Optional["ArgumentNode"],
         value: Any,
         ctx: Optional[Any],
     ):
-        await next_directive(parent_node, argument_node, value, ctx)
+        await next_directive(
+            parent_node, argument_definition_node, argument_node, value, ctx
+        )
         return directive_args["newValue"]
 
     async def on_post_input_coercion(
diff --git a/tests/functional/coercers/test_coercer_arguments_errors.py b/tests/functional/coercers/test_coercer_arguments_errors.py
index 0d817ab..fd99797 100644
--- a/tests/functional/coercers/test_coercer_arguments_errors.py
+++ b/tests/functional/coercers/test_coercer_arguments_errors.py
@@ -466,6 +466,7 @@ async def ttftt_engine():
             directive_args,
             next_directive,
             parent_node,
+            argument_definition_node,
             argument_node,
             value,
             ctx,
@@ -486,6 +487,7 @@ async def ttftt_engine():
             directive_args,
             next_directive,
             parent_node,
+            argument_definition_node,
             argument_node,
             value,
             ctx,
diff --git a/tests/functional/regressions/test_directives_arguments.py b/tests/functional/regressions/test_directives_arguments.py
index 4be472e..f24607c 100644
--- a/tests/functional/regressions/test_directives_arguments.py
+++ b/tests/functional/regressions/test_directives_arguments.py
@@ -37,12 +37,17 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ):
             result = await next_directive(
-                parent_node, argument_node, value, ctx
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                value,
+                ctx,
             )
             if isinstance(result, (int, float)):
                 if result > directive_args["limit"]:
@@ -70,12 +75,17 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ):
             result = await next_directive(
-                parent_node, argument_node, value, ctx
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                value,
+                ctx,
             )
             if isinstance(result, (int, float)):
                 return result + directive_args["step"]
diff --git a/tests/functional/regressions/test_input_directives.py b/tests/functional/regressions/test_input_directives.py
index b826aad..5ba6f5a 100644
--- a/tests/functional/regressions/test_input_directives.py
+++ b/tests/functional/regressions/test_input_directives.py
@@ -81,12 +81,17 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ):
             result = await next_directive(
-                parent_node, argument_node, value, ctx
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                value,
+                ctx,
             )
             return result
 
@@ -117,7 +122,8 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ):
diff --git a/tests/functional/regressions/test_issue133.py b/tests/functional/regressions/test_issue133.py
index 156e2ff..b000ab1 100644
--- a/tests/functional/regressions/test_issue133.py
+++ b/tests/functional/regressions/test_issue133.py
@@ -66,12 +66,17 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ) -> Any:
             result = await next_directive(
-                parent_node, argument_node, value, ctx
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                value,
+                ctx,
             )
             if len(result) > directive_args["limit"]:
                 raise Exception(
@@ -113,12 +118,17 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ) -> Any:
             result = await next_directive(
-                parent_node, argument_node, value, ctx
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                value,
+                ctx,
             )
             if result not in directive_args["choices"]:
                 raise Exception(
@@ -158,11 +168,18 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ) -> Any:
-            return await next_directive(parent_node, argument_node, value, ctx)
+            return await next_directive(
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                value,
+                ctx,
+            )
 
         async def on_post_input_coercion(
             self,
@@ -181,7 +198,8 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ) -> Any:
diff --git a/tests/functional/regressions/test_issue139.py b/tests/functional/regressions/test_issue139.py
index 75871e9..391ea91 100644
--- a/tests/functional/regressions/test_issue139.py
+++ b/tests/functional/regressions/test_issue139.py
@@ -45,13 +45,18 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ):
             limit = directive_args["limit"]
             value = await next_directive(
-                parent_node, argument_node, value, ctx
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                value,
+                ctx,
             )
             argument_length = len(value)
             if argument_length > limit:
@@ -69,13 +74,18 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ):
             choices = directive_args["choices"]
             value = await next_directive(
-                parent_node, argument_node, value, ctx
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                value,
+                ctx,
             )
             if value not in choices:
                 raise Exception(
diff --git a/tests/functional/regressions/test_issue209.py b/tests/functional/regressions/test_issue209.py
index 406f5ef..c0c3259 100644
--- a/tests/functional/regressions/test_issue209.py
+++ b/tests/functional/regressions/test_issue209.py
@@ -46,12 +46,17 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ):
             value = await next_directive(
-                parent_node, argument_node, value, ctx
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                value,
+                ctx,
             )
             if value > directive_args["limit"]:
                 raise LimitReachedException("Limit has been reached")
diff --git a/tests/functional/regressions/test_issue365.py b/tests/functional/regressions/test_issue365.py
new file mode 100644
index 0000000..715749f
--- /dev/null
+++ b/tests/functional/regressions/test_issue365.py
@@ -0,0 +1,113 @@
+import logging
+
+import pytest
+
+from tartiflette import Directive, Resolver, create_engine
+
+logger = logging.getLogger(__name__)
+
+
+@pytest.fixture(scope="module")
+async def ttftt_engine():
+    sdl = """
+    directive @appendArgumentName on ARGUMENT_DEFINITION
+    directive @prependArgumentName on ARGUMENT_DEFINITION
+
+    type Query {
+      hello(
+        identifier: String! = "Unknown" @prependArgumentName @appendArgumentName
+      ): String!
+    }
+    """
+
+    @Directive("appendArgumentName", schema_name="test_issue365")
+    class AppendArgumentNameDirective:
+        @staticmethod
+        async def on_argument_execution(
+            directive_args,
+            next_directive,
+            parent_node,
+            argument_definition_node,
+            argument_node,
+            value,
+            ctx,
+        ):
+            return await next_directive(
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                f"{value}.{argument_definition_node.name.value}",
+                ctx,
+            )
+
+    @Directive("prependArgumentName", schema_name="test_issue365")
+    class PrependArgumentNameDirective:
+        @staticmethod
+        async def on_argument_execution(
+            directive_args,
+            next_directive,
+            parent_node,
+            argument_definition_node,
+            argument_node,
+            value,
+            ctx,
+        ):
+            return await next_directive(
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                f"{argument_definition_node.name.value}.{value}",
+                ctx,
+            )
+
+    @Resolver("Query.hello", schema_name="test_issue365")
+    async def resolve_query_hello(parent, args, ctx, info):
+        return f"Hello < {args['identifier']} >"
+
+    return await create_engine(sdl, schema_name="test_issue365")
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+    "query,variables,expected",
+    [
+        (
+            """
+            query {
+              hello
+            }
+            """,
+            {},
+            {"data": {"hello": "Hello < identifier.Unknown.identifier >"}},
+        ),
+        (
+            """
+            query {
+              hello(identifier: "John")
+            }
+            """,
+            {},
+            {"data": {"hello": "Hello < identifier.John.identifier >"}},
+        ),
+        (
+            """
+            query ($identifier: String! = "Oops") {
+              hello(identifier: $identifier)
+            }
+            """,
+            {},
+            {"data": {"hello": "Hello < identifier.Oops.identifier >"}},
+        ),
+        (
+            """
+            query ($identifier: String! = "Unknown") {
+              hello(identifier: $identifier)
+            }
+            """,
+            {"identifier": "John"},
+            {"data": {"hello": "Hello < identifier.John.identifier >"}},
+        ),
+    ],
+)
+async def test_issue365(ttftt_engine, query, variables, expected):
+    assert await ttftt_engine.execute(query, variables=variables) == expected
diff --git a/tests/functional/regressions/test_oh_god.py b/tests/functional/regressions/test_oh_god.py
index 78c4e72..b308f88 100644
--- a/tests/functional/regressions/test_oh_god.py
+++ b/tests/functional/regressions/test_oh_god.py
@@ -229,12 +229,17 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ) -> Any:
             result = await next_directive(
-                parent_node, argument_node, value, ctx
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                value,
+                ctx,
             )
             if len(result) > directive_args["limit"]:
                 raise Exception(
@@ -266,12 +271,17 @@ async def ttftt_engine():
             directive_args: Dict[str, Any],
             next_directive: Callable,
             parent_node: Union["FieldNode", "DirectiveNode"],
-            argument_node: "ArgumentNode",
+            argument_definition_node: "InputValueDefinitionNode",
+            argument_node: Optional["ArgumentNode"],
             value: Any,
             ctx: Optional[Any],
         ) -> Any:
             result = await next_directive(
-                parent_node, argument_node, value, ctx
+                parent_node,
+                argument_definition_node,
+                argument_node,
+                value,
+                ctx,
             )
             return (
                 f'{result}+{directive_args["with"]}'