rmosolgo/graphql-ruby

[PRO] Can't get custom directive from a document retrieved from the operation store

alexeyr-ci opened this issue · 3 comments

Describe the bug

I've added a custom directive which applies to queries. When I get a document for a query which has this directive, I can't obtain the directive (and its arguments) from the document.

Versions

graphql version: 1.13.23
graphql-pro: 1.25.2
rails: 7.0.8.1

GraphQL schema

class GraphDirective::Cache < GraphQL::Schema::Directive
  argument :max_age, Integer, required: false, description: 'Cache expiration in seconds'
  locations QUERY
end
class ApplicationSchema < GraphQL::Schema
  directive(GraphDirective::Cache)
  ...
end

GraphQL query

Example GraphQL query and response (if query execution is involved)

query menuItemReviews ($menuItemId: Int, $pagination: PaginationInput, $approved: Boolean) @cache { ... }
{
  "data": {
    "products": []
  }
}

Steps to reproduce

  1. Create a custom directive
  2. Create a schema and a query including this directive.
  3. Obtain a document for the operation_id corresponding to the query as follows:
    GraphQL::Query.new(
      ApplicationSchema,
      variables: variables,
      context: context,
      operation_name: operation_name,
    ).document
    context here contains operation_id. It's actually constructed in a pretty complex way, if I need to create a minimal reproduction I'll have to reduce that part. Maybe there is a better way to obtain the document? But it's the one I found by checking where Schema.execute ends up.
  4. Get the definition for the query from the document with definition = document.definitions.find { |definition| definition.name == operation_name }.
  5. Check definition.directives.

Expected behavior

definition.directives should contain the directive from the query.

Actual behavior

definition.directives is empty. I can see the full definition in the debugger, and all @directives are empty, so it doesn't seem like there is another way to access the needed directive.

Hey, thanks for the detailed report and sorry for the trouble! I wrote up a script like your report above, but it worked for me:

Reading custom query-level directives from OperationStore

require "bundler/inline"

gemfile do
  gem "graphql", "1.13.23" # or latest, 2.3.2
  gem "graphql-pro", "1.25.2" # or latest, 1.27.4
  gem "redis"
  gem "racc"
  gem "logger"
end

$redis = Redis.new
$redis.flushall

class MySchema < GraphQL::Schema
  class Cache < GraphQL::Schema::Directive
    argument :max_age, Integer, required: false, description: 'Cache expiration in seconds'
    locations QUERY
  end

  class Query < GraphQL::Schema::Object
    field :int, Integer do
      argument :i, Integer
    end
  end

  query(Query)
  directives(Cache)
  use GraphQL::Pro::OperationStore, redis: $redis
end

MySchema.operation_store.upsert_client("client-1", "secret")
MySchema.operation_store.add(
  client_name: "client-1",
  operation_alias: "get-int",
  body: "query GetInt($int: Int!) @cache { int(i: $int) }"
)

query = GraphQL::Query.new(MySchema, context: { operation_id: "client-1/get-int"})
pp query.selected_operation.directives.map(&:name)
# ["cache"]

pp query.document.definitions.find { |d| d.name == "GetInt" }.directives.map(&:name)
# ["cache"]

Does that script work for you? Or do you see anything missing from it, compared to your case?

One thing you could check is the OperationStore dashboard (doc). Can you find your sync'd operation there? Does the query string in the dashboard include @cache?

I'll try to check during the weekend or early next week.

I'm sorry, it looks like I simply didn't regenerate the operation store after modifying the query. It works as expected now, though I have an additional question I asked in discussions.