Skip to content

Commit

Permalink
Support Constraints for Feature Flags
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Fontaine authored and afontaine committed Oct 18, 2020
1 parent 5feba88 commit c2ead81
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 10 deletions.
10 changes: 8 additions & 2 deletions lib/unleash/strategy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule Unleash.Strategy do
require Logger

alias Unleash.Config
alias Unleash.Strategy.Constraint

defmacro __using__(opts) do
name = opts[:name]
Expand Down Expand Up @@ -49,7 +50,7 @@ defmodule Unleash.Strategy do
* `parameters` - A map of paramters returned from the Unleash server. This
can be whatever you like, such as a configured list of `userIds`.
* `context` - The context passed into `Unleash.enabled?/3`.
* `context` - The context passed into `Unleash.enabled?/3`.
## Examples
Expand All @@ -72,7 +73,7 @@ defmodule Unleash.Strategy do
Config.strategies()
|> Enum.find(fn {n, _mod} -> n == name end)

module.check_enabled(strategy["parameters"], context)
check_constraints(strategy, context) and module.check_enabled(strategy["parameters"], context)
end

def enabled?(_strat, _context), do: false
Expand All @@ -97,4 +98,9 @@ defmodule Unleash.Strategy do

result
end

defp check_constraints(%{"constraints" => constraints}, context),
do: Constraint.verify_all(constraints, context)

defp check_constraints(_strategy, _context), do: true
end
35 changes: 35 additions & 0 deletions lib/unleash/strategy/constraint.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
defmodule Unleash.Strategy.Constraint do
@moduledoc """
Module that is used to verify
[constraints](https://www.unleash-hosted.com/docs/strategy-constraints/) are
met.
These constraints allow for very complex and specifc strategies to be
enacted by allowing users to specify context values to include or exclude.
"""

def verify_all(constraints, context) do
Enum.all?(constraints, &verify(&1, context))
end

defp verify(%{"contextName" => name, "operator" => op, "values" => values}, context) do
context
|> find_value(name)
|> check(op, values)
end

defp verify(%{}, _context), do: false

defp check(value, "IN", values), do: value in values
defp check(value, "NOT_IN", values), do: value not in values

defp find_value(nil, _name), do: nil

defp find_value(ctx, name) do
Map.get(
ctx,
String.to_atom(Recase.to_snake(name)),
find_value(Map.get(ctx, :properties), name)
)
end
end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ defmodule Unleash.MixProject do
{:stream_data, "~> 0.4.3", only: [:test, :dev]},
{:excoveralls, "~> 0.8", only: :test},
{:mox, "~> 0.5.1", only: :test},
{:recase, "~> 0.6.0", only: :test},
{:recase, "~> 0.6.0"},
{:murmur, "~> 1.0"},
{:mojito, "~> 0.5.0"},
{:jason, "~> 1.1"},
Expand Down
12 changes: 5 additions & 7 deletions test/unleash/client_specification_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,6 @@ defmodule Unleash.ClientSpecificationTest do
@expected expected

@tag capture_log: true
if String.starts_with?(name, "09-strategy-constraints") do
@tag skip: true
else
@tag skip: false
end

test t do
context = entity_from_file(@context)

Expand Down Expand Up @@ -76,7 +70,11 @@ defmodule Unleash.ClientSpecificationTest do

defp entity_from_file(e) do
e
|> Enum.map(fn {k, v} -> {String.to_atom(Recase.to_snake(k)), v} end)
|> Enum.map(fn
{"payload", v} -> {:payload, v}
{k, v} when is_map(v) -> {String.to_atom(Recase.to_snake(k)), entity_from_file(v)}
{k, v} -> {String.to_atom(Recase.to_snake(k)), v}
end)
|> Enum.into(%{})
end
end

0 comments on commit c2ead81

Please sign in to comment.