Packages
absinthe
1.7.8
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
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/absinthe/phase/schema.ex
defmodule Absinthe.Phase.Schema do
@moduledoc false
# 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
# SDL directives, etc).
#
# Note that no validation occurs in this phase.
use Absinthe.Phase
alias Absinthe.{Blueprint, Type, Schema}
# The approach here is pretty simple.
# We start at the top blueprint node and set the appropriate schema node on operations
# directives and so forth.
#
# Then, as `prewalk` walks down the tree we hit a node. If that node has a schema_node
# set by its parent, we walk to its children and set the schema node on those children.
# We do not need to walk any further because `prewalk` will do that for us.
#
# Thus at each node we need only concern ourselves with immediate children.
@spec run(Blueprint.t(), Keyword.t()) :: {:ok, Blueprint.t()}
def run(input, options \\ []) do
{input, schema} = apply_settings(input, Map.new(options))
result =
input
|> update_context(schema)
|> Blueprint.prewalk(&handle_node(&1, schema, input.adapter))
{:ok, result}
end
# Set schema and adapter settings on the blueprint appropriate to whether we're
# applying a normal schema for a document or a prototype schema used to define
# a schema.
defp apply_settings(input, %{prototype_schema: schema} = options) do
adapter = Map.get(options, :adapter, Absinthe.Adapter.LanguageConventions)
{%{input | prototype_schema: schema, adapter: adapter}, schema}
end
defp apply_settings(input, options) do
adapter = Map.get(options, :adapter, Absinthe.Adapter.LanguageConventions)
{%{input | schema: options.schema, adapter: adapter}, options.schema}
end
defp update_context(input, nil), do: input
defp update_context(input, schema) do
context = schema.context(input.execution.context)
put_in(input.execution.context, context)
end
defp handle_node(%Blueprint{} = node, schema, adapter) do
set_children(node, schema, adapter)
end
defp handle_node(%Absinthe.Blueprint.Document.VariableDefinition{} = node, _, _) do
{:halt, node}
end
defp handle_node(node, schema, adapter) do
set_children(node, schema, adapter)
end
defp set_children(parent, schema, adapter) do
Blueprint.prewalk(parent, fn
^parent -> parent
%Absinthe.Blueprint.Input.Variable{} = child -> {:halt, child}
child -> {:halt, set_schema_node(child, parent, schema, adapter)}
end)
end
# Do note, the `parent` arg is the parent blueprint node, not the parent's schema node.
defp set_schema_node(
%Blueprint.Document.Fragment.Inline{type_condition: %{name: type_name} = condition} =
node,
_parent,
schema,
_adapter
) do
schema_node = Absinthe.Schema.lookup_type(schema, type_name)
%{node | schema_node: schema_node, type_condition: %{condition | schema_node: schema_node}}
end
defp set_schema_node(%Blueprint.Directive{name: name} = node, _parent, schema, adapter) do
%{node | schema_node: find_schema_directive(name, schema, adapter)}
end
defp set_schema_node(
%Blueprint.Document.Operation{type: op_type} = node,
_parent,
schema,
_adapter
) do
%{node | schema_node: Absinthe.Schema.lookup_type(schema, op_type)}
end
defp set_schema_node(
%Blueprint.Document.Fragment.Named{type_condition: %{name: type_name} = condition} =
node,
_parent,
schema,
_adapter
) do
schema_node = Absinthe.Schema.lookup_type(schema, type_name)
%{node | schema_node: schema_node, type_condition: %{condition | schema_node: schema_node}}
end
defp set_schema_node(
%Blueprint.Document.VariableDefinition{type: type_reference} = node,
_parent,
schema,
_adapter
) do
wrapped =
type_reference
|> type_reference_to_type(schema)
%{node | schema_node: wrapped}
end
defp set_schema_node(node, %{schema_node: nil}, _, _) do
# if we don't know the parent schema node, and we aren't one of the earlier nodes,
# then we can't know our schema node.
node
end
defp set_schema_node(
%Blueprint.Document.Fragment.Inline{type_condition: nil} = node,
parent,
schema,
adapter
) do
type =
case parent.schema_node do
%{type: type} -> type
other -> other
end
|> Type.expand(schema)
|> Type.unwrap()
set_schema_node(
%{node | type_condition: %Blueprint.TypeReference.Name{name: type.name, schema_node: type}},
parent,
schema,
adapter
)
end
defp set_schema_node(%Blueprint.Document.Field{} = node, parent, schema, adapter) do
%{node | schema_node: find_schema_field(parent.schema_node, node.name, schema, adapter)}
end
defp set_schema_node(%Blueprint.Input.Argument{name: name} = node, parent, _schema, adapter) do
schema_node = find_schema_argument(parent.schema_node, name, adapter)
%{node | schema_node: schema_node}
end
defp set_schema_node(%Blueprint.Document.Fragment.Spread{} = node, _, _, _) do
node
end
defp set_schema_node(%Blueprint.Input.Field{} = node, parent, schema, adapter) do
case node.name do
"__" <> _ ->
%{node | schema_node: nil}
name ->
%{node | schema_node: find_schema_field(parent.schema_node, name, schema, adapter)}
end
end
defp set_schema_node(%Blueprint.Input.List{} = node, parent, _schema, _adapter) do
case Type.unwrap_non_null(parent.schema_node) do
%{of_type: internal_type} ->
%{node | schema_node: internal_type}
_ ->
node
end
end
defp set_schema_node(%Blueprint.Input.Value{} = node, parent, schema, _) do
case parent.schema_node do
%Type.Argument{type: type} ->
%{node | schema_node: type |> Type.expand(schema)}
%Absinthe.Type.Field{type: type} ->
%{node | schema_node: type |> Type.expand(schema)}
type ->
%{node | schema_node: type |> Type.expand(schema)}
end
end
defp set_schema_node(%{schema_node: nil} = node, %Blueprint.Input.Value{} = parent, _schema, _) do
%{node | schema_node: parent.schema_node}
end
defp set_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(&match?(%{name: ^internal_name}, &1))
end
# Given a name, lookup a schema directive
@spec find_schema_directive(String.t(), Absinthe.Schema.t(), Absinthe.Adapter.t()) ::
nil | Type.Directive.t()
defp find_schema_directive(name, schema, adapter) do
internal_name = adapter.to_internal_name(name, :directive)
schema.__absinthe_directive__(internal_name)
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(%{of_type: type}, name, schema, adapter) do
find_schema_field(type, name, schema, adapter)
end
defp find_schema_field(%{fields: fields}, name, _schema, adapter) do
internal_name = adapter.to_internal_name(name, :field)
fields
|> Map.values()
|> Enum.find(&match?(%{name: ^internal_name}, &1))
end
defp find_schema_field(%Type.Field{type: maybe_wrapped_type}, name, schema, adapter) do
type =
Type.unwrap(maybe_wrapped_type)
|> schema.__absinthe_lookup__
find_schema_field(type, name, schema, adapter)
end
defp find_schema_field(_, _, _, _) do
nil
end
@type_mapping %{
Blueprint.TypeReference.List => Type.List,
Blueprint.TypeReference.NonNull => Type.NonNull
}
defp type_reference_to_type(%Blueprint.TypeReference.Name{name: name}, schema) do
Schema.lookup_type(schema, 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
end