Skip to content

Commit

Permalink
Add configuration resolving to lists, maps & tuples. Better messages …
Browse files Browse the repository at this point in the history
…on failure.
  • Loading branch information
gordalina committed Aug 3, 2020
1 parent 1deb81a commit bb2d2b7
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 17 deletions.
31 changes: 18 additions & 13 deletions lib/hush/resolver.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,28 @@ defmodule Hush.Resolver do
@spec resolve!(Keyword.t() | map) :: Keyword.t()
def resolve!(config) do
Enum.reduce(config, [], fn
{key, {:hush, provider, name}}, acc ->
acc ++ [{key, resolve_value!(key, provider, name, [])}]
{:hush, provider, name}, acc ->
acc ++ [resolve_value!(provider, name, [])]

{key, {:hush, provider, name, options}}, acc ->
acc ++ [{key, resolve_value!(key, provider, name, options)}]
{:hush, provider, name, options}, acc ->
acc ++ [resolve_value!(provider, name, options)]

{key, rest = [_ | _]}, acc ->
acc ++ [{key, rest |> resolve!()}]
rest, acc when is_list(rest) ->
acc ++ [rest |> resolve!()]

{key, rest = %{}}, acc ->
acc ++ [{key, rest |> resolve!() |> Map.new()}]
rest, acc when is_map(rest) ->
acc ++ [rest |> resolve!() |> Map.new()]

rest, acc when is_tuple(rest) ->
acc ++ [Tuple.to_list(rest) |> resolve!() |> List.to_tuple()]

other, acc ->
acc ++ [other]
end)
end

@spec resolve_value!(String.t(), module(), String.t(), Keyword.t()) :: any()
defp resolve_value!(key, provider, name, options) do
@spec resolve_value!(module(), String.t(), Keyword.t()) :: any()
defp resolve_value!(provider, name, options) do
type = Keyword.get(options, :cast, :string)

with {:ok, value} <- Provider.fetch(provider, name, options),
Expand All @@ -51,19 +54,21 @@ defmodule Hush.Resolver do
{:error, :required} ->
raise ArgumentError,
message:
"Could not resolve '#{key}'. I was trying to evaluate '#{name}' with #{provider}. If this is an optional key, you add `optional: true` to the options list."
"Could not resolve {:hush, #{provider}, #{inspect(name)}}. If this is an optional key, you add `optional: true` to the options list."

{:error, :cast} ->
raise ArgumentError,
message:
"Although I was able to resolve configuration '#{key}', I wasn't able to cast it to type '#{
"Although I was able to resolve {:hush, #{provider}, #{inspect(name)}}, I wasn't able to cast it to type '#{
type
}'."

{:error, error} ->
raise RuntimeError,
message:
"An error occured while trying to resolve value in provider: #{provider}.\n#{error}"
"An error occured in the provider while trying to resolve {:hush, #{provider}, #{
inspect(name)
}}: #{error}"
end
end
end
29 changes: 25 additions & 4 deletions test/hush/resolver_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@ defmodule Hush.ResolverTest do
assert Resolver.resolve(config) == {:ok, [{:app, [foo: %{key: "map"}]}]}
end

test "with keyword list" do
expect(MockProvider, :fetch, fn _ -> {:ok, "list"} end)
config = app_config(key: {:hush, MockProvider, "list"})

assert Resolver.resolve(config) == {:ok, [{:app, [foo: [key: "list"]]}]}
end

test "with list" do
expect(MockProvider, :fetch, fn _ -> {:ok, "list"} end)
config = app_config([{:hush, MockProvider, "list"}])

assert Resolver.resolve(config) == {:ok, [{:app, [foo: ["list"]]}]}
end

test "with tuple" do
expect(MockProvider, :fetch, fn _ -> {:ok, "tuple"} end)
config = app_config({{:hush, MockProvider, "tuple"}})

assert Resolver.resolve(config) == {:ok, [{:app, [foo: {"tuple"}]}]}
end

test "with missing adapter" do
config = [
{:app, [foo: {:hush, ThisModuleDoesNotExist, "bar"}]}
Expand All @@ -31,7 +52,7 @@ defmodule Hush.ResolverTest do
{:error,
%RuntimeError{
message:
"An error occured while trying to resolve value in provider: Elixir.ThisModuleDoesNotExist.\nProvider is not available (nofile)"
"An error occured in the provider while trying to resolve {:hush, Elixir.ThisModuleDoesNotExist, \"bar\"}: Provider is not available (nofile)"
}}
end

Expand All @@ -43,7 +64,7 @@ defmodule Hush.ResolverTest do
{:error,
%ArgumentError{
message:
"Could not resolve 'foo'. I was trying to evaluate 'HUSH_UNKNOWN' with Elixir.Hush.Provider.MockProvider. If this is an optional key, you add `optional: true` to the options list."
"Could not resolve {:hush, Elixir.Hush.Provider.MockProvider, \"HUSH_UNKNOWN\"}. If this is an optional key, you add `optional: true` to the options list."
}}
end

Expand All @@ -55,7 +76,7 @@ defmodule Hush.ResolverTest do
{:error,
%ArgumentError{
message:
"Although I was able to resolve configuration 'foo', I wasn't able to cast it to type 'integer'."
"Although I was able to resolve {:hush, Elixir.Hush.Provider.MockProvider, \"bar\"}, I wasn't able to cast it to type 'integer'."
}}
end

Expand All @@ -67,7 +88,7 @@ defmodule Hush.ResolverTest do
{:error,
%RuntimeError{
message:
"An error occured while trying to resolve value in provider: Elixir.Hush.Provider.MockProvider.\nProvider returned an unexpected value: wrong return.\nExpected {:ok, value}, {:error, :not_found} or {:error, \"error\"}"
"An error occured in the provider while trying to resolve {:hush, Elixir.Hush.Provider.MockProvider, \"bar\"}: Provider returned an unexpected value: wrong return.\nExpected {:ok, value}, {:error, :not_found} or {:error, \"error\"}"
}}
end
end
Expand Down

0 comments on commit bb2d2b7

Please sign in to comment.