Skip to content

feat: add show admin endpoint #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/authenticator/test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ExUnit.configure(formatters: [JUnitFormatter])
ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter])
ExUnit.start()

Ecto.Adapters.SQL.Sandbox.mode(Authenticator.Repo, :manual)
Expand Down
2 changes: 1 addition & 1 deletion apps/authorizer/test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
ExUnit.configure(formatters: [JUnitFormatter])
ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter])
ExUnit.start()
2 changes: 1 addition & 1 deletion apps/resource_manager/test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ExUnit.configure(formatters: [JUnitFormatter])
ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter])
ExUnit.start()

Ecto.Adapters.SQL.Sandbox.mode(ResourceManager.Repo, :manual)
16 changes: 16 additions & 0 deletions apps/rest_api/lib/controllers/admin/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,20 @@ defmodule RestAPI.Controller.Admin.User do
error
end
end

def show(conn, %{"id" => username} = params) do
params
|> Map.put(:username, username)
|> ResourceManager.get_identity()
|> case do
{:ok, identity} when is_map(identity) ->
conn
|> put_status(:created)
|> put_view(User)
|> render("show.json", response: identity)

{:error, error_reason} ->
error_reason
end
end
end
9 changes: 9 additions & 0 deletions apps/rest_api/lib/ports/resource_manager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,25 @@ defmodule RestAPI.Ports.ResourceManager do
@type possible_create_identity_response ::
{:ok, struct()} | {:error, Ecto.Changeset.t() | :invalid_params}

@type possible_get_identity_responses ::
{:ok, struct()} | {:error, :not_found | :invalid_params}

@doc "Delegates to ResourceManager.create_identity/1"
@callback create_identity(input :: map()) :: possible_create_identity_response()

@doc "Delegates to ResourceManager.password_allowed?/1"
@callback password_allowed?(password :: String.t()) :: boolean()

@callback get_identity(input :: map()) :: possible_get_identity_responses()

@doc "Create a new identity with it's credentials"
@spec create_identity(input :: map()) :: possible_create_identity_response()
def create_identity(input), do: implementation().create_identity(input)

@doc "Returns an user or application identity seaching by the given input"
@spec get_identity(input :: map()) :: possible_get_identity_responses()
def get_identity(input), do: implementation().get_identity(input)

@doc "Checks if the given password is strong enough to be used"
@spec password_allowed?(password :: String.t()) :: boolean()
def password_allowed?(password), do: implementation().password_allowed?(password)
Expand Down
11 changes: 11 additions & 0 deletions apps/rest_api/lib/views/admin/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,15 @@ defmodule RestAPI.Views.Admin.User do
update_at: response.updated_at
}
end

def render("show.json", %{response: response}) do
%{
id: response.id,
username: response.username,
status: response.status,
is_admin: response.is_admin,
inserted_at: response.inserted_at,
updated_at: response.updated_at
}
end
end
92 changes: 92 additions & 0 deletions apps/rest_api/test/controllers/admin/user_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule RestAPI.Controllers.Admin.User do
alias RestAPI.Ports.{AuthenticatorMock, AuthorizerMock, ResourceManagerMock}

@create_endpoint "/admin/v1/users"
@show_endpoint "/admin/v1/users/"

describe "POST #{@create_endpoint}" do
setup do
Expand Down Expand Up @@ -163,6 +164,97 @@ defmodule RestAPI.Controllers.Admin.User do
end
end

describe "GET #{@show_endpoint}" do
setup do
access_token = "my-access-token"
claims = default_claims()

{:ok, access_token: access_token, claims: claims, user: insert!(:user)}
end

test "should render user identity", %{
conn: conn,
access_token: access_token,
claims: claims,
user: user
} do
username = user.username

expect(AuthenticatorMock, :validate_access_token, fn token ->
assert access_token == token
{:ok, claims}
end)

