graphql-ruby
graphql-ruby copied to clipboard
Invisible object class makes interfaces ivislbe for all objects that extend it
Describe the bug
Given a GraphQL object class which implements one or more interfaces, and which is extended by another object class, if the original object is not visible?, its interfaces are not exposed on the extending class.
Versions
I am on 1.13 but I have reproduced with 2.0 latest:
Demo
require "bundler/inline"
gemfile do
source "https://rubygems.org"
gem "graphql", "~> 2.0"
end
class Account
def initialize(id)
@id = id
end
attr_reader :id
end
module Identifiable
include GraphQL::Schema::Interface
field :id, ID, null: false
end
class AccountType < GraphQL::Schema::Object
implements Identifiable
# FIX: Comment this method out
def self.visible?(_context)
false
end
end
class NewAccountType < AccountType
def self.visible?(_context)
true
end
end
class QueryType < GraphQL::Schema::Object
field :account, NewAccountType, null: true
def account
Account.new(1)
end
end
class MySchema < GraphQL::Schema
query(QueryType)
end
query = <<-GRAPHQL
query {
account {
id
}
}
GRAPHQL
result = MySchema.execute(query, variables: {}, context: {})
puts result.to_h
Steps to reproduce
Run the above with and without the commented section.
Expected behavior
$ ruby ./demo.rb
{"data"=>{"account"=>{"id"=>"1"}}}
Actual behavior
$ ruby ./demo.rb
{"errors"=>[{"message"=>"Field 'id' doesn't exist on type 'NewAccount'", "locations"=>[{"line"=>3, "column"=>7}], "path"=>["query", "account", "id"], "extensions"=>{"code"=>"undefinedField", "typeName"=>"NewAccount", "fieldName"=>"id"}}]}
Hey, thanks for the detailed report. I'll have to check back in the archives to determine whether this is a feature or a bug 😆 . I have a hunch it was addressed in a previous issue or PR.
In any case, the described behavior comes from here:
https://github.com/rmosolgo/graphql-ruby/blob/f05ea569af0ceea6eea41919b7bb61f3eb06f2cd/lib/graphql/schema/type_membership.rb#L30-L34
You could customize how interface implementations and union memberships are determined to be visible by making a custom subclass of TypeMembership and attaching it to your base interface and base union using the type_membership_class(...) config:
https://github.com/rmosolgo/graphql-ruby/blob/f05ea569af0ceea6eea41919b7bb61f3eb06f2cd/lib/graphql/schema/interface.rb#L31
https://github.com/rmosolgo/graphql-ruby/blob/f05ea569af0ceea6eea41919b7bb61f3eb06f2cd/lib/graphql/schema/union.rb#L35
That would allow you to opt out of this behavior in the meantime.