interface.ex (3978B)
1 defmodule Absinthe.Type.Interface do 2 @moduledoc """ 3 A defined interface type that represent a list of named fields and their 4 arguments. 5 6 Fields on an interface have the same rules as fields on an 7 `Absinthe.Type.Object`. 8 9 If an `Absinthe.Type.Object` lists an interface in its `:interfaces` entry, 10 it guarantees that it defines the same fields and arguments that the 11 interface does. 12 13 Because sometimes it's for the interface to determine the implementing type of 14 a resolved object, you must either: 15 16 * Provide a `:resolve_type` function on the interface 17 * Provide a `:is_type_of` function on each implementing type 18 19 ``` 20 interface :named_entity do 21 field :name, :string 22 resolve_type fn 23 %{age: _}, _ -> :person 24 %{employee_count: _}, _ -> :business 25 _, _ -> nil 26 end 27 end 28 29 object :person do 30 field :name, :string 31 field :age, :string 32 33 interface :named_entity 34 end 35 36 object :business do 37 field :name, :string 38 field :employee_count, :integer 39 40 interface :named_entity 41 end 42 ``` 43 """ 44 45 use Absinthe.Introspection.TypeKind, :interface 46 47 alias Absinthe.Type 48 alias Absinthe.Schema 49 50 @typedoc """ 51 * `:name` - The name of the interface type. Should be a TitleCased `binary`. Set automatically. 52 * `:description` - A nice description for introspection. 53 * `:fields` - A map of `Absinthe.Type.Field` structs. See `Absinthe.Schema.Notation.field/4` and 54 * `:args` - A map of `Absinthe.Type.Argument` structs. See `Absinthe.Schema.Notation.arg/2`. 55 * `:resolve_type` - A function used to determine the implementing type of a resolved object. See also `Absinthe.Type.Object`'s `:is_type_of`. 56 57 The `:resolve_type` function will be passed two arguments; the object whose type needs to be identified, and the `Absinthe.Execution` struct providing the full execution context. 58 59 The `__private__` and `:__reference__` keys are for internal use. 60 """ 61 @type t :: %__MODULE__{ 62 name: binary, 63 description: binary, 64 fields: map, 65 identifier: atom, 66 interfaces: [Absinthe.Type.Interface.t()], 67 __private__: Keyword.t(), 68 definition: module, 69 __reference__: Type.Reference.t() 70 } 71 72 defstruct name: nil, 73 description: nil, 74 fields: nil, 75 identifier: nil, 76 resolve_type: nil, 77 interfaces: [], 78 __private__: [], 79 definition: nil, 80 __reference__: nil 81 82 @doc false 83 defdelegate functions, to: Absinthe.Blueprint.Schema.InterfaceTypeDefinition 84 85 @spec resolve_type(Type.Interface.t(), any, Absinthe.Resolution.t()) :: Type.t() | nil 86 def resolve_type(type, obj, env, opts \\ [lookup: true]) 87 88 def resolve_type(interface, obj, %{schema: schema} = env, opts) do 89 if resolver = Type.function(interface, :resolve_type) do 90 case resolver.(obj, env) do 91 nil -> 92 nil 93 94 ident when is_atom(ident) -> 95 if opts[:lookup] do 96 Absinthe.Schema.lookup_type(schema, ident) 97 else 98 ident 99 end 100 end 101 else 102 type_name = 103 Schema.implementors(schema, interface.identifier) 104 |> Enum.find(fn type -> 105 Absinthe.Type.function(type, :is_type_of).(obj) 106 end) 107 108 if opts[:lookup] do 109 Absinthe.Schema.lookup_type(schema, type_name) 110 else 111 type_name 112 end 113 end 114 end 115 116 @doc """ 117 Whether the interface (or implementors) are correctly configured to resolve 118 objects. 119 """ 120 @spec type_resolvable?(Schema.t(), t) :: boolean 121 def type_resolvable?(schema, %{resolve_type: nil} = iface) do 122 Schema.implementors(schema, iface) 123 |> Enum.all?(& &1.is_type_of) 124 end 125 126 def type_resolvable?(_, %{resolve_type: _}) do 127 true 128 end 129 130 @doc false 131 @spec member?(t, Type.t()) :: boolean 132 def member?(%{identifier: ident}, %{interfaces: ifaces}) do 133 ident in ifaces 134 end 135 136 def member?(_, _) do 137 false 138 end 139 end