graphql-ruby icon indicating copy to clipboard operation
graphql-ruby copied to clipboard

Invisible object class makes interfaces ivislbe for all objects that extend it

Open bessey opened this issue 2 years ago • 1 comments

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"}}]}

bessey avatar Jul 24 '23 14:07 bessey

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.

rmosolgo avatar Jul 25 '23 14:07 rmosolgo