defmodule Fennel.Pipeline do
@moduledoc """
For simplicity, we match phases from Absinthe.Pipeline.
"""
# No need to execute everything during query validation
# in the future, possibly disable more elements if they're not needed
# like telemetry?
alias Absinthe.Phase
def prepare(options) do
pipeline =
Keyword.fetch!(options, :schema)
|> Absinthe.Pipeline.for_document(options)
|> Absinthe.Pipeline.upto(Phase.Document.CurrentOperation)
|> Absinthe.Pipeline.reject(
&(&1 in [
Phase.Document.Validation.ProvidedAnOperation
])
)
|> (fn pipeline ->
if Keyword.get(options, :add_typenames) do
Absinthe.Pipeline.insert_after(
pipeline,
Phase.Parse,
Fennel.Phase.Document.InsertTypeName
)
else
pipeline
end
end).()
pipeline ++ [Phase.Document.Result]
end
@doc """
Arguments validation is mostly disabled here.
"""
def validation(options) do
[
Phase.Document.Uses,
# Validate Document Structure
{Phase.Document.Validation.NoFragmentCycles, options},
Phase.Document.Validation.LoneAnonymousOperation,
# {Phase.Document.Validation.SelectedCurrentOperation, options},
Phase.Document.Validation.KnownFragmentNames,
Phase.Document.Validation.NoUndefinedVariables,
Phase.Document.Validation.NoUnusedVariables,
# Phase.Document.Validation.NoUnusedFragments,
Phase.Document.Validation.UniqueFragmentNames,
Phase.Document.Validation.UniqueOperationNames,
Phase.Document.Validation.UniqueVariableNames,
# Apply Input
{Phase.Document.Context, options},
{Phase.Document.Variables, options},
# Phase.Document.Validation.ProvidedNonNullVariables,
# Phase.Document.Arguments.Normalize,
# Map to Schema
{Phase.Schema, options},
# Ensure Types
Phase.Validation.KnownTypeNames,
# Phase.Document.Arguments.VariableTypesMatch,
# 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.Document.Validation.KnownDirectives,
Phase.Document.Validation.RepeatableDirectives,
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,
Phase.Document.Validation.OnlyOneSubscription,
Fennel.Phase.Document.Validation.DeprecatedFields,
# Check Validation
{Phase.Document.Validation.Result, options},
# Format Result
Phase.Document.Result
]
end
def runner(options) do
[
Phase.Document.Validation.ProvidedAnOperation,
Phase.Document.Uses,
# Validate Document Structure
{Phase.Document.Validation.NoFragmentCycles, options},
Phase.Document.Validation.LoneAnonymousOperation,
{Phase.Document.Validation.SelectedCurrentOperation, options},
Phase.Document.Validation.KnownFragmentNames,
Phase.Document.Validation.NoUndefinedVariables,
Phase.Document.Validation.NoUnusedVariables,
Phase.Document.Validation.NoUnusedFragments,
Phase.Document.Validation.UniqueFragmentNames,
Phase.Document.Validation.UniqueOperationNames,
Phase.Document.Validation.UniqueVariableNames,
# Apply Input
{Phase.Document.Context, options},
{Phase.Document.Variables, options},
Phase.Document.Validation.ProvidedNonNullVariables,
Phase.Document.Arguments.Normalize,
# Map to Schema
{Phase.Schema, options},
# Ensure Types
Phase.Validation.KnownTypeNames,
Phase.Document.Arguments.VariableTypesMatch,
# 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.Document.Validation.KnownDirectives,
Phase.Document.Validation.RepeatableDirectives,
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,
Phase.Document.Validation.OnlyOneSubscription,
# Check Validation
{Phase.Document.Validation.Result, options},
# Prepare for Execution
Phase.Document.Arguments.Data,
# Apply Directives
Phase.Document.Directives,
# Analyse Complexity
# {Phase.Document.Complexity.Analysis, options},
# {Phase.Document.Complexity.Result, options},
# Execution
{Fennel.Phase.Document.Execution.Subscription.Subscribe, options},
{Fennel.Phase.Document.Execution.Cache.Fetch, options},
{Fennel.Phase.Document.Execution.Run.Fetch, options},
{Phase.Document.Execution.Resolution, options},
{Fennel.Phase.Document.Execution.Cache.Store, options},
# Format Result
Phase.Document.Result,
{Phase.Telemetry, Keyword.put(options, :event, [:execute, :operation, :stop])}
]
end
def options(client, options) do
default_options = client.default_options()
default_options
|> Keyword.merge(options)
|> Keyword.put_new_lazy(:variables, fn ->
if ctx = Keyword.get(options, :context) do
ctx.variables
else
%{}
end
end)
|> Keyword.put_new(:add_typenames, Fennel.config_add_typenames())
|> Absinthe.Pipeline.options()
end
end