Riptide.Interceptor behaviour (riptide v0.5.2) View Source
Riptide Interceptors let you define simple rules using Elixir's pattern matching that trigger conditionally when data is written or read. Each one is defined as a module that can be added to your Riptide configuration for easy enabling/disabling.
config :riptide,
interceptors: [
TodoList.Permissions,
TodoList.Todo.Created,
TodoList.Todo.Alert
]Every Interceptor in this list is called in order for every Mutation and Query processed
Mutation Interceptors
Mutation interceptors run as a mutation is being processed. The callbacks are called for each part of the paths in the mutation so you can define a pattern to match any kind of mutation. The arguments passed to them are
path: A string list representing the path where the data is being writtenlayer: Themergeanddeletethat is occuring at the pathmut: The full, original mutationstate: The state of the connection which can be used to store things like the currently authed user
mutation_before
This runs before a mutation is written. It's best used to perform validations to make sure the data looks right, augmenting mutations with information that is known by the server only, or data denormalization. Here is an example that keeps track of the time when a Todo was marked complete
defmodule Todo.Created do
use Riptide.Interceptor
def mutation_before(["todos", _key], %{ merge: %{ "complete" => true }}, state) do
{
:merge,
%{
"times" => %{
"completed" => :os.system_time(:millisecond)
}
}
}
end
endThe valid responses are
:ok- Returns successfully but doesn't modify anything{:error, err}- Halts processing of interceptors and returns the error{:combine, mut}- Combinesmutwith the input mutation before writing{:merge, map}- Convenience version of:combinethat mergesmapat the current path{:delete, map}- Convenience version of:combinethat deletesmapat the current path
mutation_effect
This interceptor can be used to schedule work to be done after a mutation is successfully written. It can be used to trigger side effects like sending an email or syncing data with a third party system.
defmodule Todo.Created do
use Riptide.Interceptor
def mutation_before(["todos", _key], %{ merge: %{ "complete" => true }}, state) do
{
:merge,
%{
"times" => %{
"completed" => :os.system_time(:millisecond)
}
}
}
end
endThe valid responses are
:ok- Returns successfully but doesn't schedule any work{fun, args}- Callsfunin the current module withargs{module, fun, args}- Callsfuninmodulewithargs
Query Interceptors
Query interceptors run as a query is being processed. They can be used to allow/disallow access to certain paths or even expose third party data sources. Unlike the mutation interceptors they are called only once for each path requested by a query. The arguments passed to them are
path: A string list representing the full path where the data is being writtenopts: The options for the query at this pathstate: The state of the connection which can be used to store things like the currently authed user
query_before
This runs before data is read. A common way to use it is to control access to data
defmodule Todo.Permissions do
use Riptide.Interceptor
def query_before(["secrets" | _rest], _opts, state) do
case state do
state.user === "bad-guy" -> {:error, :auth_error}
true -> :ok
end
end
endThe valid responses are
:ok- Returns successfully{:error, err}- Halts processing of interceptors and returns the error
query_resolve
This is run before data is fetched from the store. This interceptor allows you to return data for the query and skip reading from the store. They effectively create virtual paths.
defmodule Todo.Twilio do
use Riptide.Interceptor
def query_resolve(["twilio", "numbers" | _rest], _opts, state) do
TwilioApi.numbers()
|> case do
{:ok, result} -> Kernel.get_in(result, rest)
{:error, err} -> {:error, err}
end
end
endThe valid responses are
nil- Skips this interceptor and continues processingany_value- Returnsany_valueas the data under the requested path
Link to this section Summary
Functions
Trigger mutation_before callback on configured interceptors for given mutation
Trigger mutation_before callback on interceptors for given mutation
Trigger mutation_effect callback on configured interceptors for given mutation
Trigger mutation_effect callback on interceptors for given mutation
Trigger query_before callback on configured interceptors for given query
Trigger query_before callback on interceptors for given query
Trigger query_resolve callback on configured interceptors for given query
Trigger query_resolve callback on interceptors for given query
Link to this section Functions
Specs
mutation_before(Riptide.Mutation.t(), any()) :: {:ok, Riptide.Mutation.t()} | {:error, any()}
Trigger mutation_before callback on configured interceptors for given mutation
Specs
mutation_before(Riptide.Mutation.t(), any(), [atom()]) :: {:ok, Riptide.Mutation.t()} | {:error, any()}
Trigger mutation_before callback on interceptors for given mutation
Trigger mutation_effect callback on configured interceptors for given mutation
Trigger mutation_effect callback on interceptors for given mutation
Trigger query_before callback on configured interceptors for given query
Trigger query_before callback on interceptors for given query
Trigger query_resolve callback on configured interceptors for given query
Trigger query_resolve callback on interceptors for given query
Link to this section Callbacks
Specs
mutation_after( path :: [String.t()], layer :: Riptide.Mutation.t(), mut :: Riptide.Mutation.t(), state :: String.t() ) :: :ok | nil
Specs
mutation_before( path :: [String.t()], layer :: Riptide.Mutation.t(), mut :: Riptide.Mutation.t(), state :: String.t() ) :: :ok | {:error, term()} | {:combine, Riptide.Mutation.t()} | nil
Specs
mutation_effect( path :: [String.t()], layer :: Riptide.Mutation.t(), mut :: Riptide.Mutation.t(), state :: String.t() ) :: :ok | {atom(), atom(), list()} | {atom(), list()} | nil