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

View cache busting logic fails if more than one GQL operation is provided in payload #209

Open
ethagnawl opened this issue Feb 7, 2025 · 0 comments

Comments

@ethagnawl
Copy link

ethagnawl commented Feb 7, 2025

Per the GQL spec, more than one operation may be contained in the body of a request as long as a specific operation is specified using operationName. ExtraGraphQLView#get_operation_ast returns None in this case and results in the cache not being busted when a mutation does occur.

Here is the test I used to reproduce this:

# ...
            cache.set(cache_key, "no-not-me!", timeout=Config.DJANGO_CACHE_TTL)

            cache_was_set = cache.get(cache_key)
            assert cache_was_set

            api_query = f"""
query someName {{
  foo(id:"{lib_relay_id}") {{
    id
    bar(id:"{asset_relay_id}") {{
      edges {{
        node {{
          id
        }}
      }}
    }}
  }}
}}

mutation someOtherName {{
    createThing(title: "hello") {{
        status
        qux {{
            id
            title
            description
        }}
    }}
}}
        """
            private_client.post(
                "/api/v1/graphql/",
                data={
                    "query": api_query,
                    "operationName": "someOtherName",
                },
                content_type="application/json",
            )

            cache_was_cleared = not (bool(cache.get(cache_key)))
            assert cache_was_cleared

And I introduced this new method to get a list of all operations included in the payload and bust the cache if any contained a mutation. Having just written this out, I realize this approach is greedy and the specified operationName should be used to determine whether or not that operation is a mutation. But the following does address the broken behavior, if in a heavy handed way.

    def get_operations(self, request):
        """
        Return list of all operations -- regardless of type -- found in query
        """

        data = self.parse_body(request)
        query = request.GET.get("query") or data.get("query")

        if not query:
            return []

        source = Source(query, name="GraphQL request")
        document_ast = parse(source)

        operations = [
            definition
            for definition in document_ast.definitions
            if hasattr(definition, "operation")
        ]

        for operation in operations:
            logger.debug(f"operation: {str(operation)} - {operation.operation}")

        return operations

# ...

    def dispatch(self, request, *args, **kwargs):
        operations = self.get_operations(request)
        if operations and any(op.operation == OperationType.MUTATION for op in operations):


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant