Packages

A cleaner DSL for GraphQL schema definitions built on Absinthe

Current section

Files

Jump to
green_fairy lib green_fairy deferred interface.ex
Raw

lib/green_fairy/deferred/interface.ex

defmodule GreenFairy.Deferred.Interface do
@moduledoc """
Deferred GraphQL interface definition.
Like `GreenFairy.Deferred.Type`, this stores definitions as pure data
with no compile-time dependencies on implementing types.
## Usage
defmodule MyApp.GraphQL.Interfaces.Node do
use GreenFairy.Deferred.Interface
@desc "An object with an ID"
interface "Node" do
field :id, non_null(:id)
end
end
The resolve_type function is automatically generated based on registered
implementations in the schema compiler.
"""
alias GreenFairy.Deferred.Definition
@doc false
defmacro __using__(_opts) do
quote do
import GreenFairy.Deferred.Interface, only: [interface: 2, interface: 3]
Module.register_attribute(__MODULE__, :desc, accumulate: false)
Module.register_attribute(__MODULE__, :deferred_interface_def, accumulate: false)
Module.register_attribute(__MODULE__, :deferred_fields, accumulate: true)
@before_compile GreenFairy.Deferred.Interface
end
end
@doc """
Defines a GraphQL interface.
## Options
- `:description` - Interface description (can also use @desc)
- `:resolve_type` - Custom resolve_type function (optional, auto-generated by default)
"""
defmacro interface(name, opts \\ [], do: block) do
quote do
@deferred_interface_def %{
name: unquote(name),
description: @desc || unquote(opts[:description]),
resolve_type: unquote(Macro.escape(opts[:resolve_type]))
}
@desc nil
import GreenFairy.Deferred.Interface, only: [field: 2, field: 3]
unquote(block)
end
end
@doc "Defines a field on the interface."
defmacro field(name, type, opts \\ []) do
quote do
@deferred_fields %Definition.Field{
name: unquote(name),
type: unquote(Macro.escape(type)),
description: @desc || unquote(opts[:description]),
null: Keyword.get(unquote(opts), :null, true),
args: unquote(Macro.escape(opts[:args])),
deprecation_reason: unquote(opts[:deprecation_reason])
}
@desc nil
end
end
@doc false
defmacro __before_compile__(env) do
interface_def = Module.get_attribute(env.module, :deferred_interface_def)
fields = Module.get_attribute(env.module, :deferred_fields) || []
identifier = GreenFairy.Naming.to_identifier(interface_def[:name])
definition = %Definition.Interface{
name: interface_def[:name],
identifier: identifier,
module: env.module,
description: interface_def[:description],
fields: Enum.reverse(fields),
resolve_type: interface_def[:resolve_type]
}
quote do
@doc false
def __green_fairy_definition__ do
unquote(Macro.escape(definition))
end
@doc false
def __green_fairy_kind__, do: :interface
@doc false
def __green_fairy_identifier__, do: unquote(identifier)
# Register with the deferred registry when module is loaded
GreenFairy.Deferred.Registry.register(__MODULE__, :interface)
end
end
end