Commit 5b7b0f2b authored by Jacob Priddy's avatar Jacob Priddy 👌
Browse files

Merge branch '5-create-get-users-for-group-route' into 'master'

Resolve "create get users for group route"

Closes #5

See merge request kretschmar/doorcode!16
parents 446b906b 9ed6e3f8
Pipeline #2421 passed with stages
in 1 minute and 44 seconds
......@@ -10,11 +10,13 @@ use Source\UseCases\Groups\CreateGroup\CreateGroupUseCase;
use Source\UseCases\Groups\DeleteGroup\DeleteGroupUseCase;
use Source\UseCases\Groups\UpdateGroup\UpdateGroupUseCase;
use Source\UseCases\Groups\GetAllGroups\GetAllGroupsUseCase;
use Source\UseCases\GroupUser\GetGroupUsers\GetGroupUsersUseCase;
use Source\UseCases\Groups\GetGroup\APIPresenter as GetGroupAPIPresenter;
use Source\UseCases\Groups\GetAllGroups\APIPresenter as AllGroupsAPIPresenter;
use Source\UseCases\Groups\CreateGroup\APIPresenter as CreateGroupAPIPresenter;
use Source\UseCases\Groups\DeleteGroup\APIPresenter as DeleteGroupAPIPresenter;
use Source\UseCases\Groups\UpdateGroup\APIPresenter as UpdateGroupAPIPresenter;
use Source\UseCases\GroupUser\GetGroupUsers\APIPresenter as GetGroupUsersAPIPresenter;
class GroupsController extends ApiController
{
......@@ -126,4 +128,22 @@ class GroupsController extends ApiController
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\GroupUser\GetGroupUsers\GetGroupUsersUseCase $useCase
* @param string $groupId
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function getUsersForGroup(GetGroupUsersUseCase $useCase, string $groupId): JsonResponse
{
$this->authorizer->protectOne([Permissions::MANAGE_GROUPS, Permissions::MANAGE_USERS]);
$presenter = new GetGroupUsersAPIPresenter();
$useCase->getUsersForGroup($groupId, $presenter);
return $this->respondWithData($presenter->getViewModel());
}
}
......@@ -21,6 +21,7 @@ use Source\UseCases\Groups\DeleteGroup\DeleteGroupUseCaseServiceProvider;
use Source\UseCases\Groups\UpdateGroup\UpdateGroupUseCaseServiceProvider;
use Source\UseCases\Token\Authenticate\AuthenticateUseCaseServiceProvider;
use Source\UseCases\Groups\GetAllGroups\GetAllGroupsUseCaseServiceProvider;
use Source\UseCases\GroupUser\GetGroupUsers\GetGroupUsersUseCaseServiceProvider;
use Source\UseCases\GroupUser\GetUserGroups\GetUserGroupsUseCaseServiceProvider;
use Source\UseCases\GroupUser\AddUserToGroup\AddUserToGroupUseCaseServiceProvider;
use Source\UseCases\GroupUser\RemoveUserFromGroup\RemoveUserFromGroupUseCaseServiceProvider;
......@@ -62,6 +63,7 @@ class AppServiceProvider extends ServiceProvider
// GroupUser
GetUserGroupsUseCaseServiceProvider::class,
GetGroupUsersUseCaseServiceProvider::class,
AddUserToGroupUseCaseServiceProvider::class,
RemoveUserFromGroupUseCaseServiceProvider::class,
......
......@@ -44,6 +44,8 @@ Route::group(['middleware' => 'auth:api'], static function () {
Route::get('{groupId}', [GroupsController::class, 'get']);
Route::put('{groupId}', [GroupsController::class, 'update']);
Route::delete('{groupId}', [GroupsController::class, 'delete']);
Route::get('{groupId}/users', [GroupsController::class, 'getUsersForGroup']);
});
Route::get('/user', static function (Request $request) {
......
......@@ -61,6 +61,10 @@ class InMemoryGroupUserRepository implements GroupUserRepository
*/
public function getUsersForGroup(string $groupId): array
{
if (!$this->groups->exists($groupId)) {
throw new EntityNotFoundException();
}
$users = [];
/**
......
......@@ -9,35 +9,6 @@ use Source\Entities\Group;
abstract class BasePresenter
{
/**
* @param bool|null $bool
* @param string $true
* @param string $false
* @return string
*/
public function formatBool(?bool $bool, string $true = 'Yes', string $false = 'No'): ?string
{
if ($bool === null) {
return null;
}
return $bool ? $true : $false;
}
/**
* @param Carbon|null $time
* @param string $format
* @return string|null
*/
public function formatTime(?Carbon $time, string $format = 'g:i A'): ?string
{
if ($time === null) {
return null;
}
return $time->format($format);
}
/**
* @param Carbon|null $datetime
* @param string $format
......@@ -53,10 +24,10 @@ abstract class BasePresenter
}
/**
* @param User $user
* @param \Source\Entities\User $user
* @return array
*/
public function formatUser(User $user): array
public function formatFullUser(User $user): array
{
return [
'id' => $user->getId(),
......@@ -71,6 +42,31 @@ abstract class BasePresenter
];
}
/**
* @param \Source\Entities\User $user
* @return array
*/
public function formatPartialUser(User $user): array
{
$expired = false;
if ($user->getExpiresAt()) {
$expired = $user->getExpiresAt() < Carbon::now();
}
return [
'id' => $user->getId(),
'first_name' => $user->getFirstName(),
'last_name' => $user->getLastName(),
'display_name' => $user->getDisplayName(),
'email' => $user->getEmail(),
'expired' => $expired,
];
}
/**
* @param \Source\Entities\Group $group
* @return array
*/
public function formatGroup(Group $group): array
{
return [
......@@ -81,18 +77,4 @@ abstract class BasePresenter
'updated_at' => $this->formatDateTime($group->getUpdatedAt()),
];
}
/**
* @param Carbon|null $date
* @param string $format
* @return string|null
*/
public function formatDate(?Carbon $date, string $format = 'M j, Y'): ?string
{
if ($date === null) {
return null;
}
return $date->format($format);
}
}
<?php
namespace Source\UseCases\GroupUser\GetGroupUsers;
use Source\Entities\User;
use Source\UseCases\BasePresenter;
class APIPresenter extends BasePresenter implements Presenter
{
protected array $viewModel = [];
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
$this->viewModel['users'] = array_map(function (User $user) {
return $this->formatPartialUser($user);
}, $responseModel->getUsers());
}
/** @inheritDoc */
public function getViewModel(): array
{
return $this->viewModel;
}
}
<?php
namespace Source\UseCases\GroupUser\GetGroupUsers;
use Source\Gateways\GroupUser\GroupUserRepository;
class GetGroupUsers implements GetGroupUsersUseCase
{
/**
* @var \Source\Gateways\GroupUser\GroupUserRepository
*/
protected GroupUserRepository $repository;
public function __construct(GroupUserRepository $repository)
{
$this->repository = $repository;
}
/**
* @inheritDoc
*/
public function getUsersForGroup(string $groupId, Presenter $presenter): void
{
$users = $this->repository->getUsersForGroup($groupId);
$response = new ResponseModel($users);
$presenter->present($response);
}
}
<?php
namespace Source\UseCases\GroupUser\GetGroupUsers;
interface GetGroupUsersUseCase
{
/**
* @param string $groupId
* @param \Source\UseCases\GroupUser\GetGroupUsers\Presenter $presenter
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function getUsersForGroup(string $groupId, Presenter $presenter): void;
}
<?php
namespace Source\UseCases\GroupUser\GetGroupUsers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Foundation\Application;
use Source\Gateways\GroupUser\GroupUserRepository;
use Illuminate\Contracts\Support\DeferrableProvider;
/**
* Service provider must be registered in AppServiceProvider
*/
class GetGroupUsersUseCaseServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->bind(GetGroupUsersUseCase::class, static function (Application $app) {
return new GetGroupUsers($app->make(GroupUserRepository::class));
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot(): void
{
}
/**
* @return array
*/
public function provides()
{
return [GetGroupUsersUseCase::class];
}
}
<?php
namespace Source\UseCases\GroupUser\GetGroupUsers;
interface Presenter
{
/**
* @param ResponseModel $responseModel
* @return void
*/
public function present(ResponseModel $responseModel): void;
/**
* @return array
*/
public function getViewModel(): array;
}
<?php
namespace Source\UseCases\GroupUser\GetGroupUsers;
class ResponseModel
{
/**
* @var \Source\Entities\User[]
*/
protected array $users;
/**
* @param \Source\Entities\User[] $users
*/
public function __construct(array $users)
{
$this->users = $users;
}
public function getUsers(): array
{
return $this->users;
}
}
......@@ -23,7 +23,7 @@ class APIPresenter extends BasePresenter implements Presenter
}
$this->viewModel['user'] = $this->formatUser($user);
$this->viewModel['user'] = $this->formatFullUser($user);
$this->viewModel['token'] = [
'value' => $token->getTokenString(),
'expires_at' => $this->formatDateTime($token->getExpiresAt()),
......
......@@ -11,7 +11,7 @@ class APIPresenter extends BasePresenter implements Presenter
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
$this->viewModel['user'] = $this->formatUser($responseModel->getUser());
$this->viewModel['user'] = $this->formatFullUser($responseModel->getUser());
}
/** @inheritDoc */
......
......@@ -2,7 +2,6 @@
namespace Source\UseCases\Users\GetAllUsers;
use Carbon\Carbon;
use Source\Entities\User;
use Source\UseCases\BasePresenter;
......@@ -13,15 +12,8 @@ class APIPresenter extends BasePresenter implements Presenter
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
$this->viewModel['users'] = array_map(static function (User $user) {
return [
'id' => $user->getId(),
'first_name' => $user->getFirstName(),
'last_name' => $user->getLastName(),
'display_name' => $user->getDisplayName(),
'email' => $user->getEmail(),
'expired' => $user->getExpiresAt() < Carbon::now(),
];
$this->viewModel['users'] = array_map(function (User $user) {
return $this->formatPartialUser($user);
}, $responseModel->getUsers());
}
......
......@@ -11,7 +11,7 @@ class APIPresenter extends BasePresenter implements Presenter
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
$this->viewModel['user'] = $this->formatUser($responseModel->getUser());
$this->viewModel['user'] = $this->formatFullUser($responseModel->getUser());
}
/** @inheritDoc */
......
......@@ -17,7 +17,7 @@ class APIPresenter extends BasePresenter implements Presenter
if ($responseModel->hasError()) {
$this->viewModel['message'] = $responseModel->getError();
} else {
$this->viewModel['user'] = $this->formatUser($responseModel->getUser());
$this->viewModel['user'] = $this->formatFullUser($responseModel->getUser());
}
}
......
<?php
namespace Tests\Feature\Api\Groups;
use Source\Entities\User;
use Source\Entities\Group;
use Source\Gateways\Groups\GroupsRepository;
use Illuminate\Foundation\Testing\TestResponse;
use Source\Gateways\GroupUser\GroupUserRepository;
use Tests\Feature\AuthenticatesWithApplicationTestCase;
class GetUsersForGroupApiTest extends AuthenticatesWithApplicationTestCase
{
protected TestResponse $response;
protected GroupsRepository $groups;
protected GroupUserRepository $repository;
public function setUp(): void
{
parent::setUp();
$this->groups = $this->app->make(GroupsRepository::class);
$this->repository = $this->app->make(GroupUserRepository::class);
}
public function handleTest(string $groupId): void
{
$this->response = $this->getJson("/groups/$groupId/users?api_token={$this->authToken}");
}
/**
* @test
*/
public function it_denies_unauthorized(): void
{
$this->handleTest('uid');
$this->response->assertStatus(401);
}
/**
* @test
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function it_protects_the_route(): void
{
$this->authorize(false);
$this->handleTest('uid');
$this->response->assertStatus(403);
}
/**
* @test
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function it_gets_users_for_a_group(): void
{
$this->authenticate();
$user = $this->usersRepository->create(new User(0, '', '', '', '', '', '', ''));
$group = $this->groups->create(new Group(0, '', ''));
$this->repository->addUserToGroup($user->getId(), $group->getId());
$this->handleTest($group->getId());
$this->response->assertStatus(200);
$this->assertCount(1, $this->response->json('users'));
}
}
......@@ -58,7 +58,7 @@ class GetGroupsForUserApiTest extends AuthenticatesWithApplicationTestCase
* @test
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function it_adds_a_group_to_a_user(): void
public function it_gets_groups_for_a_user(): void
{
$this->authenticate();
......
<?php
namespace Tests\Unit\Source\UseCases\GroupUser\GetGroupUsers;
use Source\UseCases\GroupUser\GetGroupUsers\Presenter;
use Source\UseCases\GroupUser\GetGroupUsers\ResponseModel;
class PresenterStub implements Presenter
{
public ResponseModel $response;
protected bool $presenterCalled = false;
public function present(ResponseModel $responseModel): void
{
$this->presenterCalled = true;
$this->response = $responseModel;
}
public function wasPresenterCalled(): bool
{
return $this->presenterCalled;
}
public function getViewModel(): array
{
return [];
}
}
<?php
namespace Tests\Unit\Source\UseCases\GroupUser\GetGroupUsers;
use Source\Entities\User;
use PHPUnit\Framework\TestCase;
use Source\UseCases\GroupUser\GetGroupUsers\APIPresenter;
use Source\UseCases\GroupUser\GetGroupUsers\ResponseModel;
class PresenterTest extends TestCase
{
protected APIPresenter $presenter;
protected ResponseModel $model;
protected array $response;
public function setUp(): void
{
parent::setUp();
$this->presenter = new APIPresenter();
}
/**
* @param \Source\Entities\Group[] $groups
*/
public function handleTest(array $groups): void
{
$this->model = new ResponseModel($groups);
$this->presenter->present($this->model);
$this->response = $this->presenter->getViewModel();
}
/**
* @test
*/
public function it_formats_users_when_empty(): void
{
$this->handleTest([]);
$this->assertEquals(['users' => []], $this->response);
}
/** @test */
public function it_formats_a_user(): void
{
$users = [
new User(1, '', '', '', '', '', '', ''),
];
$this->handleTest($users);
$this->assertCount(1, $this->response['users']);
$this->assertEquals([
'users' => [
[
'id' => 1,
'first_name' => '',
'last_name' => '',
'display_name' => '',