Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix interfaces with duplicate directives #3674

Merged
merged 12 commits into from
Oct 21, 2024
4 changes: 4 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Release type: patch

This pull request addresses a bug where directives were being added multiple times, causing VSCode errors.
The fix involves deduplicating directives when applying extensions/permissions to a field, ensuring that each directive is only added once.
bellini666 marked this conversation as resolved.
Show resolved Hide resolved
16 changes: 12 additions & 4 deletions strawberry/permission.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,19 @@ def __init__(
self.use_directives = use_directives

def apply(self, field: StrawberryField) -> None:
"""Applies all of the permission directives to the schema and sets up silent permissions."""
"""Applies all of the permission directives (deduped) to the schema and sets up silent permissions."""
if self.use_directives:
field.directives.extend(
p.schema_directive for p in self.permissions if p.schema_directive
)
permission_directives = [
perm.schema_directive
for perm in self.permissions
if perm.schema_directive
]
# Iteration, because we want to keep order
for perm_directive in permission_directives:
# Dedupe multiple directives
if perm_directive in field.directives:
continue
field.directives.append(perm_directive)
# We can only fail silently if the field is optional or a list
if self.fail_silently:
if isinstance(field.type, StrawberryOptional):
Expand Down
65 changes: 64 additions & 1 deletion tests/test_printer/test_schema_directives.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import textwrap
from enum import Enum
from typing import List, Optional, Union
from typing import Any, List, Optional, Union
from typing_extensions import Annotated

import strawberry
from strawberry import BasePermission, Info
from strawberry.permission import PermissionExtension
from strawberry.printer import print_schema
from strawberry.schema.config import StrawberryConfig
from strawberry.schema_directive import Location
Expand Down Expand Up @@ -532,6 +534,67 @@
assert print_schema(schema) == textwrap.dedent(expected_output).strip()


def test_dedupe_multiple_equal_directives():
class MemberRoleRequired(BasePermission):
message = "Keine Rechte"

def has_permission(self, source, info: Info, **kwargs: Any) -> bool:
return True

Check warning on line 542 in tests/test_printer/test_schema_directives.py

View check run for this annotation

Codecov / codecov/patch

tests/test_printer/test_schema_directives.py#L542

Added line #L542 was not covered by tests

@strawberry.interface
class MyInterface:
id: strawberry.ID

@strawberry.field(
extensions=[PermissionExtension(permissions=[MemberRoleRequired()])]
)
def hello(info: Info) -> str:
return "world"

Check warning on line 552 in tests/test_printer/test_schema_directives.py

View check run for this annotation

Codecov / codecov/patch

tests/test_printer/test_schema_directives.py#L552

Added line #L552 was not covered by tests

@strawberry.type
class MyType1(MyInterface):
name: str

@strawberry.type
class MyType2(MyInterface):
age: int

@strawberry.type
class Query:
@strawberry.field
def my_type(info: Info) -> MyInterface:
return MyType1(id="1", name="Hello")

Check warning on line 566 in tests/test_printer/test_schema_directives.py

View check run for this annotation

Codecov / codecov/patch

tests/test_printer/test_schema_directives.py#L566

Added line #L566 was not covered by tests

expected_output = """
directive @memberRoleRequired on FIELD_DEFINITION

interface MyInterface {
id: ID!
hello: String! @memberRoleRequired
}

type MyType1 implements MyInterface {
id: ID!
hello: String! @memberRoleRequired
name: String!
}

type MyType2 implements MyInterface {
id: ID!
hello: String! @memberRoleRequired
age: Int!
}

type Query {
myType: MyInterface!
}
"""

schema = strawberry.Schema(Query, types=[MyType1, MyType2])

assert print_schema(schema) == textwrap.dedent(expected_output).strip()


def test_print_directive_on_union():
@strawberry.type
class A:
Expand Down
Loading