expect(AuthenticatorMock, :get_session, fn %{"jti" => jti} ->
assert claims["jti"] == jti
{:ok, success_session(claims)}
end)

expect(AuthorizerMock, :authorize_admin, fn %Plug.Conn{} -> :ok end)

expect(ResourceManagerMock, :get_identity, fn input ->
assert is_map(input)

{:ok,
%{
id: user.id,
inserted_at: NaiveDateTime.utc_now(),
is_admin: user.is_admin,
status: user.status,
updated_at: NaiveDateTime.utc_now(),
username: username
}}
end)

assert %{
"id" => _id,
"inserted_at" => _inserted_at,
"updated_at" => _updated_at,
"is_admin" => false,
"status" => "active",
"username" => ^username
} =
conn
|> put_req_header("authorization", "Bearer #{access_token}")
|> get(@show_endpoint <> "username")
|> json_response(201)
end

test "if id is not an id, should return error", %{
conn: conn,
access_token: access_token,
claims: claims
} do
expect(AuthenticatorMock, :validate_access_token, fn token ->
assert access_token == token
{:ok, claims}
end)

expect(AuthenticatorMock, :get_session, fn %{"jti" => jti} ->
assert claims["jti"] == jti
{:ok, success_session(claims)}
end)

expect(AuthorizerMock, :authorize_admin, fn %Plug.Conn{} -> :ok end)

expect(ResourceManagerMock, :get_identity, fn input ->
assert is_map(input)

{:error, {:error, :invalid_params}}
end)

assert %{
"detail" => "The given parameters are invalid",
"error" => "bad_request",
"status" => 400
} =
conn
|> put_req_header("authorization", "Bearer #{access_token}")
|> get(@show_endpoint <> "1")
|> json_response(400)
end
end

defp default_claims do
%{
"jti" => "03eds74a-c291-4b5f",
Expand Down
11 changes: 9 additions & 2 deletions apps/rest_api/test/support/conn_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ defmodule RestAPI.ConnCase do
"""

use ExUnit.CaseTemplate
alias Ecto.Adapters.SQL.Sandbox

using do
quote do
# Import conveniences for testing with connections
import Plug.Conn
import Phoenix.ConnTest
import RestAPI.ConnCase
import RestAPI.{ConnCase, Factory}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use the resource_manager factory here.

import Mox

alias RestAPI.Router.Helpers, as: Routes
Expand All @@ -32,7 +33,13 @@ defmodule RestAPI.ConnCase do
end
end

setup _tags do
setup tags do
:ok = Sandbox.checkout(ResourceManager.Repo)

unless tags[:async] do
Sandbox.mode(ResourceManager.Repo, {:shared, self()})
end

{:ok, conn: Phoenix.ConnTest.build_conn()}
end
end
32 changes: 32 additions & 0 deletions apps/rest_api/test/support/factory.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
defmodule RestAPI.Factory do
@moduledoc false

alias ResourceManager.Identities.Schemas.User
alias ResourceManager.Repo

@doc "Builds a default struct from the requested model"
@spec build(model :: atom()) :: struct()
def build(:user) do
%User{
username: "my-test-username#{System.unique_integer()}",
status: "active",
is_admin: false
}
end

@doc "Returns the a model struct with the given attributes"
@spec build(factory_name :: atom(), attributes :: Keyword.t()) :: struct()
def build(factory_name, attributes) when is_atom(factory_name) and is_list(attributes) do
factory_name
|> build()
|> struct!(attributes)
end

@doc "Inserts a model with the given attributes on database"
@spec insert!(factory_name :: atom(), attributes :: Keyword.t()) :: struct()
def insert!(factory_name, attributes \\ []) when is_atom(factory_name) do
factory_name
|> build(attributes)
|> Repo.insert!()
end
end
Comment on lines +1 to +32
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.
This is not necessary.

2 changes: 1 addition & 1 deletion apps/rest_api/test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
ExUnit.configure(formatters: [JUnitFormatter])
ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter])
ExUnit.start()