Packages
absinthe
1.10.2
1.11.0
1.10.2
1.10.1
1.10.0
1.9.1
1.9.0
1.8.0
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.8
1.6.7
retired
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.5
1.5.0-rc.4
1.5.0-rc.3
1.5.0-rc.2
1.5.0-rc.1
1.5.0-rc.0
1.5.0-beta.2
1.5.0-beta.1
1.5.0-beta.0
1.5.0-alpha.4
1.5.0-alpha.3
1.5.0-alpha.2
1.5.0-alpha.1
1.5.0-alpha.0
1.4.16
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.4.9
1.4.8
retired
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.3
1.4.0-rc.2
1.4.0-rc.1
1.4.0-rc.0
1.4.0-beta.5
1.4.0-beta.4
1.4.0-beta.3
1.4.0-beta.2
1.4.0-beta.1
1.3.2
1.3.1
1.3.0
1.3.0-rc.0
1.3.0-beta.2
1.3.0-beta.1
1.3.0-beta.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.0
1.2.0-beta.0
1.2.0-alpha0
1.2.0-alpha.2
1.2.0-alpha.1
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
0.5.2
0.5.1
0.5.0
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.2.3
0.2.2
0.2.1
0.1.0
GraphQL for Elixir
Current section
Files
Jump to
Current section
Files
lib/absinthe/type.ex
defmodule Absinthe.Type do
@moduledoc false
alias __MODULE__
alias Absinthe.Schema
@type function_identifier :: {module, any}
@type function_ref :: {:ref, module, function_identifier}
# ALL TYPES
@type_modules [
Type.Scalar,
Type.Object,
Type.Interface,
Type.Union,
Type.Enum,
Type.InputObject,
Type.List,
Type.NonNull
]
@typedoc "The types that can be custom-built in a schema"
@type custom_t ::
Type.Scalar.t()
| Type.Object.t()
| Type.Field.t()
| Type.Interface.t()
| Type.Union.t()
| Type.Enum.t()
| Type.InputObject.t()
@typedoc "All the possible types"
@type t :: custom_t | wrapping_t
@typedoc "A type identifier"
@type identifier_t :: atom
@typedoc "A type reference"
@type reference_t :: identifier_t | binary | t
def function(type, key) do
case Map.fetch!(type, key) do
{:ref, module, identifier} ->
module.__absinthe_function__(identifier, key)
function ->
function
end
end
@doc false
# this is just for debugging
def expand(%module{} = type) do
module.functions()
|> Enum.reduce(type, fn
:middleware, type ->
type
attr, type ->
Map.put(type, attr, Absinthe.Type.function(type, attr))
end)
end
@doc "Lookup a custom metadata field on a type"
@spec meta(custom_t, atom) :: nil | any
def meta(%{__private__: store}, key) do
get_in(store, [:meta, key])
end
@doc "Return all custom metadata on a type"
@spec meta(custom_t) :: map
def meta(%{__private__: store}) do
Keyword.get(store, :meta, [])
|> Enum.into(%{})
end
@doc "Determine if a struct matches one of the types"
@spec type?(any) :: boolean
def type?(%{__struct__: mod}) when mod in @type_modules, do: true
def type?(_), do: false
@doc "Determine whether a field/argument is deprecated"
@spec deprecated?(Type.Field.t() | Type.Argument.t()) :: boolean
def deprecated?(%{deprecation: nil}), do: false
def deprecated?(%{deprecation: _}), do: true
def equal?(%{name: name}, %{name: name}), do: true
def equal?(_, _), do: false
def built_in?(type) do
type.definition
|> built_in_module?()
end
def built_in_module?(module) do
module
|> Module.split()
|> Enum.take(3)
|> Module.concat() == Absinthe.Type.BuiltIns
end
# INPUT TYPES
@input_type_modules [Type.Scalar, Type.Enum, Type.InputObject, Type.List, Type.NonNull]
@typedoc "These types may be used as input types for arguments and directives."
@type input_t ::
Type.Scalar.t()
| Type.Enum.t()
| Type.InputObject.t()
| Type.List.t()
| Type.NonNull.t()
@doc "Determine if a term is an input type"
@spec input_type?(any) :: boolean
def input_type?(term) do
term
|> named_type
|> do_input_type?
end
defp do_input_type?(%{__struct__: mod}) when mod in @input_type_modules, do: true
defp do_input_type?(_), do: false
# OBJECT TYPE
@doc "Determine if a term is an object type"
@spec object_type?(any) :: boolean
def object_type?(%Type.Object{}), do: true
def object_type?(_), do: false
@doc "Resolve a type for a value from an interface (if necessary)"
@spec resolve_type(t, any) :: t
def resolve_type(%{resolve_type: resolver}, value), do: resolver.(value)
def resolve_type(type, _value), do: type
# TYPE WITH FIELDS
@doc "Determine if a type has fields"
@spec fielded?(any) :: boolean
def fielded?(%{fields: _}), do: true
def fielded?(_), do: false
# OUTPUT TYPES
@output_type_modules [Type.Scalar, Type.Object, Type.Interface, Type.Union, Type.Enum]
@typedoc "These types may be used as output types as the result of fields."
@type output_t ::
Type.Scalar.t() | Type.Object.t() | Type.Interface.t() | Type.Union.t() | Type.Enum.t()
@doc "Determine if a term is an output type"
@spec output_type?(any) :: boolean
def output_type?(term) do
term
|> named_type
|> do_output_type?
end
defp do_output_type?(%{__struct__: mod}) when mod in @output_type_modules, do: true
defp do_output_type?(_), do: false
# LEAF TYPES
@leaf_type_modules [Type.Scalar, Type.Enum]
@typedoc "These types may describe types which may be leaf values."
@type leaf_t :: Type.Scalar.t() | Type.Enum.t()
@doc "Determine if a term is a leaf type"
@spec leaf_type?(any) :: boolean
def leaf_type?(term) do
term
|> named_type
|> do_leaf_type?
end
defp do_leaf_type?(%{__struct__: mod}) when mod in @leaf_type_modules, do: true
defp do_leaf_type?(_), do: false
# COMPOSITE TYPES
@composite_type_modules [Type.Object, Type.Interface, Type.Union]
@typedoc "These types may describe the parent context of a selection set."
@type composite_t :: Type.Object.t() | Type.Interface.t() | Type.Union.t()
@doc "Determine if a term is a composite type"
@spec composite_type?(any) :: boolean
def composite_type?(%{__struct__: mod}) when mod in @composite_type_modules, do: true
def composite_type?(_), do: false
# ABSTRACT TYPES
@abstract_type_modules [Type.Interface, Type.Union]
@typedoc "These types may describe the parent context of a selection set."
@type abstract_t :: Type.Interface.t() | Type.Union.t()
@doc "Determine if a term is an abstract type"
@spec abstract?(any) :: boolean
def abstract?(%{__struct__: mod}) when mod in @abstract_type_modules, do: true
def abstract?(_), do: false
# NULLABLE TYPES
# @nullable_type_modules [Type.Scalar, Type.Object, Type.Interface, Type.Union, Type.Enum, Type.InputObject, Type.List]
@typedoc "These types can all accept null as a value."
@type nullable_t ::
Type.Scalar.t()
| Type.Object.t()
| Type.Interface.t()
| Type.Union.t()
| Type.Enum.t()
| Type.InputObject.t()
| Type.List.t()
@doc "Unwrap the underlying nullable type or return unmodified"
# nullable_t is a subset of t, but broken out for clarity
@spec nullable(any) :: nullable_t | t
def nullable(%Type.NonNull{of_type: nullable}), do: nullable
def nullable(term), do: term
@doc "Determine if a type is non null"
@spec non_null?(t) :: boolean
def non_null?(%Type.NonNull{}), do: true
def non_null?(_), do: false
# NAMED TYPES
@named_type_modules [
Type.Scalar,
Type.Object,
Type.Interface,
Type.Union,
Type.Enum,
Type.InputObject
]
@typedoc "These named types do not include modifiers like Absinthe.Type.List or Absinthe.Type.NonNull."
@type named_t ::
Type.Scalar.t()
| Type.Object.t()
| Type.Interface.t()
| Type.Union.t()
| Type.Enum.t()
| Type.InputObject.t()
@doc "Determine the underlying named type, if any"
@spec named_type(any) :: nil | named_t
def named_type(%{__struct__: mod, of_type: unmodified}) when mod in [Type.List, Type.NonNull] do
named_type(unmodified)
end
def named_type(%{__struct__: mod} = term) when mod in @named_type_modules, do: term
def named_type(_), do: nil
@doc "Determine if a type is named"
@spec named?(t) :: boolean
def named?(%{name: _}), do: true
def named?(_), do: false
# WRAPPERS
@wrapping_modules [Type.List, Type.NonNull]
@typedoc "A type wrapped in a List on NonNull"
@type wrapping_t :: Type.List.t() | Type.NonNull.t()
@spec wrapped?(t) :: boolean
def wrapped?(%{__struct__: mod}) when mod in @wrapping_modules, do: true
def wrapped?(_), do: false
@doc "Unwrap a type from a List or NonNull"
@spec unwrap(custom_t | wrapping_t | map) :: reference_t | map | nil
def unwrap(%{of_type: t}), do: unwrap(t)
def unwrap(type), do: type
@doc "Unwrap a type from NonNull"
@spec unwrap_non_null(Type.NonNull.t()) :: reference_t
@spec unwrap_non_null(type) :: type when type: custom_t | Type.List.t()
def unwrap_non_null(%Type.NonNull{of_type: t}), do: unwrap_non_null(t)
def unwrap_non_null(type), do: type
@doc """
Get the GraphQL name for a (possibly wrapped) type, expanding
any references if necessary using the provided schema.
"""
@spec name(reference_t, Schema.t()) :: String.t()
def name(ref, schema) do
expanded = expand(ref, schema)
name(expanded)
end
@doc """
Get the GraphQL name for a (possibly wrapped) type.
Note: Use `name/2` if the provided type reference needs to
be expanded to resolve any atom type references.
"""
@spec name(wrapping_t | t) :: String.t()
def name(%Type.NonNull{of_type: contents}) do
name(contents) <> "!"
end
def name(%Type.List{of_type: contents}) do
"[" <> name(contents) <> "]"
end
def name(%{name: name}) do
name
end
@doc "Expand any atom type references inside a List or NonNull"
@spec expand(reference_t, Schema.t()) :: wrapping_t | t
def expand(ref, schema) when is_atom(ref) do
schema.__absinthe_lookup__(ref)
end
def expand(%{of_type: contents} = ref, schema) do
%{ref | of_type: expand(contents, schema)}
end
def expand(type, _) do
type
end
# INTROSPECTION TYPE
@spec introspection?(t) :: boolean
def introspection?(%{name: "__" <> _}) do
true
end
def introspection?(_) do
false
end
# VALUE TYPE
@spec value_type(t, Schema.t()) :: Type.t()
def value_type(%Type.Field{} = node, schema) do
Type.expand(node.type, schema)
end
def value_type(type, schema) do
Type.expand(type, schema)
end
# VALID TYPE
def valid_input?(%Type.NonNull{}, nil) do
false
end
def valid_input?(%Type.NonNull{of_type: internal_type}, value) do
valid_input?(internal_type, value)
end
def valid_input?(_type, nil) do
true
end
def valid_input?(%{parse: parse}, value) do
case parse.(value) do
{:ok, _} -> true
:error -> false
end
end
def valid_input?(_, _) do
true
end
end