Repository: mirumee/ariadne

Issue:
Query filter with default `null` filter causes error
**GraphQL Schema:**

```gql
Query {
    """Get sensors with filtering"""
    sensors(filters: SensorFilters = null): [Sensor!]!
}
```

**Expected Behavior:**

Schema is built with no errors. This happens in Ariadne 0.13.0.

**Actual Behavior:**

In Ariadne >= 0.14.0 (currently running 0.19.1), the following error is raised when running the `make_executable_schema()` method:

```python
ImportError while loading conftest '/Users/*****/Code/mycode/tests/conftest.py'.
tests/conftest.py:21: in <module>
    from src import (
mycode/src/graphql/__init__.py:103: in <module>
    schema = make_executable_schema(type_defs, types)
venv/lib/python3.9/site-packages/ariadne/executable_schema.py:354: in make_executable_schema
    validate_schema_enum_values(schema)
venv/lib/python3.9/site-packages/ariadne/enums.py:239: in validate_schema_enum_values
    for type_name, field_name, arg, _ in find_enum_values_in_schema(schema):
venv/lib/python3.9/site-packages/ariadne/enums.py:260: in find_enum_values_in_schema
    yield from result
venv/lib/python3.9/site-packages/ariadne/enums.py:278: in enum_values_in_object_type
    yield from enum_values_in_field_args(field_name, field)
venv/lib/python3.9/site-packages/ariadne/enums.py:301: in enum_values_in_field_args
    yield from _get_field_with_keys(field_name, args)
venv/lib/python3.9/site-packages/ariadne/enums.py:312: in _get_field_with_keys
    routes = get_enum_keys_from_ast(field.ast_node)
venv/lib/python3.9/site-packages/ariadne/enums.py:320: in get_enum_keys_from_ast
    nodes = [([field.name.value], field) for field in object_node.fields]
E   AttributeError: 'NullValueNode' object has no attribute 'fields'
```

**Workaround:**

If I remove the default `null` value from the query filter, this works as expected, but removing the default value is not desired.

**Solution:**

Checking the `default_value` to ensure it is an instance of `ObjectValueNode` (which is expected by `get_enum_keys_from_ast`) resolves this issue.

```diff
diff --git a/ariadne/enums.py b/ariadne/enums.py
index 05e0ccd..544127a 100644
--- a/ariadne/enums.py
+++ b/ariadne/enums.py
@@ -308,7 +308,11 @@ def _get_field_with_keys(field_name, fields):
             yield field_name, input_name, field, None
 
         if isinstance(resolved_type, GraphQLInputObjectType):
-            if field.ast_node is not None and field.ast_node.default_value is not None:
+            if (
+                field.ast_node is not None
+                and field.ast_node.default_value is not None
+                and isinstance(field.ast_node.default_value, ObjectValueNode)
+            ):
                 routes = get_enum_keys_from_ast(field.ast_node)
                 for route in routes:
                     yield field_name, input_name, field, route
```






Discussion:
I'm curious to know why removing the default value as null is not desired. Based on my understanding there is a difference between stating "null" for query fields and not providing any default value. Stating "null" means that the field will always be present in the response, even if the field has no value. Not providing a default value means that the field will only be included in the response if there is a value for that field.
So removing the default value of a GraphQL query from "null" to nothing can be a good optimization strategy for improving the performance and efficiency of a GraphQL API. Having raised this issue has made me think through this, I might be missing something here...
@SamWanekeya,

Please feel free to show me where I'm wrong, as I am relatively new to GraphQL. Given the following schema, I'm not aware that the `= null` has any bearing on the returned fields.

