Skip to content

Commit

Permalink
add context transformer tests
Browse files Browse the repository at this point in the history
  • Loading branch information
JoE11-y committed Dec 5, 2024
1 parent a0e3673 commit f5dc825
Show file tree
Hide file tree
Showing 11 changed files with 379 additions and 59 deletions.
217 changes: 217 additions & 0 deletions openfeature/providers/elixir-provider/.credo.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# This file contains the configuration for Credo and you are probably reading
# this after creating it with `mix credo.gen.config`.
#
# If you find anything wrong or unclear in this file, please report an
# issue on GitHub: https://github.com/rrrene/credo/issues
#
%{
#
# You can have as many configs as you like in the `configs:` field.
configs: [
%{
#
# Run any config using `mix credo -C <name>`. If no config name is given
# "default" is used.
#
name: "default",
#
# These are the files included in the analysis:
files: %{
#
# You can give explicit globs or simply directories.
# In the latter case `**/*.{ex,exs}` will be used.
#
included: [
"lib/",
"src/",
"test/",
"web/",
"apps/*/lib/",
"apps/*/src/",
"apps/*/test/",
"apps/*/web/"
],
excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"]
},
#
# Load and configure plugins here:
#
plugins: [],
#
# If you create your own checks, you must specify the source files for
# them here, so they can be loaded by Credo before running the analysis.
#
requires: [],
#
# If you want to enforce a style guide and need a more traditional linting
# experience, you can change `strict` to `true` below:
#
strict: false,
#
# To modify the timeout for parsing files, change this value:
#
parse_timeout: 5000,
#
# If you want to use uncolored output by default, you can change `color`
# to `false` below:
#
color: true,
#
# You can customize the parameters of any check by adding a second element
# to the tuple.
#
# To disable a check put `false` as second element:
#
# {Credo.Check.Design.DuplicatedCode, false}
#
checks: %{
enabled: [
#
## Consistency Checks
#
{Credo.Check.Consistency.ExceptionNames, []},
{Credo.Check.Consistency.LineEndings, []},
{Credo.Check.Consistency.ParameterPatternMatching, []},
{Credo.Check.Consistency.SpaceAroundOperators, []},
{Credo.Check.Consistency.SpaceInParentheses, []},
{Credo.Check.Consistency.TabsOrSpaces, []},

#
## Design Checks
#
# You can customize the priority of any check
# Priority values are: `low, normal, high, higher`
#
{Credo.Check.Design.AliasUsage,
[priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]},
{Credo.Check.Design.TagFIXME, []},
# You can also customize the exit_status of each check.
# If you don't want TODO comments to cause `mix credo` to fail, just
# set this value to 0 (zero).
#
{Credo.Check.Design.TagTODO, [exit_status: 2]},

#
## Readability Checks
#
{Credo.Check.Readability.AliasOrder, []},
{Credo.Check.Readability.FunctionNames, []},
{Credo.Check.Readability.LargeNumbers, []},
{Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]},
{Credo.Check.Readability.ModuleAttributeNames, []},
{Credo.Check.Readability.ModuleDoc, []},
{Credo.Check.Readability.ModuleNames, []},
{Credo.Check.Readability.ParenthesesInCondition, []},
{Credo.Check.Readability.ParenthesesOnZeroArityDefs, []},
{Credo.Check.Readability.PipeIntoAnonymousFunctions, []},
{Credo.Check.Readability.PredicateFunctionNames, []},
{Credo.Check.Readability.PreferImplicitTry, []},
{Credo.Check.Readability.RedundantBlankLines, []},
{Credo.Check.Readability.Semicolons, []},
{Credo.Check.Readability.SpaceAfterCommas, []},
{Credo.Check.Readability.StringSigils, []},
{Credo.Check.Readability.TrailingBlankLine, []},
{Credo.Check.Readability.TrailingWhiteSpace, []},
{Credo.Check.Readability.UnnecessaryAliasExpansion, []},
{Credo.Check.Readability.VariableNames, []},
{Credo.Check.Readability.WithSingleClause, []},

#
## Refactoring Opportunities
#
{Credo.Check.Refactor.Apply, []},
{Credo.Check.Refactor.CondStatements, []},
{Credo.Check.Refactor.CyclomaticComplexity, []},
{Credo.Check.Refactor.FilterCount, []},
{Credo.Check.Refactor.FilterFilter, []},
{Credo.Check.Refactor.FunctionArity, []},
{Credo.Check.Refactor.LongQuoteBlocks, []},
{Credo.Check.Refactor.MapJoin, []},
{Credo.Check.Refactor.MatchInCondition, []},
{Credo.Check.Refactor.NegatedConditionsInUnless, []},
{Credo.Check.Refactor.NegatedConditionsWithElse, []},
{Credo.Check.Refactor.Nesting, []},
{Credo.Check.Refactor.RedundantWithClauseResult, []},
{Credo.Check.Refactor.RejectReject, []},
{Credo.Check.Refactor.UnlessWithElse, []},
{Credo.Check.Refactor.WithClauses, []},

#
## Warnings
#
{Credo.Check.Warning.ApplicationConfigInModuleAttribute, []},
{Credo.Check.Warning.BoolOperationOnSameValues, []},
{Credo.Check.Warning.Dbg, []},
{Credo.Check.Warning.ExpensiveEmptyEnumCheck, []},
{Credo.Check.Warning.IExPry, []},
{Credo.Check.Warning.IoInspect, []},
{Credo.Check.Warning.MissedMetadataKeyInLoggerConfig, []},
{Credo.Check.Warning.OperationOnSameValues, []},
{Credo.Check.Warning.OperationWithConstantResult, []},
{Credo.Check.Warning.RaiseInsideRescue, []},
{Credo.Check.Warning.SpecWithStruct, []},
{Credo.Check.Warning.UnsafeExec, []},
{Credo.Check.Warning.UnusedEnumOperation, []},
{Credo.Check.Warning.UnusedFileOperation, []},
{Credo.Check.Warning.UnusedKeywordOperation, []},
{Credo.Check.Warning.UnusedListOperation, []},
{Credo.Check.Warning.UnusedPathOperation, []},
{Credo.Check.Warning.UnusedRegexOperation, []},
{Credo.Check.Warning.UnusedStringOperation, []},
{Credo.Check.Warning.UnusedTupleOperation, []},
{Credo.Check.Warning.WrongTestFileExtension, []}
],
disabled: [
#
# Checks scheduled for next check update (opt-in for now)
{Credo.Check.Refactor.UtcNowTruncate, []},

#
# Controversial and experimental checks (opt-in, just move the check to `:enabled`
# and be sure to use `mix credo --strict` to see low priority checks)
#
{Credo.Check.Consistency.MultiAliasImportRequireUse, []},
{Credo.Check.Consistency.UnusedVariableNames, []},
{Credo.Check.Design.DuplicatedCode, []},
{Credo.Check.Design.SkipTestWithoutComment, []},
{Credo.Check.Readability.AliasAs, []},
{Credo.Check.Readability.BlockPipe, []},
{Credo.Check.Readability.ImplTrue, []},
{Credo.Check.Readability.MultiAlias, []},
{Credo.Check.Readability.NestedFunctionCalls, []},
{Credo.Check.Readability.OneArityFunctionInPipe, []},
{Credo.Check.Readability.OnePipePerLine, []},
{Credo.Check.Readability.SeparateAliasRequire, []},
{Credo.Check.Readability.SingleFunctionToBlockPipe, []},
{Credo.Check.Readability.SinglePipe, []},
{Credo.Check.Readability.Specs, []},
{Credo.Check.Readability.StrictModuleLayout, []},
{Credo.Check.Readability.WithCustomTaggedTuple, []},
{Credo.Check.Refactor.ABCSize, []},
{Credo.Check.Refactor.AppendSingleItem, []},
{Credo.Check.Refactor.DoubleBooleanNegation, []},
{Credo.Check.Refactor.FilterReject, []},
{Credo.Check.Refactor.IoPuts, []},
{Credo.Check.Refactor.MapMap, []},
{Credo.Check.Refactor.ModuleDependencies, []},
{Credo.Check.Refactor.NegatedIsNil, []},
{Credo.Check.Refactor.PassAsyncInTestCases, []},
{Credo.Check.Refactor.PipeChainStart, []},
{Credo.Check.Refactor.RejectFilter, []},
{Credo.Check.Refactor.VariableRebinding, []},
{Credo.Check.Warning.LazyLogging, []},
{Credo.Check.Warning.LeakyEnvironment, []},
{Credo.Check.Warning.MapGetUnsafePass, []},
{Credo.Check.Warning.MixEnv, []},
{Credo.Check.Warning.UnsafeToAtom, []}

# {Credo.Check.Refactor.MapInto, []},

#
# Custom checks can be created using `mix credo.gen.check`.
#
]
}
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ defmodule ElixirProvider.CacheController do
@flag_table :flag_cache

@spec start_link() :: GenServer.on_start()
def start_link() do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
def start_link do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end

def get(flag_key, evaluation_hash) do
cache_key = build_cache_key(flag_key, evaluation_hash)

case :ets.lookup(@flag_table, cache_key) do
[{^cache_key, cached_value}] -> {:ok, cached_value}
[] -> :miss
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,31 @@ defmodule ElixirProvider.ContextTransformer do
alias OpenFeature.Types

@doc """
Finds any key-value pair with a non-nil value.
Extracts other key value pairs after the targeting key
"""
def get_any_value(map) when is_map(map) do
case Enum.find(map, fn {_key, value} -> value != nil end) do
{key, value} -> {:ok, {key, value}}
nil -> {:error, "No keys found with a value"}
end
map
|> Enum.reject(fn {key, _value} -> key === :targetingKey end)
|> Enum.into(%{})
end

@doc """
Converts an EvaluationContext map into a ElixirProvider.GofEvaluationContext struct.
Returns `{:ok, context}` on success, or `{:error, reason}` on failure.
"""
@spec transform_context(Types.context()) :: {:ok, GofEvaluationContext.t()} | {:error, String.t()}
@spec transform_context(Types.context()) ::
{:ok, GofEvaluationContext.t()} | {:error, String.t()}
def transform_context(ctx) do
case get_any_value(ctx) do
{:ok, {key, value}} ->
{:ok, %GofEvaluationContext{
key: key,
custom: value
}}
{:error, reason} ->
{:error, reason}
case Map.fetch(ctx, :targetingKey) do
{:ok, value} ->
{:ok,
%GofEvaluationContext{
key: value,
custom: get_any_value(ctx)
}}

:error ->
{:error, "targeting key not found"}
end
end
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
defmodule ElixirProvider.DataCollectorHook do
@moduledoc """
Data collector hook
"""

use GenServer
require Logger
Expand All @@ -25,13 +28,14 @@ defmodule ElixirProvider.DataCollectorHook do
}

# Starts the GenServer and initializes with options
def start_link() do
def start_link do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end

def stop(state) do
GenServer.stop(__MODULE__)
collect_data(state.data_flush_interval)

%__MODULE__{
http_client: state.http_client,
data_collector_endpoint: state.data_collector_endpoint,
Expand Down Expand Up @@ -71,13 +75,15 @@ defmodule ElixirProvider.DataCollectorHook do
:ok
else
feature_event = %FeatureEvent{
context_kind: if(Map.get(hook_context.context, "anonymous"), do: "anonymousUser", else: "user"),
context_kind:
if(Map.get(hook_context.context, "anonymous"), do: "anonymousUser", else: "user"),
creation_date: DateTime.utc_now() |> DateTime.to_unix(:millisecond),
default: false,
key: hook_context.flag_key,
value: flag_evaluation_details.value,
variation: flag_evaluation_details.variant || "SdkDefault",
user_key: Map.get(hook_context.evaluation_context, "targeting_key") || @default_targeting_key
user_key:
Map.get(hook_context.evaluation_context, "targeting_key") || @default_targeting_key
}

GenServer.cast(__MODULE__, {:add_event, feature_event})
Expand All @@ -89,7 +95,8 @@ defmodule ElixirProvider.DataCollectorHook do
:ok
else
feature_event = %FeatureEvent{
context_kind: if(Map.get(hook_context.context, "anonymous"), do: "anonymousUser", else: "user"),
context_kind:
if(Map.get(hook_context.context, "anonymous"), do: "anonymousUser", else: "user"),
creation_date: DateTime.utc_now() |> DateTime.to_unix(:millisecond),
default: true,
key: hook_context.flag_key,
Expand Down Expand Up @@ -120,7 +127,11 @@ defmodule ElixirProvider.DataCollectorHook do
{:noreply, %{state | event_queue: []}}
end

defp collect_data(%__MODULE__{event_queue: event_queue, http_client: http_client, data_collector_endpoint: endpoint}) do
defp collect_data(%__MODULE__{
event_queue: event_queue,
http_client: http_client,
data_collector_endpoint: endpoint
}) do
if Enum.empty?(event_queue) do
:ok
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ defmodule ElixirProvider.HttpClient do
}

@spec start_link() :: GenServer.on_start()
def start_link() do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
def start_link do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end

def stop() do
def stop do
GenServer.stop(__MODULE__)
end

Expand Down
Loading

0 comments on commit f5dc825

Please sign in to comment.