union.ex (3182B)
1 defmodule Absinthe.Type.Union do 2 @moduledoc """ 3 A unions is an abstract type made up of multiple possible concrete types. 4 5 No common fields are declared in a union. Compare to `Absinthe.Type.Interface`. 6 7 Because it's necessary for the union to determine the concrete type of a 8 resolved object, you must either: 9 10 * Provide a `:resolve_type` function on the union 11 * Provide a `:is_type_of` function on each possible concrete type 12 13 ``` 14 union :search_result do 15 description "A search result" 16 17 types [:person, :business] 18 resolve_type fn 19 %Person{}, _ -> :person 20 %Business{}, _ -> :business 21 end 22 end 23 ``` 24 """ 25 26 use Absinthe.Introspection.TypeKind, :union 27 28 alias Absinthe.{Schema, Type} 29 30 @typedoc """ 31 * `:name` - The name of the union type. Should be a TitleCased `binary`. Set automatically. 32 * `:description` - A nice description for introspection. 33 * `:types` - The list of possible types. 34 * `:resolve_type` - A function used to determine the concrete type of a resolved object. See also `Absinthe.Type.Object`'s `:is_type_of`. Either `resolve_type` is specified in the union type, or every object type in the union must specify `is_type_of` 35 36 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. 37 38 The `__private__` and `:__reference__` keys are for internal use. 39 40 """ 41 @type t :: %__MODULE__{ 42 name: binary, 43 description: binary, 44 types: [Type.identifier_t()], 45 identifier: atom, 46 fields: map, 47 __private__: Keyword.t(), 48 definition: module, 49 __reference__: Type.Reference.t() 50 } 51 52 defstruct name: nil, 53 description: nil, 54 identifier: nil, 55 resolve_type: nil, 56 types: [], 57 fields: nil, 58 __private__: [], 59 definition: nil, 60 __reference__: nil 61 62 @doc false 63 defdelegate functions, to: Absinthe.Blueprint.Schema.UnionTypeDefinition 64 65 @doc false 66 @spec member?(t, Type.t()) :: boolean 67 def member?(%{types: types}, %{identifier: ident}) do 68 ident in types 69 end 70 71 def member?(_, _) do 72 false 73 end 74 75 @doc false 76 @spec resolve_type(t, any, Absinthe.Resolution.t()) :: Type.t() | nil 77 def resolve_type(type, object, env, opts \\ [lookup: true]) 78 79 def resolve_type(%{types: types} = union, obj, %{schema: schema} = env, opts) do 80 if resolver = Type.function(union, :resolve_type) do 81 case resolver.(obj, env) do 82 nil -> 83 nil 84 85 ident when is_atom(ident) -> 86 if opts[:lookup] do 87 Absinthe.Schema.lookup_type(schema, ident) 88 else 89 ident 90 end 91 end 92 else 93 type_name = 94 Enum.find(types, fn 95 %{is_type_of: nil} -> 96 false 97 98 type -> 99 type = Absinthe.Schema.lookup_type(schema, type) 100 Absinthe.Type.function(type, :is_type_of).(obj) 101 end) 102 103 if opts[:lookup] do 104 Schema.lookup_type(schema, type_name) 105 else 106 type_name 107 end 108 end 109 end 110 end