```graphql
scalar UUID
scalar Timestamp

type MyDataType {
  id: UUID!
  creation_time: Timestamp!
  last_updated_time: Timestamp!
  duration: Int
}

input MyDataTypeFilterInput {
  creation_time_begin: Timestamp = null
  creation_time_end: Timestamp = null
  update_time_begin: Timestamp = null
  update_time_end: Timestamp = null
}

Query {
  data_type(id: UUID!): MyDataType
  filtered_data_type(filters: MyDataTypeFilterInput = null): [MyDataType!]
}
```
Both the `(filters: MyDataTypeFilterInput = null)` and the `= null` in the `MyDataTypeFilterInput` cause this error. The only way to avoid it is to remove the default value for `null` or add a check like in the original question.

To my knowledge the filters input having a default null value does not impact the returned fields.
You are correct that in the schema you provided, setting the default value of the `MyDataTypeFilterInput` fields to `null` will not have any impact on the returned fields. 

In this case, setting the default value to `null` is simply providing a default value for the input arguments of the `filtered_data_type` query. If no value is provided for the `filters` argument when the query is executed, it will default to `null`. 

However, as you pointed out, setting the default value of `filters` to `null` in this schema is causing an error. This is because the `MyDataTypeFilterInput` object has required fields (`creation_time_begin`, `creation_time_end`, `update_time_begin`, and `update_time_end`) that must have values provided for them. Setting the default value of `null` for these fields means that they will still be required, but they will default to `null` if no value is provided.

To fix this issue, you could either remove the default value of `null` for the fields in `MyDataTypeFilterInput`, or you could update the resolver function for the `filtered_data_type` query to check if the `filters` argument is `null` and provide default values for the required fields if it is. 

In short, the default value of `null` for input fields in GraphQL can be useful for providing default values for optional input arguments, but it's important to ensure that required fields are not inadvertently set to `null` by default, as this can cause errors in the execution of queries.
Change the sql_mode setting: You can remove the ONLY_FULL_GROUP_BY option from the sql_mode setting, which will allow queries with default null filters to be executed without error. You can change the sql_mode setting using the following SQL command: SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));

Note that changing the sql_mode setting may have other implications for your database queries, so it's important to test your queries thoroughly after making this change.

Use a different default value: If possible, you can change the default value of the field in question to something other than null, which will allow queries with default filters to be executed without error.

Use a different filter approach: Instead of using a default null filter, you can use a different filter approach that does not rely on default values. For example, you can use a conditional statement in your query to check if the filter value is null and handle the query differently in that case.

Update your query syntax: Depending on the specifics of your query and database setup, you may be able to resolve the error by updating the syntax of your query to avoid the use of a default null filter. This may involve restructuring your query or using a different type of filter.


Resolving patch:
diff --git a/ariadne/enums.py b/ariadne/enums.py
--- a/ariadne/enums.py
+++ b/ariadne/enums.py
@@ -308,7 +308,11 @@ def _get_field_with_keys(field_name, fields):
             yield field_name, input_name, field, None
 
         if isinstance(resolved_type, GraphQLInputObjectType):
-            if field.ast_node is not None and field.ast_node.default_value is not None:
+            if (
+                field.ast_node is not None
+                and field.ast_node.default_value is not None
+                and isinstance(field.ast_node.default_value, ObjectValueNode)
+            ):
                 routes = get_enum_keys_from_ast(field.ast_node)
                 for route in routes:
                     yield field_name, input_name, field, route


Test patch:
diff --git a/tests/test_enums.py b/tests/test_enums.py
--- a/tests/test_enums.py
+++ b/tests/test_enums.py
@@ -515,3 +515,18 @@ class UnknownEnum(str, Enum):
 
     with pytest.raises(ValueError):
         make_executable_schema([enum_definition, enum_field], query, UnknownEnum)
+
+
+def test_schema_enum_values_fixer_handles_null_input_default():
+    #  regression test for: https://github.com/mirumee/ariadne/issues/1074
+    make_executable_schema(
+        """
+        input SearchInput {
+          name: String!
+        }
+    
+        type Query {
+          search(filters: SearchInput = null): String
+        }
+        """
+    )