defmodule GraphQL.Node do
@moduledoc """
Functions to create all different types of nodes of a GraphQL operation.
Usually, this module should not be used directly, since it is easier to use
the function from `GraphQL.QueryBuilder`.
"""
@enforce_keys [:node_type]
defstruct node_type: nil,
name: nil,
alias: nil,
type: nil,
arguments: nil,
nodes: nil,
directives: nil
@typedoc """
The GraphQL query element that this node represents.
The four node types are:
- field: a single field of a GraphQL schema, may have arguments and other nodes
- fragment_ref: a reference to a fragment, used inside fields to import fragment fields
- fragment: a fragment definition, with name, type and fields
- inline_fragment: much like a fragment, but being inline, it does not need a name
"""
@type node_type :: :field | :fragment_ref | :fragment | :inline_fragment
@typedoc """
A GraphQL identifier that is not a GraphQL keyword (like mutation, query and fragment)
Used to identify fields, aliases and fragments.
"""
@type name :: String.t() | atom()
@typedoc """
A two-element tuple where the first position is the name of the field and the
second element is the alias of the field.
"""
@type name_and_alias :: {name(), name()}
@typedoc """
A struct representing a GraphQL operation node.
A %Node{} struct can be represent a field, a fragment, an inline fragment or a
fragment reference, identified by the `:node_type` field.
The `name` represents how this node is identified within the GraphQL operation.
The `alias` is only used when the `:node_type` is `:field`, and as the name
suggests, represents the alias of the field's name.
The `arguments` is a map with all the arguments used by a node, and it's only
valid when thew `:node_type` is `:field`.
The `type` is only used when `:node_type` is `:fragment` or `:inline_fragment`,
and represents the GraphQL type of the fragment.
The `nodes` is a list of child nodes, that can used to query for complex
objects.
The `directives` field is an enum with all the graphQL directives to be
applied on a node node.
"""
@type t :: %__MODULE__{
node_type: node_type(),
name: name(),
alias: name(),
type: String.t(),
arguments: map() | Keyword.t(),
nodes: [t()],
directives: map() | Keyword.t()
}
@doc """
Creates a simple field, with no arguments or sub nodes.
The `name` parameter can be an atom or string, or a two-element tuple with
atoms or strings, where the first element is the actual name of the field and
the second element is the alias of the field.
## GraphQL example
A query with a simple field inside another field:
```
query {
user {
id <---- Simple field
}
}
```
A query with a simple field with an alias:
```
query {
user {
theId: id <---- Simple field with alias
}
}
```
## Examples
iex> field(:my_field)
%GraphQL.Node{node_type: :field, name: :my_field}
iex> field({:my_field, "field_alias"})
%GraphQL.Node{node_type: :field, name: :my_field, alias: "field_alias"}
"""
@spec field(name() | name_and_alias()) :: t()
def field(name_spec)
def field({name, an_alias}) do
%__MODULE__{
node_type: :field,
name: name,
alias: an_alias
}
end
def field(name) do
%__MODULE__{
node_type: :field,
name: name
}
end
@doc """
Creates a field with arguments and sub nodes.
The `name` parameter can be an atom or string, or a two-element tuple with
atoms or strings, where the first element is the actual name of the field and
the second element is the alias of the field.
The `arguments` parameter is a map.
The `nodes` argument is a list of `%GraphQL.Node{}` structs.
## GraphQL Example
A query with a field that has arguments, an alias and subfields
```
query {
someObject: object(slug: "the-object") { <----- Field with an alias and arguments
field <----- Sub field
anotherField <----- Sub field
}
}
```
## Examples
iex> field(:my_field, %{id: "id"}, [ field(:subfield) ] )
%GraphQL.Node{node_type: :field, name: :my_field, arguments: %{id: "id"}, nodes: [%GraphQL.Node{node_type: :field, name: :subfield}]}
iex> field({:my_field, "field_alias"}, %{id: "id"}, [ field(:subfield) ] )
%GraphQL.Node{node_type: :field, name: :my_field, alias: "field_alias", arguments: %{id: "id"}, nodes: [%GraphQL.Node{node_type: :field, name: :subfield}]}
"""
@spec field(name() | name_and_alias(), map(), [t()], [any()]) :: t()
def field(name_spec, arguments, nodes, directives \\ nil)
def field({name, an_alias}, arguments, nodes, directives) do
%__MODULE__{
node_type: :field,
name: name,
alias: an_alias,
arguments: arguments,
nodes: nodes,
directives: directives
}
end
def field(name, arguments, nodes, directives) do
%__MODULE__{
node_type: :field,
name: name,
arguments: arguments,
nodes: nodes,
directives: directives
}
end
@doc """
Creates a reference to a fragment.
A fragment reference is used inside a field to import the fields of a fragment.
## GraphQL Example
```
query {
object {
...fieldsFromFragment <----- Fragment Reference
}
}
```
## Examples
iex> fragment("myFields")
%GraphQL.Node{node_type: :fragment_ref, name: "myFields"}
"""
@spec fragment(name()) :: t()
def fragment(name) do
%__MODULE__{
node_type: :fragment_ref,
name: name
}
end
@doc """
Creates a fragment.
A fragment is used to share fields between other fields
## GraphQL Example
```
query {
object {
...fieldsFromFragment
}
}
fragment fieldsFromFragment on Type { <------ Fragment
field1
field2
}
```
## Examples
iex> fragment("myFields", "SomeType", [field(:field)])
%GraphQL.Node{node_type: :fragment, name: "myFields", type: "SomeType", nodes: [%GraphQL.Node{node_type: :field, name: :field}]}
"""
@spec fragment(name(), name(), [t()]) :: t()
def fragment(name, type, fields) do
%__MODULE__{
node_type: :fragment,
name: name,
type: type,
nodes: fields
}
end
@doc """
Creates an inline fragment.
An inline fragment is used to conditionally add fields on another field depending
on its type
## GraphQL Example
```
query {
object {
... on Type { <------ Inline Fragment
field1
field2
}
}
}
```
## Examples
iex> inline_fragment("SomeType", [field(:field)])
%GraphQL.Node{node_type: :inline_fragment, type: "SomeType", nodes: [%GraphQL.Node{node_type: :field, name: :field}]}
"""
@spec inline_fragment(name(), [t()]) :: t()
def inline_fragment(type, fields) do
%__MODULE__{
node_type: :inline_fragment,
type: type,
nodes: fields
}
end
end