From cd0905a98d7e911c7280e16fad832b3e719fab4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20M=C3=A4nnchen?= Date: Wed, 2 Jun 2021 13:18:29 +0200 Subject: [PATCH 1/4] Prevent key :is_type_of not found error on interfaces --- lib/absinthe/type/interface.ex | 4 ++- test/absinthe/type/interface_test.exs | 45 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/lib/absinthe/type/interface.ex b/lib/absinthe/type/interface.ex index 423b7bbcf6..82f48c05f4 100644 --- a/lib/absinthe/type/interface.ex +++ b/lib/absinthe/type/interface.ex @@ -102,7 +102,9 @@ defmodule Absinthe.Type.Interface do end else type_name = - Enum.find(implementors, fn type -> + implementors + |> Enum.filter(&match?(%Type.Object{}, &1)) + |> Enum.find(fn type -> Absinthe.Type.function(type, :is_type_of).(obj) end) diff --git a/test/absinthe/type/interface_test.exs b/test/absinthe/type/interface_test.exs index 822ca5123d..59cd4f9ffa 100644 --- a/test/absinthe/type/interface_test.exs +++ b/test/absinthe/type/interface_test.exs @@ -271,4 +271,49 @@ defmodule Absinthe.Type.InterfaceTest do test "works even when resolve_type returns nil" do assert_data(%{"namedThing" => %{}}, run(@graphql, Schema)) end + + defmodule NestedInterfacesSchema do + use Absinthe.Schema + + interface :root do + field :root, :string + end + + interface :intermediate do + field :root, :string + field :intermediate, :string + + interface :root + end + + # Name starts with Z to order it to the back of the list of types + object :z_child do + field :root, :string + field :intermediate, :string + field :child, :string + + interface :root + interface :intermediate + + is_type_of fn _entry -> true end + end + + query do + field :root, :root do + resolve fn _, _, _ -> {:ok, %{}} end + end + end + end + + @graphql """ + query GetRoot { + root { + __typename + } + } + """ + + test "resolved type of nested interfaces" do + assert_data(%{"root" => %{"__typename" => "ZChild"}}, run(@graphql, NestedInterfacesSchema)) + end end From 1bc0e503523c821d558b6f2ee4647aa7925fcf7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20M=C3=A4nnchen?= Date: Fri, 26 Nov 2021 14:32:57 +0000 Subject: [PATCH 2/4] Switch to find_implementors based fix --- lib/absinthe/blueprint/schema/interface_type_definition.ex | 4 +--- lib/absinthe/type/interface.ex | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/absinthe/blueprint/schema/interface_type_definition.ex b/lib/absinthe/blueprint/schema/interface_type_definition.ex index d0be1363a5..b1fd11be9e 100644 --- a/lib/absinthe/blueprint/schema/interface_type_definition.ex +++ b/lib/absinthe/blueprint/schema/interface_type_definition.ex @@ -49,10 +49,8 @@ defmodule Absinthe.Blueprint.Schema.InterfaceTypeDefinition do } end - @interface_types [Schema.ObjectTypeDefinition, Schema.InterfaceTypeDefinition] - def find_implementors(iface, type_defs) do - for %struct{} = obj when struct in @interface_types <- type_defs, + for %Schema.ObjectTypeDefinition{} = obj <- type_defs, iface.identifier in obj.interfaces, do: obj.identifier end diff --git a/lib/absinthe/type/interface.ex b/lib/absinthe/type/interface.ex index 82f48c05f4..423b7bbcf6 100644 --- a/lib/absinthe/type/interface.ex +++ b/lib/absinthe/type/interface.ex @@ -102,9 +102,7 @@ defmodule Absinthe.Type.Interface do end else type_name = - implementors - |> Enum.filter(&match?(%Type.Object{}, &1)) - |> Enum.find(fn type -> + Enum.find(implementors, fn type -> Absinthe.Type.function(type, :is_type_of).(obj) end) From e4f3ffe91734b08bdf187820914359e5c7ab7cd5 Mon Sep 17 00:00:00 2001 From: Christian Meunier Date: Thu, 9 Dec 2021 20:00:57 +0800 Subject: [PATCH 3/4] Fix tests to expect only concrete implementors --- .../object_must_implement_interfaces_test.exs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/absinthe/schema/rule/object_must_implement_interfaces_test.exs b/test/absinthe/schema/rule/object_must_implement_interfaces_test.exs index 9aeaac3d07..fc07ce8914 100644 --- a/test/absinthe/schema/rule/object_must_implement_interfaces_test.exs +++ b/test/absinthe/schema/rule/object_must_implement_interfaces_test.exs @@ -99,7 +99,7 @@ defmodule Absinthe.Schema.Rule.ObjectMustImplementInterfacesTest do assert %{ named: [:cat, :dog, :user], favorite_foods: [:cat, :dog, :user], - parented: [:cat, :dog, :named, :user] + parented: [:cat, :dog, :user] } == Schema.__absinthe_interface_implementors__() end @@ -117,7 +117,7 @@ defmodule Absinthe.Schema.Rule.ObjectMustImplementInterfacesTest do url: String } - interface Image implements Resource & Node { + type Image implements Resource & Node { id: ID! url: String thumbnail: String @@ -125,14 +125,21 @@ defmodule Absinthe.Schema.Rule.ObjectMustImplementInterfacesTest do """ + def hydrate(%Absinthe.Blueprint.Schema.InterfaceTypeDefinition{}, _) do + {:resolve_type, &__MODULE__.resolve_type/1} + end + + def hydrate(_node, _ancestors), do: [] + + def resolve_type(_), do: false + query do end end test "interfaces are set from sdl" do assert %{ - image: [], - node: [:image, :resource], + node: [:image], resource: [:image] } == InterfaceImplementsInterfaces.__absinthe_interface_implementors__() From e09cd6bf01d8397b5ba5a100ff062dded6702e08 Mon Sep 17 00:00:00 2001 From: Christian Meunier Date: Thu, 9 Dec 2021 20:04:22 +0800 Subject: [PATCH 4/4] Lookup implementors only when needed --- lib/absinthe/type/interface.ex | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/absinthe/type/interface.ex b/lib/absinthe/type/interface.ex index 423b7bbcf6..246de79dc6 100644 --- a/lib/absinthe/type/interface.ex +++ b/lib/absinthe/type/interface.ex @@ -86,8 +86,6 @@ defmodule Absinthe.Type.Interface do def resolve_type(type, obj, env, opts \\ [lookup: true]) def resolve_type(interface, obj, %{schema: schema} = env, opts) do - implementors = Schema.implementors(schema, interface.identifier) - if resolver = Type.function(interface, :resolve_type) do case resolver.(obj, env) do nil -> @@ -102,7 +100,8 @@ defmodule Absinthe.Type.Interface do end else type_name = - Enum.find(implementors, fn type -> + Schema.implementors(schema, interface.identifier) + |> Enum.find(fn type -> Absinthe.Type.function(type, :is_type_of).(obj) end)