Is there any way to resolve a field with a ordinary Ruby Hash? At the moment I need to convert the response of a particular API response into a list of OpenStructs, so that graphql can use public_send on it.
resolve ->(obj, args, ctx) {
HTTParty.post('http://example.com')
.parsed_response
.fetch('some_key')
.map { |data| OpenStruct.new(data) } # this step feels unnecessary and could be a potential performance problem
}
Nothing automatic, you have to specify the resolve proc by hand:
field :some_hash_member, types.String do
resolve -> (obj, args, ctx) {
obj[:some_hash]
end
end
You could metaprogram that a bit,
# Generate a proc which resolves by getting
# `key` from obj
def resolves_by_hash_key(key)
-> (obj, args, ctx) { obj[key]
end
# ...
field :some_key, types.String do
resolve(resolves_by_hash_key(:some_key))
end
We use hashes extensively and we are doing this atm:
resolve -> (data, args, _ctx) do
data['price']
end
It would be great if the one line syntax could support this e.g.
field :price, !types.String, hash: true
The latest graphql version supports custom definitions, I posted a solution in the original PR but forgot to copy it over here! What do you think of this?
# Define a callable with desired behavior:
module HashField
def self.call(object_type, field_name, field_type)
# stringify the field name because graphql APIs depend on string names
field_name_s = field_name.to_s
# define a field which accesses its value with `[]`
field = GraphQL::Field.define do
name(field_name_s)
type(field_type)
resolve -> (obj, args, ctx) {
obj[field_name_s]
# or, to access the field by Symbol instead:
# obj[field_name]
}
end
# attach the field to the object type which is being defined
object_type.fields[field_name_s] = field
end
end
# Add it to the object's definitions:
GraphQL::ObjectType.accepts_definitions(hash_field: HashField)
# Use the new definition to define fields
GraphQL::ObjectType.define do
# These arguments are passed to `HashField.call`, which defines a field based on them
hash_field :nickname, types.String
end
This look good, but this seems to be a common case to warrant having hash_field in the gem maybe?
Coming back around to this,
hash_fieldin the gem
Somehow we have to disambiguate between String keys and Symbol keys. At first I thought
hash_field :sym_key_field # ...
hash_field "string_key_field" # ...
But that seems a bit hard on the eyes. What about accepting a hash_key: param, similar to the property: param? It could be:
hash_field :sym_key_field, types.Int, hash_key: :sym_key
hash_field :str_key_field, types.Int, hash_key: "str_key"
That way, the hash_key field could be a signal to use #[] to resolve the field, and whatever value is passed is the key.
If you want to PR it, please feel free, otherwise I'll try to get a chance soon!
This is implemented on master like this:
field :sym_key_field, types.Int, hash_key: :sym_key
field :str_key_field, types.Int, hash_key: "str_key"
You can pass a hash_key option to the field helper, or use hash_key in a block:
field ... do
hash_key :sym_key
end
See ae9abd2
Most helpful comment
The latest graphql version supports custom definitions, I posted a solution in the original PR but forgot to copy it over here! What do you think of this?