Add conditional stage

Allows applying another pipeline stage based on a condition, which can
either be a whole filter or a single filter rule.
This commit is contained in:
Shadowfacts 2019-11-01 22:50:25 -04:00
parent 13c44d5e10
commit f84d849432
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
1 changed files with 87 additions and 0 deletions

View File

@ -0,0 +1,87 @@
defmodule Frenzy.Pipeline.ConditionalStage do
require Logger
alias Frenzy.Pipeline.{Stage, FilterEngine}
@behaviour Stage
@filter_modes ["accept", "reject"]
@rule_modes ["contains_string", "matches_regex"]
@impl Stage
def apply(%{"stage" => stage, "opts" => stage_opts, "condition" => condition}, item_params) do
if test_condition(condition, item_params) do
apply(String.to_existing_atom("Elixir." <> stage), :apply, [stage_opts, item_params])
else
{:ok, item_params}
end
end
@impl Stage
def apply(opts, item_params) do
Logger.warn("Received invalid conditional opts: #{inspect(opts)}")
{:ok, item_params}
end
defp test_condition(%{"mode" => mode} = filter, item_params) when mode in @filter_modes do
FilterEngine.test(filter, item_params)
end
defp test_condition(%{"mode" => mode} = rule, item_params) when mode in @rule_modes do
FilterEngine.test_rule(rule, item_params)
end
defp test_condition(condition, _item_params) do
Logger.warn("Received invalid condition: #{inspect(condition)}")
false
end
@impl Stage
def validate_opts(opts) do
cond do
not (Map.has_key?(opts, "stage") and is_binary(opts["stage"]) and
module_exists(opts["stage"])) ->
{:error, "stage must be a string containg a module that exists"}
not (Map.has_key?(opts, "opts") and is_map(opts["opts"])) ->
{:error, "opts must be a map"}
not (Map.has_key?(opts, "condition") and is_map(opts["condition"])) ->
{:error, "condition must be a map"}
true ->
with {:ok, stage_opts} <-
apply(String.to_existing_atom("Elixir." <> opts["stage"]), :validate_opts, [
opts["opts"]
]),
{:ok, condition} <- validate_condition(opts["condition"]) do
{
:ok,
opts
|> Map.put("opts", stage_opts)
|> Map.put("condition", condition)
}
else
{:error, _reason} = err ->
err
end
end
end
defp module_exists(module_name) do
try do
String.to_existing_atom("Elixir." <> module_name)
true
rescue
ArgumentError -> false
end
end
defp validate_condition(%{"mode" => mode} = filter) when mode in @filter_modes do
FilterEngine.validate_filter(filter)
end
defp validate_condition(%{"mode" => mode} = rule) when mode in @rule_modes do
FilterEngine.validate_rule(rule)
end
defp validate_condition(_condition), do: {:error, "condition must be either a filter or a rule"}
end