Closed
Description
Describe the bug
I found a case when context.namespace(:interpreter)[:current_path]
is nil
(to be precise, most of the keys inside the :interpreter
hash are missing). The problem appears when Schema#multiplex
is called and I try to get the current path inside .then
block in promise, i.e.:
class Tweet < GraphQL::Schema::Object
field :content, String, null: false
field :author, Types::User, null: false
def author
puts "Tweet:author current_path=#{context.namespace(:interpreter)[:current_path]}"
Batch::RecordLoader.for(::User).load(object.author_id).then do |author|
puts "Tweet:author after batch current_path=#{context.namespace(:interpreter)[:current_path]}"
author
end
end
end
However, it works fine when Schema#execute
is used.
Not sure if I should go to the graphql-batch repo with this issue.
Versions
graphql
version: latest
graphql-batch
version: latest
Steps to reproduce
A whole example:
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem "rails", "6.0.3"
gem "pg"
gem "graphql", "~> 1.12"
gem "rspec-rails", "~> 4.0.1"
gem "db-query-matchers"
gem "ar_lazy_preload"
gem "graphql-batch"
end
require "active_record"
class App < Rails::Application
config.logger = Logger.new('/dev/null')
end
App.initialize!
ActiveRecord::Base.establish_connection(adapter: "postgresql", database: "nplusonedb")
ActiveRecord::Schema.define do
enable_extension "plpgsql"
create_table "users", force: :cascade do |t|
t.string "nickname", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "tweets", force: :cascade do |t|
t.text "content", null: false
t.bigint "author_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "tweets", "users", column: "author_id"
end
class User < ActiveRecord::Base
has_many :tweets, foreign_key: :author_id
end
class Tweet < ActiveRecord::Base
belongs_to :author, class_name: "User"
end
module Batch
class RecordLoader < GraphQL::Batch::Loader
def initialize(model)
@model = model
end
def perform(ids)
@model.where(id: ids).each { |record| fulfill(record.id, record) }
ids.each { |id| fulfill(id, nil) unless fulfilled?(id) }
end
end
end
module Types
class User < GraphQL::Schema::Object
field :nickname, String, null: false
end
class Tweet < GraphQL::Schema::Object
field :content, String, null: false
field :author, Types::User, null: false
def author
puts "Tweet:author current_path=#{context.namespace(:interpreter)[:current_path]}"
Batch::RecordLoader.for(::User).load(object.author_id).then do |author|
puts "Tweet:author after batch current_path=#{context.namespace(:interpreter)[:current_path]}"
author
end
end
end
end
class FeedResolver < GraphQL::Schema::Resolver
type [Types::Tweet], null: false
def resolve(limit: nil, offset: nil)
Tweet.limit(limit).offset(offset)
end
end
module Types
class Query < GraphQL::Schema::Object
field :feed, [Types::Tweet], null: false, resolver: FeedResolver do
argument :limit, Integer, required: false
argument :offset, Integer, required: false
end
end
end
class GraphqlSchema < GraphQL::Schema
query Types::Query
use GraphQL::Batch
end
Tweet.delete_all
User.delete_all
john = User.create(nickname: "John")
max = User.create(nickname: "Max")
john.tweets.create(content: "Hi!", created_at: Time.new(2020, 6, 12, 10))
max.tweets.create(content: "Hello!", created_at: Time.new(2020, 6, 13, 10))
john.tweets.create(content: "My second tweet is here", created_at: Time.new(2020, 6, 15, 10))
max.tweets.create(content: "The weather is nice", created_at: Time.new(2020, 6, 17, 10))
puts "\nSchema#multiplex\n"
query1 = <<~GQL
query {
feed(offset: 0, limit: 2) {
content
author {
nickname
}
}
}
GQL
query2 = <<~GQL
query {
feed(offset: 2, limit: 2) {
content
author {
nickname
}
}
}
GQL
puts GraphqlSchema.multiplex([{ query: query1 }, { query: query2 }]).map(&:to_h).inspect
puts "\nSchema#execute\n"
puts GraphqlSchema.execute(query1).to_h.inspect
Expected behavior
I want to be able to get the current path inside the promise 🙂
Actual behavior
Current path is available only inside first promise block. Here is what I see in the console:
Schema#multiplex
Tweet:author current_path=["feed", 0, "author"]
Tweet:author current_path=["feed", 1, "author"]
Tweet:author current_path=["feed", 0, "author"]
Tweet:author current_path=["feed", 1, "author"]
Tweet:author after batch current_path=["feed", 0, "author"]
Tweet:author after batch current_path=
Tweet:author after batch current_path=["feed", 0, "author"]
Tweet:author after batch current_path=
[{"data"=>{"feed"=>[{"content"=>"Hi!", "author"=>{"nickname"=>"John"}}, {"content"=>"Hello!", "author"=>{"nickname"=>"Max"}}]}}, {"data"=>{"feed"=>[{"content"=>"My second tweet is here", "author"=>{"nickname"=>"John"}}, {"content"=>"The weather is nice", "author"=>{"nickname"=>"Max"}}]}}]
Schema#execute
Tweet:author current_path=["feed", 0, "author"]
Tweet:author current_path=["feed", 1, "author"]
Tweet:author after batch current_path=["feed", 0, "author"]
Tweet:author after batch current_path=["feed", 0, "author"]
Metadata
Metadata
Assignees
Labels
No labels