Version: 1.4.0-beta.2
interface :foo do
field :bar, :integer do
resolve fn _, _ -> {:ok, 1} end
end
end
It's possible to add a resolve field to a field on an interface, but the function is never used. I see two possible solutions:
I have a preference for the "default" solution because of the use case where I found this issue, but either one of those will make sense.
I like the default idea as well, at least at first...
As I've seen it it in other implementations, not supporting definition in the interface does serve a purpose: it makes it more obvious when an object type that's declared that it implements an interface type actually has. It also makes it clearer what an implementing types's field resolver actually is. I'm wondering if the second choice you point out might make more sense, to keep things simple.
Consider this case:
interface :foo do
field :bar, :integer do
resolve fn _, _ -> {:ok, 1} end
end
end
object :baz do
interface :foo
field :bar, :integer
end
object :spam do
interface :foo
end
:baz trying to set for :bar? No resolution has been set for the field; should it use the interface default, or the system-level default? As a reader, would you have to check the interface every time to see what "fell through" to the interface?:bar, is :spam meeting the requirements of the interface, or not? If not, how would it (avoiding the ambiguity of the :baz example above)Yeah, it looks like the default could be ambiguous, so I guess raising would be a better solution.
Alternatively, what do you think about adding a default block to interfaces? This would mean you don't need to repeat what's there in the implementations. Any mention of a field would mean you lose anything from the default. But I guess this would be a major feature, for now
Scheduling addressing this as part of the "schema definition refactor" milestone.
One idea I had for this that could be both explicit and reduce duplication is something like:
interface :foo do
field :bar, :integer do
resolve fn _, _ -> {:ok, 1} end
end
end
object :baz do
interfaces [foo: [import_resolvers: true]]
end
object :spam do
interface :foo
end
In this case, spam would still raise as it doesn't resolve the field. baz does resolve since it has specified import_resolvers: true. Other options for this include import_fields.
I would also like to point out that something similar could be relevant for documentation:
interface :foo do
@desc """
The bar field
"""
field :bar, :integer do
resolve fn _, _ -> {:ok, 1} end
end
end
object :baz do
interfaces [foo: [import_resolvers: true]]
end
This would result in the bar field being documented with the documentation from the interface. Currently with the above, the bar field would not be documented.
Has there been any update on this issue? I would dearly love the default concept mentioned by @michalmuskala to be implemented. I've got a few places in my code where an abstract concept is modelled (like a Company, say) and then concrete types are conceptually derived (like a Supplier and a Manufacturer) and I need to share resolvers with all the different Company types whilst still using specific fields for Supplier and Manufacturer.
@ratbag98 it's slated to be in 1.5. Schema features are frozen on 1.4 at the moment since all the underlying mechanisms are changing in 1.5.
A little pattern that works pretty well for me to address this for now @ratbag98 is to define an abstract_company object with all the fields & resolvers you want to share and then in your supplier and manufacturer, you import the fields from abstract_company.
FYI this works in 1.5 by using import_fields $INTERFACE
defmodule Foo do
use Absinthe.Schema
interface :named do
field :name, :string do
resolve fn parent, _, _ -> {:ok, parent.name} end
end
resolve_type fn _, _ ->
:user
end
end
object :user do
interfaces [:named]
import_fields :named
end
query do
end
end
Most helpful comment
FYI this works in 1.5 by using
import_fields $INTERFACE