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

Deprecation warnings with graphql-1.13.1 #53

Open
IngusSkaistkalns opened this issue Dec 15, 2021 · 15 comments
Open

Deprecation warnings with graphql-1.13.1 #53

IngusSkaistkalns opened this issue Dec 15, 2021 · 15 comments

Comments

@IngusSkaistkalns
Copy link

IngusSkaistkalns commented Dec 15, 2021

Hello,

Deprecation warnings starting graphql-1.13.1:

Legacy `.to_graphql` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Remove `.to_graphql` to use a class-based definition instead.

Called on #<GraphQL::Schema::Field Query.posts(...): [Post!]!> from:

graphql-guard/lib/graphql/guard/testing.rb:29:in `field_with_guard'
.....

From changelog

.to_graphql and .graphql_definition are deprecated and will be removed in GraphQL-Ruby 2.0. 
All features using those legacy definitions are already removed
and all behaviors should have been ported to class-based definitions. 
So, you should be able to remove those calls entirely.
Please open an issue if you have trouble with it! #3750 #3765

Also I noticed, that inline guards return Array wrapped guard Proc and not just proc right away. Is this intended outcome in graphql or some undesired behaviour - I don't know, should investigate more.

@pierry01
Copy link

👍 up

@asgeo1
Copy link

asgeo1 commented Jan 14, 2022

Hmm, I'm getting this error now on the latest graphql-ruby (1.13.4)

undefined method `graphql_definition' for #<GraphQL::Schema::TypeMembership
Did you mean?  graphql_name
/bundle/ruby/3.0.0/bundler/gems/graphql-guard-ca368a8dbdac/lib/graphql/guard.rb:17:in `block in <class:Guard>'

Not quite sure what's happening as I didn't think they removed the .graphql_definition yet? In the PR, it seems deprecated, not removed. Not quite following what's going on.

Downgrading to graphql 1.12.23 addresses the issue, and I no longer get the error.

@23tux
Copy link

23tux commented Feb 8, 2022

Is there any news on this? I get the same warnings when updating to graphql-ruby 1.13.8

@23tux
Copy link

23tux commented Feb 23, 2022

I just tried to find a fix for this, but I'm not sure how to get the metadata from schema_member without using graphql_definition in those lines:

MASKING_FILTER = ->(schema_member, ctx) do
mask = schema_member.graphql_definition.metadata[:mask]
mask ? mask.call(ctx) : true
end

In sum there are 3 places where #graphql_definition is still used.

As mentioned here https://github.com/rmosolgo/graphql-ruby/blob/master/CHANGELOG.md#deprecations-3

All features using those legacy definitions are already removed and all behaviors should have been ported to class-based definitions. So, you should be able to remove those calls entirely.

But of course, we can't just remove the call itself, somehow it has to load the masks.

Maybe @rmosolgo can help?

@23tux
Copy link

23tux commented Feb 23, 2022

I just discovered, that it's not only a deprecation warning, but an error:

NoMethodError:
 undefined method `graphql_definition' for #<GraphQL::Schema::TypeMembership:0x0000557f69381178>
 Did you mean?  graphql_name
# /usr/local/bundle/gems/graphql-guard-2.0.0/lib/graphql/guard.rb:17:in `block in <class:Guard>'

@rmosolgo
Copy link

metadata is part of the legacy type definition system, you're right that there's no way to access it apart from .graphql_definition. Instead of .metadata, you should store custom values in instance variables on your definitions. For example:

module MaskFilter 
  attr_accessor :mask 
end 

class Types::BaseObject < GraphQL::Schema::Object
  extend MaskFilter 
end 

# ^^ do the same for other base classes 

MASKING_FILTER = ->(schema_member, ctx) do 
 mask =  schema_member.respond_to?(:mask) && schema_member.mask
 mask ? mask.call(ctx) : true 
end 

Hope that helps!

@23tux
Copy link

23tux commented Mar 2, 2022

@rmosolgo thanks for the quick answer!

It seems, that the changes require more work than I first anticipated to get masking to work. So I wondered, if you can give some advice how to implement it?

The current implementation is this:

def add_schema_masking!(schema_definition)
schema_definition.class_eval do
def self.default_filter
GraphQL::Filter.new(except: default_mask).merge(only: MASKING_FILTER)
end
end
end

