Packages
absinthe
1.2.0-alpha0
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/phase/schema.ex
defmodule Absinthe.Phase.Schema do
@moduledoc """
Populate all schema nodes and the adapter for the blueprint tree. If the
blueprint tree is a _schema_ tree, this schema is the meta schema (source of
IDL directives, etc).
Note that no validation occurs in this phase.
"""
use Absinthe.Phase
alias Absinthe.{Blueprint, Type, Schema}
@spec run(Blueprint.t, Keyword.t) :: {:ok, Blueprint.t}
def run(input, options \\ []) do
schema = Keyword.fetch!(options, :schema)
adapter = Keyword.get(options, :adapter, Absinthe.Adapter.LanguageConventions)
do_run(input, %{schema: schema, adapter: adapter})
end
defp do_run(input, %{schema: schema, adapter: adapter}) do
result = Blueprint.prewalk(input, &handle_node(&1, schema, adapter))
{:ok, result}
end
@spec handle_node(Blueprint.node_t, Absinthe.Schema.t, Absinthe.Adapter.t) :: Blueprint.node_t
defp handle_node(%Blueprint{} = node, schema, adapter) do
%{node | schema: schema, adapter: adapter}
end
defp handle_node(%Blueprint.Document.Fragment.Named{} = node, schema, adapter) do
schema_node = schema.__absinthe_type__(node.type_condition.name)
selections_with_schema = Enum.map(node.selections, &selection_with_schema_node(&1, schema_node, schema, adapter))
%{node | schema_node: schema_node, selections: selections_with_schema}
end
defp handle_node(%Blueprint.Document.VariableDefinition{type: type_reference} = node, schema, _) do
type = type_reference_to_type(type_reference, schema)
if Type.unwrap(type) do
%{node | schema_node: type}
else
node
end
end
defp handle_node(%Blueprint.Document.Fragment.Inline{type_condition: nil} = node, _, _) do
node
end
defp handle_node(%Blueprint.Document.Fragment.Inline{type_condition: %{name: _}} = node, schema, adapter) do
schema_node = schema.__absinthe_type__(node.type_condition.name)
selections_with_schema = Enum.map(node.selections, &selection_with_schema_node(&1, schema_node, schema, adapter))
%{node | schema_node: schema_node, selections: selections_with_schema}
end
defp handle_node(%Blueprint.Directive{name: name} = node, schema, adapter) do
internal_name = adapter.to_internal_name(name, :directive)
schema_node = schema.__absinthe_directive__(internal_name)
arguments = Enum.map(node.arguments, &argument_with_schema_node(&1, schema_node, schema, adapter))
%{node | schema_node: schema_node, arguments: arguments}
end
defp handle_node(%Blueprint.Document.Operation{type: op_type} = node, schema, adapter) do
schema_node = schema.__absinthe_type__(op_type)
selections_with_schema = Enum.map(node.selections, &selection_with_schema_node(&1, schema_node, schema, adapter))
%{node | schema_node: schema_node, selections: selections_with_schema}
end
defp handle_node(node, _, _) do
node
end
@type_mapping %{
Blueprint.TypeReference.List => Type.List,
Blueprint.TypeReference.NonNull => Type.NonNull
}
defp type_reference_to_type(%Blueprint.TypeReference.Name{} = node, schema) do
Schema.lookup_type(schema, node.name)
end
for {blueprint_type, core_type} <- @type_mapping do
defp type_reference_to_type(%unquote(blueprint_type){} = node, schema) do
inner = type_reference_to_type(node.of_type, schema)
%unquote(core_type){of_type: inner}
end
end
# Given a blueprint field node, fill in its schema node
#
# (If it's a fragment spread or inline fragment, we skip it, as the
# appropriate `handle_node` for the fragment type will call this itself.)
@spec selection_with_schema_node(Blueprint.Document.selection_t, Type.t, Absinthe.Schema.t, Absinthe.Adapter.t) :: Type.t
defp selection_with_schema_node(%Blueprint.Document.Field{} = node, parent_schema_node, schema, adapter) do
schema_node = find_schema_field(parent_schema_node, node.name, schema, adapter)
if schema_node do
selections = Enum.map(node.selections, &selection_with_schema_node(&1, schema_node, schema, adapter))
arguments = Enum.map(node.arguments, &argument_with_schema_node(&1, schema_node, schema, adapter))
%{node | schema_node: schema_node, selections: selections, arguments: arguments}
else
node
end
end
# Inline fragments use their type condition to determine child field schema
# nodes. For inline fragments without type conditions, we set it to that of
# its parent here so the `handle_node` that takes care of the inline fragment
#
defp selection_with_schema_node(%Blueprint.Document.Fragment.Inline{type_condition: nil} = node, parent_schema_node, schema, _) do
base_type = case parent_schema_node do
%{type: type} ->
type
other ->
other
end
type = Type.unwrap(Type.expand(base_type, schema))
%{node | type_condition: %Blueprint.TypeReference.Name{name: type.name}}
end
defp selection_with_schema_node(node, _, _, _) do
node
end
# Given a schema type, lookup a child field definition
@spec find_schema_field(nil | Type.t, String.t, Absinthe.Schema.t, Absinthe.Adapter.t) :: nil | Type.Field.t
defp find_schema_field(_, "__" <> introspection_field, _, _) do
Absinthe.Introspection.Field.meta(introspection_field)
end
defp find_schema_field(%{of_type: type}, name, schema, adapter) do
find_schema_field(type, name, schema, adapter)
end
defp find_schema_field(%{fields: fields}, name, _, adapter) do
internal_name = adapter.to_internal_name(name, :field)
fields
|> Map.values
|> Enum.find(fn
%{name: ^internal_name} ->
true
_ ->
false
end)
end
defp find_schema_field(%Type.Field{type: maybe_wrapped_type}, name, schema, adapter) do
type = Type.unwrap(maybe_wrapped_type)
|> schema.__absinthe_type__
find_schema_field(type, name, schema, adapter)
end
defp find_schema_field(_, _, _, _) do
nil
end
# Given a blueprint argument node, fill in its schema node
@spec argument_with_schema_node(Blueprint.Input.Argument.t, Type.t, Absinthe.Schema.t, Absinthe.Adapter.t) :: Type.t
defp argument_with_schema_node(node, nil, _, _) do
node
end
defp argument_with_schema_node(%{name: name} = node, parent_schema_node, schema, adapter) do
schema_node = find_schema_argument(parent_schema_node, name, adapter)
normalized_value = value_with_schema_node(node.normalized_value, schema_node, schema, adapter)
%{node | schema_node: schema_node, normalized_value: normalized_value}
end
# Given a blueprint provided value node, fill in its schema node
@spec value_with_schema_node(Blueprint.Input.t, Type.t, Absinthe.Schema.t, Absinthe.Adapter.t) :: Type.Input.t
defp value_with_schema_node(node, nil, _, _) do
node
end
defp value_with_schema_node(nil, _, _, _) do
nil
end
defp value_with_schema_node(node, %Type.NonNull{of_type: type}, schema, adapter) do
value_with_schema_node(node, type, schema, adapter)
end
defp value_with_schema_node(node, %Type.List{of_type: type}, schema, adapter) do
value_with_schema_node(node, type, schema, adapter)
end
defp value_with_schema_node(node, %Type.Scalar{} = parent_schema_node, _, _) do
%{node | schema_node: parent_schema_node}
end
defp value_with_schema_node(node, %Type.Enum{} = parent_schema_node, _, _) do
%{node | schema_node: parent_schema_node}
end
defp value_with_schema_node(%Blueprint.Input.Object{} = node, parent_schema_node, schema, adapter) do
schema_node = expand_type(parent_schema_node, schema)
fields = Enum.map(node.fields, &input_field_with_schema_node(&1, schema_node, schema, adapter))
%{node | schema_node: schema_node, fields: fields}
end
defp value_with_schema_node(%Blueprint.Input.List{} = node, parent_schema_node, schema, adapter) do
schema_node = expand_type(parent_schema_node.type, schema)
values = Enum.map(node.values, &value_with_schema_node(&1, schema_node, schema, adapter))
%{node | schema_node: schema_node, values: values}
end
# Coerce argument-level lists
defp value_with_schema_node(%node_type{} = node, %Type.Argument{type: %Type.List{}} = type, schema, adapter) when node_type != Blueprint.Input.List do
Blueprint.Input.List.wrap(node)
|> value_with_schema_node(type, schema, adapter)
end
defp value_with_schema_node(node, parent_schema_node, schema, _) do
schema_node = expand_type(parent_schema_node.type, schema)
%{node | schema_node: schema_node}
end
# Expand type, but strip wrapping argument node
@spec expand_type(Type.t, Schema.t) :: Type.t
defp expand_type(%{type: type}, schema) do
Type.expand(type, schema)
end
defp expand_type(type, schema) do
Type.expand(type, schema)
end
@spec input_field_with_schema_node(Blueprint.Input.Field.t, Type.t, Absinthe.Schema.t, Absinthe.Adapter.t) :: Type.t
defp input_field_with_schema_node(%Blueprint.Input.Field{} = node, parent_schema_node, schema, adapter) do
schema_node = find_schema_field(parent_schema_node, node.name, schema, adapter)
value = value_with_schema_node(node.value, schema_node, schema, adapter)
%{node | schema_node: schema_node, value: value}
end
defp input_field_with_schema_node(node, _, _, _) do
node
end
# Given a schema field or directive, lookup a child argument definition
@spec find_schema_argument(nil | Type.Field.t | Type.Argument.t, String.t, Absinthe.Adapter.t) :: nil | Type.Argument.t
defp find_schema_argument(%{args: arguments}, name, adapter) do
internal_name = adapter.to_internal_name(name, :argument)
arguments
|> Map.values
|> Enum.find(fn
%{name: ^internal_name} ->
true
_ ->
false
end)
end
defp find_schema_argument(nil, _, _) do
nil
end
end