defmodule Absinthe.Relay.Node.Notation do @moduledoc """ Macros used to define Node-related schema entities See `Absinthe.Relay.Node` for examples of use. If you wish to use this module on its own without `use Absinthe.Relay` you need to include ``` @pipeline_modifier Absinthe.Relay.Schema ``` in your root schema module. """ alias Absinthe.Blueprint.Schema @doc """ Define a node interface, field, or object type for a schema. See the `Absinthe.Relay.Node` module documentation for examples. """ defmacro node({:interface, meta, [attrs]}, do: block) when is_list(attrs) do do_interface(meta, attrs, block) end defmacro node({:interface, meta, attrs}, do: block) do do_interface(meta, attrs, block) end defmacro node({:field, meta, [attrs]}, do: block) when is_list(attrs) do do_field(meta, attrs, block) end defmacro node({:field, meta, attrs}, do: block) do do_field(meta, attrs, block) end defmacro node({:object, meta, [identifier, attrs]}, do: block) when is_list(attrs) do do_object(meta, identifier, attrs, block) end defmacro node({:object, meta, [identifier]}, do: block) do do_object(meta, identifier, [], block) end defp do_interface(meta, attrs, block) do attrs = attrs || [] {id_type, attrs} = Keyword.pop(attrs, :id_type, get_id_type()) block = [interface_body(id_type), block] attrs = [:node | [attrs]] {:interface, meta, attrs ++ [[do: block]]} end defp do_field(meta, attrs, block) do attrs = attrs || [] {id_type, attrs} = Keyword.pop(attrs, :id_type, get_id_type()) {:field, meta, [:node, :node, attrs ++ [do: [field_body(id_type), block]]]} end defp do_object(meta, identifier, attrs, block) do {id_fetcher, attrs} = Keyword.pop(attrs, :id_fetcher) {id_type, attrs} = Keyword.pop(attrs, :id_type, get_id_type()) block = [ quote do private(:absinthe_relay, :node, {:fill, unquote(__MODULE__)}) private(:absinthe_relay, :id_fetcher, unquote(id_fetcher)) end, object_body(id_fetcher, id_type), block ] {:object, meta, [identifier, attrs] ++ [[do: block]]} end def additional_types(_, _), do: [] # def fillout(:node, %Schema.ObjectTypeDefinition{} = obj) do # id_field = id_field_template() |> Map.put(:middleware, []) # %{obj | interfaces: [:node | obj.interfaces], fields: [id_field | obj.fields]} # end def fillout(_, %Schema.ObjectTypeDefinition{identifier: :faction} = obj) do obj end def fillout(_, node) do node end defp get_id_type() do Absinthe.Relay |> Application.get_env(:node_id_type, :id) end # An id field is automatically configured defp interface_body(id_type) do quote do field(:id, non_null(unquote(id_type)), description: "The ID of the object.") end end # An id arg is automatically added defp field_body(id_type) do quote do @desc "The ID of an object." arg(:id, non_null(unquote(id_type))) middleware({Absinthe.Relay.Node, :resolve_with_global_id}) end end # Automatically add: # - An id field that resolves to the generated global ID # for an object of this type # - A declaration that this implements the node interface defp object_body(id_fetcher, id_type) do quote do @desc "The ID of an object" field :id, non_null(unquote(id_type)) do middleware {Absinthe.Relay.Node, :global_id_resolver}, unquote(id_fetcher) end interface(:node) end end end