Created
October 10, 2016 13:55
-
-
Save ahmadshah/c1209d2e09d6796178717b2a6ec4e2dd to your computer and use it in GitHub Desktop.
Elixir: Dependency Injection - http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Reactor.Service.Authenticator do | |
@behaviour Reactor.Contract.Service.Authenticator | |
@moduledoc """ | |
User authentication service. | |
""" | |
use Reactor.Resolver, repositories: [:user] | |
alias Comeonin.Bcrypt, as: Comeonin | |
alias Reactor.Entity.User, as: UserEntity | |
@doc """ | |
Authenticate user via email and password. | |
""" | |
def authenticate(email, password) do | |
with %UserEntity{}=user <- @user_repo.find_by_auth_key(email), | |
:ok <- validate_hash(password, user.password), | |
:ok <- validate_state(user.state), | |
do: {:ok, user} | |
end | |
@doc """ | |
Autheticate user via primary key. | |
""" | |
def authenticate_with_id(id) do | |
with %UserEntity{}=user <- @user_repo.find_by_id(id), | |
:ok <- validate_state(user.state), | |
do: {:ok, user} | |
end | |
@doc false | |
defp validate_hash(input, hash_value) do | |
case Comeonin.checkpw(input, hash_value) do | |
false -> {:error, :invalid_password} | |
true -> :ok | |
end | |
end | |
@doc false | |
defp validate_state(state) when state == "suspended", | |
do: {:error, :user_is_suspended} | |
defp validate_state(_), | |
do: :ok | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Reactor.Service.AuthenticatorTest do | |
use ExUnit.Case, async: true | |
alias Reactor.Service.Authenticator, as: AuthenticatorService | |
test "authenticate user with email and password" do | |
{:ok, user} = AuthenticatorService.authenticate("[email protected]", "123456") | |
assert user.email == "[email protected]" | |
end | |
test "authenticate user with id" do | |
{:ok, user} = AuthenticatorService.authenticate_with_id("1") | |
assert user.email == "[email protected]" | |
end | |
test "authenticate non-existing user with email and password" do | |
assert {:error, :user_not_found} == AuthenticatorService.authenticate("trigger_error_response", "123456") | |
end | |
test "authenticate user with email and wrong password" do | |
assert {:error, :invalid_password} == AuthenticatorService.authenticate("[email protected]", "1234567") | |
end | |
test "authenticate suspended user with email and password" do | |
assert {:error, :user_is_suspended} = AuthenticatorService.authenticate("trigger_suspended_error_response", "123456") | |
end | |
test "authenticate non-existing user with id" do | |
assert {:error, :user_not_found} == AuthenticatorService.authenticate_with_id("trigger_error_response") | |
end | |
test "authenticate suspended user with id" do | |
assert {:error, :user_is_suspended} = AuthenticatorService.authenticate_with_id("trigger_suspended_error_response") | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use Mix.Config | |
config :reactor, :repositories, | |
user: Reactor.Repo.Postgre.User |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Reactor.Mock.Repo.User do | |
@moduledoc """ | |
User repository. | |
""" | |
@behaviour Reactor.Contract.Repo.User | |
alias Ecto.UUID | |
alias Comeonin.Bcrypt, as: Comeonin | |
alias Reactor.Entity.User, as: UserEntity | |
alias Henchman.Randomizer, as: Henchman | |
def find_by_id("trigger_error_response", opts \\ []), | |
do: {:error, :user_not_found} | |
def find_by_id("trigger_suspended_error_response", opts), | |
do: Map.put(user_struct, :state, "suspended") | |
def find_by_id(id, opts), | |
do: user_struct | |
def find_by_auth_key("trigger_error_response", opts \\ []), | |
do: {:error, :user_not_found} | |
def find_by_auth_key("trigger_suspended_error_response", opts), | |
do: Map.put(user_struct, :state, "suspended") | |
def find_by_auth_key(email, opts), | |
do: user_struct | |
def find_by_uuid(uuid, opts \\ []), | |
do: user_struct | |
def find_by_column(columns, opts), | |
do: user_struct | |
defp user_struct do | |
%UserEntity{ | |
id: "1", | |
uuid: UUID.generate, | |
email: "[email protected]", | |
password: Comeonin.hashpwsalt("123456"), | |
first_name: "Foo", | |
last_name: "Bar", | |
state: "active", | |
account_confirmation_key: Henchman.generate(20), | |
} | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Reactor.Repo.Postgre.User do | |
@moduledoc """ | |
User repository. | |
""" | |
@behaviour Reactor.Contract.Repo.User | |
use Reactor.Repository, :postgre_repo | |
alias Reactor.Repo | |
alias Reactor.Repo.Postgre.Model.User, as: UserModel | |
def find_by_id(id, opts \\ []) do | |
case Repo.get(UserModel, id) do | |
nil -> user_not_found_error | |
user -> to_struct(user, opts) | |
end | |
end | |
def find_by_auth_key(email, opts \\ []), | |
do: find_by_column([email: email], opts) | |
def find_by_uuid(uuid, opts \\ []), | |
do: find_by_column([uuid: uuid], opts) | |
def find_by_column(columns, opts) do | |
case Repo.get_by(UserModel, columns) do | |
nil -> user_not_found_error | |
user -> to_struct(user, opts) | |
end | |
end | |
defp user_not_found_error, | |
do: {:error, :user_not_found} | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Reactor.Resolver do | |
@moduledoc false | |
defmacro __using__(opts) do | |
quote do | |
repositories = Keyword.get(unquote(opts), :repositories) | |
if !is_nil(repositories) do | |
Enum.map(repositories, fn (repo) -> | |
repo_module = Application.get_env(:reactor, :repositories)[repo] | |
if is_nil(repo_module), do: raise RuntimeError, message: "There is no available module for #{repo}" | |
attribute_name = String.to_atom(to_string(repo) <> "_repo") | |
Module.put_attribute(__MODULE__, attribute_name, repo_module) | |
end) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use Mix.Config | |
config :reactor, :repositories, | |
user: Reactor.Mock.Repo.User |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment