defmodule Absinthe.Federation.Schema do
@moduledoc """
Module for injecting custom `Absinthe.Phase`s for adding federated types and directives.
## Example
defmodule MyApp.MySchema do
use Absinthe.Schema
+ use Absinthe.Federation.Schema
query do
...
end
end
"""
alias Absinthe.Federation.Schema.Prototype, as: FederationPrototype
alias Absinthe.Phase.Schema.TypeImports
alias Absinthe.Pipeline
defmacro __using__(opts) do
do_using(opts)
end
defp do_using(opts) do
has_custom_prototype? = Keyword.has_key?(opts, :prototype_schema)
prototype_schema = if has_custom_prototype?, do: opts[:prototype_schema], else: FederationPrototype
quote do
@pipeline_modifier unquote(__MODULE__)
use Absinthe.Federation.Notation
if Keyword.has_key?(unquote(opts), :skip_prototype) do
raise ArgumentError,
":skip_prototype option is no longer supported. \n" <>
"Please provide your custom prototype module with the :prototype_schema option instead. \n" <>
"Example: `use Absinthe.Federation.Schema, prototype_schema: MySchema.Prototype`"
end
if unquote(has_custom_prototype?) do
@prototype_schema unquote(prototype_schema)
import_types unquote(prototype_schema),
except: unquote(Map.keys(FederationPrototype.__absinthe_types__()))
import_directives unquote(prototype_schema),
except: unquote(Map.keys(FederationPrototype.__absinthe_directives__()))
else
@prototype_schema FederationPrototype
end
import_types Absinthe.Federation.Types
end
end
@doc """
Injects custom compile-time `Absinthe.Phase`
"""
def pipeline(pipeline) do
Pipeline.insert_after(pipeline, TypeImports, [
__MODULE__.Phase.AddFederatedTypes,
__MODULE__.Phase.AddFederatedDirectives
# __MODULE__.Phase.Validation.KeyFieldsMustExist,
# __MODULE__.Phase.Validation.KeyFieldsMustBeValidWhenExtends
])
end
@spec remove_federated_types_pipeline(schema :: Absinthe.Schema.t()) :: Absinthe.Pipeline.t()
def remove_federated_types_pipeline(schema) do
schema
|> Absinthe.Pipeline.for_schema(prototype_schema: schema.__absinthe_prototype_schema__())
|> Absinthe.Pipeline.upto({Absinthe.Phase.Schema.Validation.Result, pass: :final})
|> Absinthe.Schema.apply_modifiers(schema)
# TODO: Due to an issue found with rendering the SDL we had to revert this functionality
# https://github.com/DivvyPayHQ/absinthe_federation/issues/28
# |> Absinthe.Pipeline.without(__MODULE__.Phase.AddFederatedTypes)
|> Absinthe.Pipeline.insert_before(
Absinthe.Phase.Schema.ApplyDeclaration,
__MODULE__.Phase.RemoveResolveReferenceFields
)
end
@spec to_federated_sdl(schema :: Absinthe.Schema.t()) :: String.t()
def to_federated_sdl(schema) do
pipeline = remove_federated_types_pipeline(schema)
# we can be assertive here, since this same pipeline was already used to
# successfully compile the schema.
{:ok, bp, _} = Absinthe.Pipeline.run(schema.__absinthe_blueprint__(), pipeline)
Absinthe.Schema.Notation.SDL.Render.inspect(bp, %{pretty: true})
end
end