I see some room for improvement here:

What would be a more future proof way of providing a masking feature? I found the visiblity stuff in your docs (see https://graphql-ruby.org/schema/dynamic_types.html#using-visiblecontext-1), but it seems that it's intended to dynamically switch the implementation of a type, right?

Regarding "metadata is part of the legacy type definition system": Does this imply, that using .accept_definitions is the wrong way now? See:

GraphQL::ObjectType.accepts_definitions(guard: GraphQL::Define.assign_metadata_key(:guard))
GraphQL::Field.accepts_definitions(guard: GraphQL::Define.assign_metadata_key(:guard))
GraphQL::Field.accepts_definitions(mask: GraphQL::Define.assign_metadata_key(:mask))
GraphQL::Schema::Object.accepts_definition(:guard)
GraphQL::Schema::Field.accepts_definition(:guard)
GraphQL::Schema::Field.accepts_definition(:mask)

Before you could write something like this

field :title, String, null: true, guard: ->(obj, args, ctx) { ctx[:current_user].admin? }

I'm not sure how I would achieve something like this using your approach with attr_accessor :mask. Could you elaborate?

@rmosolgo
Copy link

rmosolgo commented Mar 2, 2022

future proof way of providing a masking feature?

Yes, def self.visible?(ctx) is the way. Here are some docs on that: https://graphql-ruby.org/authorization/visibility.html

Besides swapping implementations at runtime, you can also hide implementations at runtime -- and if a type, field, argument, etc is hidden, then it doesn't exist for the current query.

using .accept_definitions

Yes, it was a way to bridge the gap from legacy definition to class-based definition, but it's removed in graphql-ruby 2.0.

Honestly, for class-based definitions, I would recommend a method-based approach instead of a proc-based approach. Something like this:

module GraphQL::Guard::Integration 
  def masked?(context)
    # library users should implement this method to hide things
    false
  end 
  
  def visible?(context)
    (!masked?(context)) && super 
  end 

  def guarded?(*args) # for objects, `[obj, ctx]`, but for fields and arguments, `[obj, args, ctx]`
    # library users should implement this method to flag things as not authorized
    false 
  end 
  
  def authorized?(*args)
    (!guarded?(*args)) && super 
  end
end 

class Types::BaseObject < GraphQL::Schema::Object 
  extend GraphQL::Guard::Integration 
  
  def self.guarded?(obj, ctx)
    ctx[:current_user].admin?
  end 
end 

# or: 

class Types::BaseField < GraphQL::Schema::Field 
  include GraphQL::Guard::Integration 
  
  def guarded?(obj, args, ctx)
    ctx[:current_user].admin?
  end 
end 

More details about graphql-ruby's .authorized? method here: https://graphql-ruby.org/authorization/authorization.html

Hope that helps!

@grxy
Copy link

grxy commented Mar 14, 2022

Hello @exAspArk, are there any plans for this library to support GraphQL Ruby 1.13 or 2.0? Are you open to PRs to address this issue?

@mcelicalderon
Copy link

@rmosolgo thank you, this helps a lot. We are going to need something similar in https://github.com/graphql-devise/graphql_devise and I think it might help for this gem too.

It looks like our only way to implement this, will be to provide a custom field class in the gem, and that one should be able to handle what you described in #53 (comment). Is there a way to assign a default field_class for all types of fields? From the docs it'd seem like we will have to call field_class in every base type like enum or object. But from a gem's perspective it would be amazing if we could assign a field class for ALL in a single call. Of course, I see how that might not be possible and we might have to document this so users do this in their projects for every base type.

@rmosolgo
Copy link

Yeah, there's not a way to install a field class for every base type in one line of code -- the base Object, Interface, and Mutation classes all need that configuration. I definitely agree that would be nice, but I haven't been able to figure out how it'd work...

@mcelicalderon
Copy link

That's fine, thank you!

@KauanCS
Copy link

KauanCS commented Oct 30, 2023

Any updates? I started having this problem after updating some old libs 😞

@tienle
Copy link

tienle commented Apr 12, 2024

Is still repo still active?

@exAspArk
Copy link
Owner

Sorry, I don't have the bandwidth to address it. But if anyone is willing to submit a PR, I'd be happy to review it!

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

10 participants