strawberry-graphql / strawberry

A GraphQL library for Python that leverages type annotations πŸ“
https://strawberry.rocks
MIT License
4.03k stars 535 forks source link

Operation directives (seemingly) always erroring #3509

Open parafoxia opened 6 months ago

parafoxia commented 6 months ago

Describe the Bug

This might be an accidental duplicate of #3125. It may also be me rather than the lib at fault as the documentation on Operation directives is very limited (already left feedback for that in the proper place).

It seems that the inclusion of a directive in...well, anything is enough to throw an error when exporting the schema.

MRE:

import strawberry
from strawberry.directive import DirectiveLocation

@strawberry.directive(locations=[DirectiveLocation.FIELD_DEFINITION])
def turn_uppercase(value: str):
    return value.upper()

@strawberry.type
class Message:
    body: str

@strawberry.type
class Query:
    @strawberry.field(directives=[turn_uppercase])
    def send_message(self) -> Message:
        return Message(body="Hello world!")

schema = strawberry.federation.Schema(
    query=Query,
    types=[Message],
    enable_federation_2=True,
)

Passing the directive to strawberry.type for Message also triggers it. There's a directives argument to Schema, but it's undocumented. It expects Sequence[Type], notably not Sequence[StrawberryDirective] -- is that in some way related to this problem?

I'm guessing by the traceback below I'm not supposed be passing a StrawberryDirective though to directives, but if not, what?

Traceback:

Traceback (most recent call last):

  File "/app/serverless-clone/node_modules/serverless-wsgi/wsgi_handler.py", line 43, in import_app
    wsgi_module = importlib.import_module(wsgi_fqn_parts[-1])
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/app/serverless-clone/server.py", line 4, in <module>
    from app import create_app
  File "/app/serverless-clone/app.py", line 6, in <module>
    from urls import auth_blueprint
  File "/app/serverless-clone/urls.py", line 2, in <module>
    from sgql import schema
  File "/app/serverless-clone/sgql.py", line 42, in <module>
    schema = strawberry.federation.Schema(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/strawberry/federation/schema.py", line 91, in __init__
    composed_directives = self._add_compose_directives()
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/strawberry/federation/schema.py", line 319, in _add_compose_directives
    definition = directive.__strawberry_directive__  # type: ignore
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'StrawberryDirective' object has no attribute '__strawberry_directive__'

System Information

Additional Context

Upvote & Fund

Fund with Polar

patrick91 commented 6 months ago

hi @parafoxia I think this is our fault in not documenting it well 😊

I made an example of operation directives: https://play.strawberry.rocks/?gist=30af63743c03d47044fe798240ca0f44

They are used when sending the operation :)

What are you trying to do exactly?

parafoxia commented 6 months ago

Ah thanks for that! I had no idea that playground existed haha.

Basically we're implementing a caching system. We're moving over from a solution on top of Ariadne to one in Strawberry, and caching was done as a directive previously. We have since stumbled across an example in the docs of caching being handled by a custom SchemaExtension -- would you say that would be the best way forward?

erikwrede commented 6 months ago

@parafoxia Depending on your desired cache granularity, using FieldExtensions for caching with a specified TTL for each field might also work well. At the other hand you can also specify a directive and read it using the schema extensions.

parafoxia commented 6 months ago

Cool, I'll have a look into all those possibilities. Thanks both of you for your responses! We're really liking Strawberry so far, can't wait for our whole system to be using it πŸ˜„

I'll leave this issue open for now in case there were any follow-ups you wanted to track using it (not sure if the docs feedback is tracked separately), but I'm happy on my end.

patrick91 commented 6 months ago

@parafoxia let us know how the migration goes (maybe on discord!) and let us know if there's anything we can help with 😊