Skip to content

Commit

Permalink
Allow to disable field suggestions (#3537)
Browse files Browse the repository at this point in the history
* Allow to disable field suggestions

* Better name

* Add tweet

* Add test with two fields
  • Loading branch information
patrick91 authored Jun 10, 2024
1 parent ad1f2eb commit d615081
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 4 deletions.
17 changes: 17 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Release type: minor

This release adds a new configuration to disable field suggestions in the error
response.

```python
@strawberry.type
class Query:
name: str


schema = strawberry.Schema(
query=Query, config=StrawberryConfig(disable_field_suggestions=True)
)
```

Trying to query `{ nam }` will not suggest to query `name` instead.
6 changes: 6 additions & 0 deletions TWEET.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
🆕 Release $version is out! Thanks to $contributor for the PR 👏

This release allows to disable field suggestions when sending an operation with
the wrong field.

Get it here 👉 $release_url
76 changes: 74 additions & 2 deletions docs/types/schema-configurations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ title: Schema Configurations
# Schema Configurations

Strawberry allows to customise how the schema is generated by passing
configurations. At the moment we only allow to disable auto camel casing of
fields and arguments names.
configurations.

To customise the schema you can create an instance of `StrawberryConfig`, as
shown in the example below:
Expand All @@ -33,3 +32,76 @@ type Query {
example_field: String!
}
```

## Available configurations

Here's a list of the available configurations:

### auto_camel_case

By default Strawberry will convert the field names to camel case, so a field
like `example_field` will be converted to `exampleField`. You can disable this
feature by setting `auto_camel_case` to `False`.

```python
schema = strawberry.Schema(query=Query, config=StrawberryConfig(auto_camel_case=False))
```

### default_resolver

By default Strawberry will use the `getattr` function as the default resolver.
You can customise this by setting the `default_resolver` configuration.

This can be useful in cases you want to allow returning a dictionary from a
resolver.

```python
import strawberry

from strawberry.schema.config import StrawberryConfig


def custom_resolver(obj, field):
try:
return obj[field]
except (KeyError, TypeError):
return getattr(obj, field)


@strawberry.type
class User:
name: str


@strawberry.type
class Query:
@strawberry.field
def user(self, info) -> User: # this won't type check, but will work at runtime
return {"name": "Patrick"}


schema = strawberry.Schema(
query=Query, config=StrawberryConfig(default_resolver=custom_resolver)
)
```

### relay_max_results

By default Strawberry's max limit for relay connections is 100. You can
customise this by setting the `relay_max_results` configuration.

```python
schema = strawberry.Schema(query=Query, config=StrawberryConfig(relay_max_results=50))
```

### disable_field_suggestions

By default Strawberry will suggest fields when a field is not found in the
schema. You can disable this feature by setting `disable_field_suggestions` to
`True`.

```python
schema = strawberry.Schema(
query=Query, config=StrawberryConfig(disable_field_suggestions=True)
)
```
19 changes: 19 additions & 0 deletions strawberry/schema/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,25 @@ def get_directive_by_name(self, graphql_name: str) -> Optional[StrawberryDirecti
def as_str(self) -> str:
raise NotImplementedError

@staticmethod
def remove_field_suggestion(error: GraphQLError) -> None:
if (
error.message.startswith("Cannot query field")
and "Did you mean" in error.message
):
error.message = error.message.split("Did you mean")[0].strip()

def _process_errors(
self,
errors: List[GraphQLError],
execution_context: Optional[ExecutionContext] = None,
) -> None:
if self.config.disable_field_suggestions:
for error in errors:
self.remove_field_suggestion(error)

self.process_errors(errors, execution_context)

def process_errors(
self,
errors: List[GraphQLError],
Expand Down
1 change: 1 addition & 0 deletions strawberry/schema/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class StrawberryConfig:
name_converter: NameConverter = field(default_factory=NameConverter)
default_resolver: Callable[[Any, str], object] = getattr
relay_max_results: int = 100
disable_field_suggestions: bool = False

def __post_init__(
self,
Expand Down
4 changes: 2 additions & 2 deletions strawberry/schema/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ async def execute(
execution_context_class=self.execution_context_class,
execution_context=execution_context,
allowed_operation_types=allowed_operation_types,
process_errors=self.process_errors,
process_errors=self._process_errors,
)

return result
Expand Down Expand Up @@ -301,7 +301,7 @@ def execute_sync(
execution_context_class=self.execution_context_class,
execution_context=execution_context,
allowed_operation_types=allowed_operation_types,
process_errors=self.process_errors,
process_errors=self._process_errors,
)

return result
Expand Down
68 changes: 68 additions & 0 deletions tests/schema/test_execution_errors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest

import strawberry
from strawberry.schema.config import StrawberryConfig


def test_runs_parsing():
Expand Down Expand Up @@ -64,3 +65,70 @@ class Query:

assert len(result.errors) == 1
assert result.errors[0].message == "Syntax Error: Expected Name, found <EOF>."


def test_suggests_fields_by_default():
@strawberry.type
class Query:
name: str

schema = strawberry.Schema(query=Query)

query = """
query {
ample
}
"""

result = schema.execute_sync(query)

assert len(result.errors) == 1
assert (
result.errors[0].message
== "Cannot query field 'ample' on type 'Query'. Did you mean 'name'?"
)


def test_can_disable_field_suggestions():
@strawberry.type
class Query:
name: str

schema = strawberry.Schema(
query=Query, config=StrawberryConfig(disable_field_suggestions=True)
)

query = """
query {
ample
}
"""

result = schema.execute_sync(query)

assert len(result.errors) == 1
assert result.errors[0].message == "Cannot query field 'ample' on type 'Query'."


def test_can_disable_field_suggestions_multiple_fields():
@strawberry.type
class Query:
name: str
age: str

schema = strawberry.Schema(
query=Query, config=StrawberryConfig(disable_field_suggestions=True)
)

query = """
query {
ample
ag
}
"""

result = schema.execute_sync(query)

assert len(result.errors) == 2
assert result.errors[0].message == "Cannot query field 'ample' on type 'Query'."
assert result.errors[1].message == "Cannot query field 'ag' on type 'Query'."

0 comments on commit d615081

Please sign in to comment.