Created
January 2, 2018 19:30
-
-
Save ssomnoremac/15312e8862b88ceab33c850104a9c98f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Hyr.Command do | |
@moduledoc """ | |
Define a command struct and an associated GraphQL mutation. | |
## Example | |
defmodule Hyr.Shifts.Opening.Commands.CancelOpeningEmployer do | |
use Hyr.Command | |
alias Hyr.Shifts.Opening.Schema.Opening | |
result Opening | |
permission "MANAGE_OPENINGS" | |
command "Cancel an opening" do | |
field :opening_id, non_null(:id) | |
field :employer_id, non_null(:id) | |
end | |
end | |
""" | |
alias Hyr.Shifts.Router | |
alias HyrWeb.Schema.Middleware | |
defmacro __using__(_) do | |
quote do | |
use Absinthe.Schema.Notation | |
use Absinthe.Relay.Schema.Notation, :modern | |
require Logger | |
Module.register_attribute(__MODULE__, :command_permission, []) | |
Module.register_attribute(__MODULE__, :command_result, []) | |
Module.register_attribute(__MODULE__, :command_result_aggregate_id_field, []) | |
import unquote(__MODULE__) | |
@before_compile unquote(__MODULE__) | |
end | |
end | |
@doc """ | |
Define the permission required to execute the command. | |
""" | |
defmacro permission(permission) | |
when is_bitstring(permission) | |
do | |
quote do | |
Module.put_attribute(__MODULE__, :command_permission, unquote(permission)) | |
end | |
end | |
@doc """ | |
Provide the result type returned after successful command execution. | |
""" | |
defmacro result(result, id_field \\ :id) do | |
quote do | |
Module.put_attribute(__MODULE__, :command_result, unquote(result)) | |
end | |
end | |
@doc """ | |
Define the fields for the command and optionally provide a description. | |
## Example | |
defmodule Hyr.Shifts.Opening.Commands.CancelOpeningEmployer do | |
use Hyr.Command | |
command do | |
field :opening_id, non_null(:id) | |
field :employer_id, non_null(:id) | |
end | |
end | |
""" | |
defmacro command(description \\ "", [do: block] = fields) do | |
identifier = identify(__CALLER__.module) | |
input_name = String.to_atom("#{identifier}_input") | |
command_name = String.to_atom("#{identifier}_command") | |
quote do | |
input_object unquote(input_name), unquote(fields) | |
object unquote(command_name) do | |
payload field unquote(identifier) do | |
description unquote(description) | |
arg :input, non_null(unquote(input_name)) | |
output do | |
field :result, identify(@command_result) | |
end | |
middleware Middleware.Authorize, @command_permission | |
resolve fn %{input: input_data}, _ -> | |
Logger.debug(fn -> "Attempting to dispatch command #{inspect __MODULE__} with input: #{inspect input_data}" end) | |
dispatch_command(input_data) | |
end | |
end | |
end | |
end | |
end | |
defmacro __before_compile__(env) do | |
identifier = identify(env.module) | |
input_name = String.to_atom("#{identifier}_input") | |
command_name = String.to_atom("#{identifier}_command") | |
fields = | |
env.module | |
|> Module.get_attribute(:absinthe_definitions) | |
|> Enum.map(fn %Absinthe.Schema.Notation.Definition{attrs: attrs} -> attrs end) | |
|> Enum.flat_map(fn attrs -> Keyword.get(attrs, :fields) end) | |
|> Enum.map(fn {identifier, _value} -> identifier end) | |
fields = fields -- [identifier, input_name, command_name, :result] | |
# first defined field is expected to be the identity field (last field in | |
# module attribute list) | |
identity_field = Enum.at(fields, -1) | |
quote do | |
defp dispatch_command(input_data) do | |
case input_data |> __MODULE__.new() |> Router.dispatch(consistency: :strong) do | |
:ok -> | |
case @command_result do | |
Hyr.Shifts.Opening.Schema.Opening -> {:ok, %{result: Hyr.Repo.get_by(@command_result, %{id: input_data.opening_id})}} | |
Hyr.Shifts.Opening.Schema.Applicant -> {:ok, %{result: Hyr.Repo.get_by(@command_result, %{opening_id: input_data.opening_id, talent_id: input_data.talent_id})}} | |
_ -> {:ok, %{result: Hyr.Repo.get(@command_result, input_data.id)}} | |
end | |
{:error, reason} -> | |
Logger.info(fn -> "Failed to dispatch command due to: #{inspect reason}" end) | |
message = reason |> to_string() |> String.replace("_", " ") | |
{:error, message} | |
end | |
end | |
@derive [Poison.Encoder] | |
defstruct unquote(fields) | |
use ExConstructor | |
end | |
end | |
def identify(module) do | |
module | |
|> Module.split() | |
|> Enum.at(-1) | |
|> Macro.underscore() | |
|> String.to_atom() | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Copying what you had in slack for reference: