Packages
cqrs_tools
0.5.13
0.5.28
0.5.27
0.5.26
0.5.25
0.5.24
0.5.23
0.5.22
0.5.21
0.5.20
0.5.19
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.30
0.2.29
0.2.28
0.2.27
0.2.26
0.2.25
0.2.24
0.2.23
0.2.22
0.2.21
0.2.20
0.2.19
0.2.18
0.2.17
0.2.16
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.1
0.1.0
A collection of handy Elixir macros for CQRS applications.
Current section
Files
Jump to
Current section
Files
lib/cqrs/absinthe.ex
if Code.ensure_loaded?(Absinthe) do
defmodule Cqrs.Absinthe do
@moduledoc """
Macros to derive queries and mutations from [Queries](`Cqrs.Query`) and [Commands](`Cqrs.Command`), respectfully.
## Configuration
* `:context_metadata_keys` - A list of the keys in the `Absinthe.Resolution` `context` to pass to queries and mutations as metadata
* `:type_mappings` - A list of mappings from ecto types to absinthe types.
config :cqrs_tools, :absinthe, context_metadata_keys: [], type_mappings: [map: :json]
## Example
defmodule ExampleApi.Types.UserTypes do
@moduledoc false
use Cqrs.Absinthe
use Absinthe.Schema.Notation
alias Example.Queries.{ListUsers, GetUser}
alias Example.Users.Protocol.{CreateUser, SuspendUser, ReinstateUser}
import ExampleApi.Resolvers.UserResolver
derive_enum :user_status, {ListUsers, :status}
object :user do
field :id, :id
field :name, :string
field :email, :string
field :status, :user_status
derive_query GetUserFriends, list_of(:user),
as: :friends,
parent_mappings: [user_id: & &1.id]
end
object :user_queries do
derive_query GetUser, :user,
as: :user,
except: [:name]
derive_query ListUsers, list_of(:user),
as: :users,
arg_types: [status: :user_status]
end
derive_mutation_input CreateUser
object :user_mutations do
derive_mutation CreateUser, :user, input_object?: true, then: &fetch_user/1
derive_mutation SuspendUser, :user, then: &fetch_user/1
derive_mutation ReinstateUser, :user, then: &fetch_user/1
end
end
"""
alias Cqrs.Guards
alias Cqrs.Absinthe.{Enum, CommandField, Object, Query}
defmacro __using__(_) do
quote do
import Cqrs.Absinthe,
only: [
derive_mutation_input: 1,
derive_mutation_input: 2,
derive_mutation: 2,
derive_mutation: 3,
computed_field: 2,
computed_field: 3,
derive_query: 2,
derive_query: 3,
derive_enum: 2,
derive_object: 2,
derive_object: 3,
derive_input_object: 2,
derive_input_object: 3
]
end
end
@doc """
Defines an `Absinthe` `query` from a [Query](`Cqrs.Query`).
## Options
* `:as` - The name to use for the query. Defaults to the query_module name snake_cased.
* `:only` - Use only the filters listed
* `:except` - Create filters for all except those listed
* `:before_resolve` - [Absinthe Middleware](`Absinthe.Middleware`) to run before the resolver.
* `:after_resolve` - [Absinthe Middleware](`Absinthe.Middleware`) to run after the resolver.
* `:arg_types` - A list of filter names to absinthe types. See example.
* `:parent_mappings` - A keyword list of query filters to functions that receive the field's parent object as an argument.
* `:filter_transforms` - A keyword list of query filters to functions that receive the filter's current value as an argument.
"""
defmacro derive_query(query_module, return_type, opts \\ []) do
opts = Macro.escape(opts)
return_type = Macro.escape(return_type)
field =
quote location: :keep do
Guards.ensure_is_query!(unquote(query_module))
opts = Keyword.merge(unquote(opts), source: unquote(query_module), macro: :derive_query)
Query.create_query(
unquote(query_module),
unquote(return_type),
Keyword.put_new(opts, :tag?, true)
)
end
Module.eval_quoted(__CALLER__, field)
end
@doc """
Defines an `Absinthe` `input_object` for a [Command](`Cqrs.Command`).
## Options
* `:as` - The name to use for the query. Defaults to the command_module name snake_cased with `_input` appended.
* `:arg_types` - A list of filter names to absinthe types. See example.
"""
defmacro derive_mutation_input(command_module, opts \\ []) do
opts = Macro.escape(opts)
input =
quote location: :keep do
opts =
unquote(opts)
|> Keyword.merge(source: unquote(command_module), macro: :derive_mutation_input)
Guards.ensure_is_command!(unquote(command_module))
CommandField.create_input_object(unquote(command_module), opts)
end
Module.eval_quoted(__CALLER__, input)
end
@doc """
Defines an `Absinthe` `mutation` for a [Command](`Cqrs.Command`).
## Options
* `:as` - The name to use for the mutation. Defaults to the query_module name snake_cased.
* `:then` - A `function/1` that accepts the result of the command execution. The function should return the standard `Absinthe` `{:ok, response}` or `{:error, error}` tuple.
* `:input_object?` - `true | false`. Defaults to `false`
* If `true`, one arg with the name of `:input` will be generated.
* If `true`, an `input_object` for the [Command](`Cqrs.Command`) is expected to exist. See `derive_mutation_input/2`.
* `:arg_types` - A list of filter names to absinthe types. See example.
* `:before_resolve` - [Absinthe Middleware](`Absinthe.Middleware`) to run before the resolver.
* `:after_resolve` - [Absinthe Middleware](`Absinthe.Middleware`) to run after the resolver.
* `:parent_mappings` - A keyword list of command fields to functions that receive the field's parent object as an argument.
* `:field_transforms` - A keyword list of command fields to functions that receive the field's current value as an argument.
"""
defmacro derive_mutation(command_module, return_type, opts \\ []) do
opts = Macro.escape(opts)
return_type = Macro.escape(return_type)
mutation =
quote location: :keep do
Guards.ensure_is_command!(unquote(command_module))
opts =
unquote(opts)
|> Keyword.put(:tag?, true)
|> Keyword.merge(source: unquote(command_module), macro: :derive_mutation)
if Keyword.get(opts, :relay?) do
CommandField.create_relay_mutation(
unquote(command_module),
unquote(return_type),
opts
)
else
CommandField.create_command_field(
unquote(command_module),
unquote(return_type),
opts
)
end
end
Module.eval_quoted(__CALLER__, mutation)
end
@doc """
Defines a field backed by a [Command](`Cqrs.Command`) resolver.
## Options
* `:as` - The name to use for the mutation. Defaults to the query_module name snake_cased.
* `:before_resolve` - [Absinthe Middleware](`Absinthe.Middleware`) to run before the resolver.
* `:after_resolve` - [Absinthe Middleware](`Absinthe.Middleware`) to run after the resolver.
* `:arg_types` - A list of filter names to absinthe types. See example.
* `:parent_mappings` - A keyword list of command fields to functions that receive the field's parent object as an argument.
* `:field_transforms` - A keyword list of command fields to functions that receive the field's current value as an argument.
"""
defmacro computed_field(command_module, return_type, opts \\ []) do
opts = Macro.escape(opts)
return_type = Macro.escape(return_type)
mutation =
quote location: :keep do
Guards.ensure_is_command!(unquote(command_module))
opts =
unquote(opts)
|> Keyword.merge(source: unquote(command_module), macro: :computed_field)
|> Keyword.put(:tag?, true)
|> Keyword.put(:input_object?, false)
CommandField.create_command_field(
unquote(command_module),
unquote(return_type),
opts
)
end
Module.eval_quoted(__CALLER__, mutation)
end
@doc """
Defines an [Absinthe Enum](`Absinthe.Type.Enum`) from a [Command](`Cqrs.Command`), [Domain Event](`Cqrs.DomainEvent`), or [Ecto Schema](`Ecto.Schema`).
"""
defmacro derive_enum(enum_name, {enum_source_module, field_name}) do
enum =
quote location: :keep do
Cqrs.Absinthe.ensure_is_schema!(unquote(enum_source_module))
Enum.create_enum(
unquote(enum_name),
unquote(enum_source_module),
unquote(field_name)
)
end
Module.eval_quoted(__CALLER__, enum)
end
defmacro derive_object(object_name, object_source_module, opts \\ []) do
object =
quote location: :keep do
Cqrs.Absinthe.ensure_is_schema!(unquote(object_source_module))
Object.create_object(
unquote(object_name),
unquote(object_source_module),
unquote(opts)
)
end
Module.eval_quoted(__CALLER__, object)
end
defmacro derive_input_object(object_name, object_source_module, opts \\ []) do
object =
quote location: :keep do
Cqrs.Absinthe.ensure_is_schema!(unquote(object_source_module))
Object.create_object(
unquote(object_name),
unquote(object_source_module),
Keyword.put(unquote(opts), :input?, true)
)
end
Module.eval_quoted(__CALLER__, object)
end
@doc false
def ensure_is_schema!(module) do
unless Guards.exports_function?(module, :__schema__, 2) do
raise Cqrs.Absinthe.InvalidSourceError, module: module
end
end
end
end