Packages
absinthe
1.2.6
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/pipeline.ex
defmodule Absinthe.Pipeline do
@moduledoc """
Execute a pipeline of phases.
A pipeline is merely a list of phases. This module contains functions for building,
modifying, and executing pipelines of phases.
"""
alias Absinthe.Phase
require Logger
@type data_t :: any
@type phase_config_t :: Phase.t | {Phase.t, Keyword.t}
@type t :: [phase_config_t | [phase_config_t]]
@spec run(data_t, t) :: {:ok, data_t, [Phase.t]} | {:error, String.t, [Phase.t]}
def run(input, pipeline) do
pipeline
|> List.flatten
|> run_phase(input)
end
@defaults [
adapter: Absinthe.Adapter.LanguageConventions,
operation_name: nil,
variables: %{},
context: %{},
root_value: %{},
validation_result_phase: Phase.Document.Validation.Result,
result_phase: Phase.Document.Result,
jump_phases: true,
]
@spec for_document(Absinthe.Schema.t) :: t
@spec for_document(Absinthe.Schema.t, Keyword.t) :: t
def for_document(schema, options \\ []) do
options = @defaults
|> Keyword.merge(Keyword.put(options, :schema, schema))
[
# Parse Document
{Phase.Parse, options},
# Convert to Blueprint
Phase.Blueprint,
# Find Current Operation (if any)
{Phase.Document.Validation.ProvidedAnOperation, options},
{Phase.Document.CurrentOperation, options},
# Mark Fragment/Variable Usage
Phase.Document.Uses,
# Validate Document Structure
{Phase.Document.Validation.NoFragmentCycles, options},
Phase.Document.Validation.LoneAnonymousOperation,
Phase.Document.Validation.SelectedCurrentOperation,
Phase.Document.Validation.KnownFragmentNames,
Phase.Document.Validation.NoUndefinedVariables,
Phase.Document.Validation.NoUnusedVariables,
Phase.Document.Validation.UniqueFragmentNames,
Phase.Document.Validation.UniqueOperationNames,
Phase.Document.Validation.UniqueVariableNames,
# Apply Input
{Phase.Document.Variables, options},
Phase.Document.Arguments.Normalize,
# Map to Schema
{Phase.Schema, options},
# Ensure Types
Phase.Validation.KnownTypeNames,
# Process Arguments
Phase.Document.Arguments.CoerceEnums,
Phase.Document.Arguments.CoerceLists,
{Phase.Document.Arguments.Parse, options},
Phase.Document.MissingVariables,
Phase.Document.MissingLiterals,
Phase.Document.Arguments.FlagInvalid,
# Validate Full Document
Phase.Validation.KnownDirectives,
Phase.Document.Validation.ScalarLeafs,
Phase.Document.Validation.VariablesAreInputTypes,
Phase.Document.Validation.ArgumentsOfCorrectType,
Phase.Document.Validation.KnownArgumentNames,
Phase.Document.Validation.ProvidedNonNullArguments,
Phase.Document.Validation.UniqueArgumentNames,
Phase.Document.Validation.UniqueInputFieldNames,
Phase.Document.Validation.FieldsOnCorrectType,
# Check Validation
{Phase.Document.Validation.Result, options},
# Apply Directives
Phase.Document.Arguments.Data,
Phase.Document.Directives,
# Prepare for Execution
Phase.Document.CascadeInvalid,
Phase.Document.Flatten,
# Analyse Complexity
{Phase.Document.Complexity.Analysis, options},
{Phase.Document.Complexity.Result, options},
# Execution
{Phase.Document.Execution.Resolution, options},
# Format Result
Phase.Document.Result
]
end
@defaults [
adapter: Absinthe.Adapter.LanguageConventions
]
@spec for_schema(nil | Absinthe.Schema.t) :: t
@spec for_schema(nil | Absinthe.Schema.t, Keyword.t) :: t
def for_schema(prototype_schema, options \\ []) do
options = @defaults
|> Keyword.merge(Keyword.put(options, :schema, prototype_schema))
[
Phase.Parse,
Phase.Blueprint,
{Phase.Schema, options},
Phase.Validation.KnownTypeNames,
Phase.Validation.KnownDirectives
]
end
@doc """
Return the part of a pipeline before a specific phase.
"""
@spec before(t, atom) :: t
def before(pipeline, phase) do
result = List.flatten(pipeline)
|> Enum.take_while(&(!match_phase?(phase, &1)))
case result do
^pipeline ->
raise RuntimeError, "Could not find phase #{phase}"
_ ->
result
end
end
@doc """
Return the part of a pipeline after (and including) a specific phase.
"""
@spec from(t, atom) :: t
def from(pipeline, phase) do
result = List.flatten(pipeline)
|> Enum.drop_while(&(!match_phase?(phase, &1)))
case result do
[] ->
raise RuntimeError, "Could not find phase #{phase}"
_ ->
result
end
end
# Whether a phase configuration is for a given phase
@spec match_phase?(Phase.t, phase_config_t) :: boolean
defp match_phase?(phase, phase), do: true
defp match_phase?(phase, {phase, _}), do: true
defp match_phase?(_, _), do: false
@doc """
Return the part of a pipeline up to and including a specific phase.
"""
@spec upto(t, atom) :: t
def upto(pipeline, phase) do
beginning = before(pipeline, phase)
item = get_in(pipeline, [Access.at(length(beginning))])
beginning ++ [item]
end
@spec without(t, Phase.t) :: t
def without(pipeline, phase) do
pipeline
|> Enum.filter(&(not match_phase?(phase, &1)))
end
@spec insert_before(t, Phase.t, Phase.t) :: t
def insert_before(pipeline, phase, additional) do
beginning = before(pipeline, phase)
beginning ++ [additional] ++ (pipeline -- beginning)
end
@spec insert_before(t, Phase.t, Phase.t) :: t
def insert_after(pipeline, phase, additional) do
beginning = upto(pipeline, phase)
beginning ++ [additional] ++ (pipeline -- beginning)
end
@spec reject(t, Regex.t) :: t
def reject(pipeline, pattern) do
Enum.reject(pipeline, fn
{phase, _} ->
Regex.match?(pattern, Atom.to_string(phase))
phase ->
Regex.match?(pattern, Atom.to_string(phase))
end)
end
@spec run_phase(t, data_t, [Phase.t]) :: {:ok, data_t, [Phase.t]} | {:error, String.t, [Phase.t]}
def run_phase(pipeline, input, done \\ [])
def run_phase([], input, done) do
{:ok, input, done}
end
def run_phase([phase_config | todo], input, done) do
{phase, options} = phase_invocation(phase_config)
case phase.run(input, options)do
{:ok, result} ->
run_phase(todo, result, [phase | done])
{:jump, result, destination_phase} when is_atom(destination_phase) ->
run_phase(from(todo, destination_phase), result, [phase | done])
{:insert, result, extra_pipeline} ->
run_phase(List.wrap(extra_pipeline) ++ todo, result, [phase | done])
{:replace, result, final_pipeline} ->
run_phase(List.wrap(final_pipeline), result, [phase | done])
{:error, message} ->
{:error, message, [phase | done]}
_ ->
{:error, "Last phase did not return a valid result tuple.", [phase | done]}
end
end
@spec phase_invocation(phase_config_t) :: {Phase.t, list}
defp phase_invocation({phase, options}) when is_list(options) do
{phase, options}
end
defp phase_invocation(phase) do
{phase, []}
end
end