-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Inheritance support for input object types #515
Comments
The confusing bit is that certain things (like module TodoInputBase
def self.included(input)
argument :content, types.String
argument :due_by, types.String
end
end
TodoUpdateInput = GraphQL::InputObjectType.define do
name "TodoUpdateInput"
include TodoInputBase
end
TodoCreateInput = GraphQL::InputObjectType.define do
name "TodoCreateInput"
include TodoInputBase
argument :author, !types.String
end If it's a common pattern and you really wanna save some lines, you could write a wrapper: def Included(&block)
Module.new do
define_singleton_method :included do |mod|
mod.instance_eval(&block)
end
end
end
TodoInputBase = Included do
argument :content, types.String
argument :due_by, types.String
end Vanilla functions are another option: module TodoInputBase
def self.apply(input)
input.instance_eval do
argument :content, types.String
argument :due_by, types.String
end
end
end
TodoUpdateInput = GraphQL::InputObjectType.define do
name "TodoUpdateInput"
TodoInputBase.apply(self)
end
TodoCreateInput = GraphQL::InputObjectType.define do
name "TodoCreateInput"
TodoInputBase.apply(self)
argument :author, !types.String
end (You could similarly create a builder pattern here and shed a few lines.) |
The syntax I settled on was GraphQL::InputObjectType.accepts_definitions(
argument_mixin: ->(defn, type) do
type.arguments.each { |key, val| defn.arguments[key] = val.clone }
end
)
TodoUpdateInput = GraphQL::InputObjectType.define do
name "TodoUpdateInput"
argument :content, types.String
argument :due_by, types.String
end
TodoCreateInput = GraphQL::InputObjectType.define do
name "TodoCreateInput"
argument_mixin TodoUpdateInput
argument :author, !types.String
end to mirror the interface pattern. Perhaps "argument_mixin" isn't the best name but it is more accurate and fits better with the naming convention of the other definable attributes. If nobody thinks that this kind of behavior should be added to the framework we can go ahead and close the issue. |
To me @MatthewChang's solution seems nice. Since we're just cloning, I would appreciate being able to specify the fields/args to be cloned:
Another option would be
This would clone every field of UserType but voiding outer non-null constraints. Suppose in a Rails app, an What do you think? Is this too specific to be generically supported by the library? |
👍 I think it's great, I might just look for a more declarative-sounding name like |
We ended up with the following in our project: GraphQL::InputObjectType.accepts_definitions(
# Usage examples:
#
# 1. clones every argument from DogInputType
# inherit_arguments DogInputType
#
# 2. Clones every argument, except a few (single value or array of values accepted)
# inherit_arguments DogInputType, except: :name
#
# 3. Clones only the whitelisted arguments
# inherit_arguments DogInputType, only: [:name, :height]
#
# 4. The nullable option will remove eventual non-null constraints:
# inherit_arguments DogInputType, only: [:name, :height], nullable: true
#
# Note: nullable: true will only remove the external non-null checks:
# [String!]! will become [String!]
inherit_arguments: ->(defn, from_type, opts = {}) do
to_inherit = from_type.arguments.keys
to_inherit -= Array(opts[:except]).map(&:to_s) if opts.key?(:except)
to_inherit &= Array(opts[:only]).map(&:to_s) if opts.key?(:only)
to_inherit.each do |key|
new_field = from_type.arguments[key.to_s].clone
if opts[:nullable] && new_field.type.kind == GraphQL::TypeKinds::NON_NULL
new_field.type = new_field.type.of_type
end
defn.arguments[key] = new_field
end
end
) |
Just tried adding this myself. Doesn't seem to be working for me @khamusa. Is there a particular place I should be putting this code? Going to try requiring it explicitly before the type using the method, will see how that goes (UPDATE: didn't work). Error I'm experiencing is: Update: To solve my problem I had to call this directly on
|
@abepetrillo I'm glad you managed to make it work. We've used the above solution successfully. However we also realized that in our case using only / except options would lead to a quite bug-prone, brittle code. Instead, it seems to works better when we inherit all fields of a given type, thinking of types as we think of classes that do not violate the Liskov substitution principle. So, the idea is: think of base types, and inherit all their properties instead of a subselection. |
this will be available after #1037, please keep an eye on that issue! |
I don't see any documentation on how to do in the new class-based syntax a way of how I did it in the new class-based syntax:
|
I am currently facing a situation in which I have a create and update mutations for a certain type which have slightly different allowed arguments. Is there currently a built in way to do input type extension? I.e.
It's certainly possible to achieve more or less the same functionality by hacking around a bit but it might be nice to have something like this in the framework. Thanks!
The text was updated successfully, but these errors were encountered: