Commit c1790119 authored by Jacob Priddy's avatar Jacob Priddy 👌
Browse files

Delete Group Endpoint

parent 8a44c180
......@@ -4,6 +4,9 @@ namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Source\Authorization\Permissions;
use Source\Exceptions\DeleteFailedException;
use Source\UseCases\Groups\DeleteGroup\APIPresenter as DeleteGroupAPIPresenter;
use Source\UseCases\Groups\DeleteGroup\DeleteGroupUseCase;
use Source\UseCases\Groups\CreateGroup\APIPresenter as CreateGroupAPIPresenter;
use Source\UseCases\Groups\CreateGroup\CreateGroupUseCase;
......@@ -32,4 +35,19 @@ class GroupsController extends ApiController
return $this->respondWithData($presenter->getViewModel());
}
public function delete(DeleteGroupUseCase $useCase, string $groupId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_GROUPS]);
$presenter = new DeleteGroupAPIPresenter();
try {
$useCase->delete($groupId, $presenter);
} catch (DeleteFailedException $e) {
return $this->respondWithError($e->getMessage());
}
return $this->respondWithData($presenter->getViewModel());
}
}
......@@ -5,12 +5,12 @@ namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Source\Authorization\Permissions;
use Source\Exceptions\DeleteFailedException;
use Source\UseCases\Users\GetUser\GetUserUseCase;
use Source\UseCases\Users\CreateUser\CreateUserUseCase;
use Source\UseCases\Users\DeleteUser\DeleteUserUseCase;
use Source\UseCases\Users\UpdateUser\UpdateUserUseCase;
use Source\UseCases\Users\GetAllUsers\GetAllUsersUseCase;
use Source\UseCases\Users\DeleteUser\DeleteFailedException;
use Source\UseCases\Users\GetUser\APIPresenter as GetUserAPIPresenter;
use Source\UseCases\Users\GetAllUsers\APIPresenter as AllUsersAPIPresenter;
use Source\UseCases\Users\CreateUser\APIPresenter as CreateUserAPIPresenter;
......
......@@ -16,6 +16,7 @@ use Source\UseCases\Users\DeleteUser\DeleteUserUseCaseServiceProvider;
use Source\UseCases\Users\UpdateUser\UpdateUserUseCaseServiceProvider;
use Source\UseCases\Users\GetAllUsers\GetAllUsersUseCaseServiceProvider;
use Source\UseCases\Groups\CreateGroup\CreateGroupUseCaseServiceProvider;
use Source\UseCases\Groups\DeleteGroup\DeleteGroupUseCaseServiceProvider;
use Source\UseCases\Token\Authenticate\AuthenticateUseCaseServiceProvider;
use Source\UseCases\Doors\Authenticate\AuthenticateUseCaseServiceProvider as DoorAuthenticateUseCaseServiceProvider;
use Source\UseCases\Users\Authenticate\AuthenticateUseCaseServiceProvider as UserAuthenticateUseCaseServiceProvider;
......@@ -48,6 +49,7 @@ class AppServiceProvider extends ServiceProvider
// Groups
CreateGroupUseCaseServiceProvider::class,
DeleteGroupUseCaseServiceProvider::class,
// Doors
AuthenticateUseCaseServiceProvider::class,
......
......@@ -38,7 +38,7 @@ Route::group(['middleware' => 'auth:api'], static function () {
Route::post('/', [GroupsController::class, 'store']);
Route::get('{groupId}', [GroupsController::class, 'get']);
Route::put('{groupId}', [GroupsController::class, 'update']);
Route::delete('{groupId', [GroupsController::class, 'delete']);
Route::delete('{groupId}', [GroupsController::class, 'delete']);
});
Route::get('/user', static function (Request $request) {
......
<?php
namespace Source\UseCases\Users\DeleteUser;
namespace Source\Exceptions;
use Exception;
use Throwable;
......
<?php
namespace Source\UseCases\Groups\DeleteGroup;
use Source\UseCases\BasePresenter;
class APIPresenter extends BasePresenter implements Presenter
{
protected array $viewModel = [];
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
$this->viewModel['message'] = $responseModel->getMessage();
}
/** @inheritDoc */
public function getViewModel(): array
{
return $this->viewModel;
}
}
<?php
namespace Source\UseCases\Groups\DeleteGroup;
use Source\Gateways\Groups\GroupsRepository;
use Source\Exceptions\DeleteFailedException;
class DeleteGroup implements DeleteGroupUseCase
{
/**
* @var \Source\Gateways\Groups\GroupsRepository
*/
protected GroupsRepository $groups;
public function __construct(GroupsRepository $groups)
{
$this->groups = $groups;
}
/**
* @inheritDoc
*/
public function delete(string $groupId, Presenter $presenter): void
{
if (!$this->groups->delete($groupId)) {
throw new DeleteFailedException('Unable to delete group.');
}
$response = new ResponseModel('Group deleted.');
$presenter->present($response);
}
}
<?php
namespace Source\UseCases\Groups\DeleteGroup;
interface DeleteGroupUseCase
{
/**
* @param string $groupId
* @param \Source\UseCases\Groups\DeleteGroup\Presenter $presenter
* @throws \Source\Exceptions\DeleteFailedException
*/
public function delete(string $groupId, Presenter $presenter): void;
}
<?php
namespace Source\UseCases\Groups\DeleteGroup;
use Source\Gateways\Groups\GroupsRepository;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
/**
* Service provider must be registered in AppServiceProvider
*/
class DeleteGroupUseCaseServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->bind(DeleteGroupUseCase::class, static function (Application $app) {
return new DeleteGroup($app->make(GroupsRepository::class));
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot(): void
{
}
/**
* @return array
*/
public function provides()
{
return [DeleteGroupUseCase::class];
}
}
<?php
namespace Source\UseCases\Groups\DeleteGroup;
interface Presenter
{
/**
* @param ResponseModel $responseModel
* @return void
*/
public function present(ResponseModel $responseModel): void;
/**
* @return array
*/
public function getViewModel(): array;
}
<?php
namespace Source\UseCases\Groups\DeleteGroup;
class ResponseModel
{
/**
* @var string
*/
protected string $message;
/**
* @param string $message
*/
public function __construct(string $message)
{
$this->message = $message;
}
/**
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
}
......@@ -3,6 +3,7 @@
namespace Source\UseCases\Users\DeleteUser;
use Source\Gateways\Users\UsersRepository;
use Source\Exceptions\DeleteFailedException;
class DeleteUser implements DeleteUserUseCase
{
......
......@@ -6,9 +6,9 @@ namespace Source\UseCases\Users\DeleteUser;
interface DeleteUserUseCase
{
/**
* @param string $userId
* @param Presenter $presenter
* @throws DeleteFailedException
* @param string $userId
* @param \Source\UseCases\Users\DeleteUser\Presenter $presenter
* @throws \Source\Exceptions\DeleteFailedException
*/
public function delete(string $userId, Presenter $presenter): void;
}
<?php
namespace Tests\Doubles;
use Source\Entities\Group;
use Source\Gateways\Groups\GroupsRepository;
class InMemoryGroupsRepositoryStub implements GroupsRepository
{
protected ?Group $user;
/**
* @param string $userId
* @return Group|null
*/
public function get(string $userId): ?Group
{
if ($this->user) {
return $this->user;
}
return null;
}
/**
* @return array
*/
public function all(): array
{
return [];
}
/**
* @param Group $user
* @return Group|null
*/
public function create(Group $user): ?Group
{
return null;
}
/**
* @param string $userId
* @param Group $user
* @return Group|null
*/
public function update(string $userId, Group $user): ?Group
{
return null;
}
/**
* @param string $userId
* @return bool
*/
public function delete(string $userId): bool
{
return false;
}
/**
* @param string $userId
* @return bool
*/
public function exists(string $userId): bool
{
return true;
}
}
<?php
namespace Tests\Feature\Api\Groups;
use Source\Entities\Group;
use Source\Gateways\Groups\GroupsRepository;
use Source\Exceptions\EntityNotFoundException;
use Illuminate\Foundation\Testing\TestResponse;
use Tests\Doubles\InMemoryGroupsRepositoryStub;
use Tests\Feature\AuthenticatesWithApplicationTestCase;
class DeleteGroupApiTest extends AuthenticatesWithApplicationTestCase
{
/**
* @var \Illuminate\Foundation\Testing\TestResponse
*/
protected TestResponse $response;
/**
* @var mixed|\Source\Gateways\Groups\GroupsRepository
*/
protected GroupsRepository $groups;
public function setUp(): void
{
parent::setUp();
$this->groups = $this->app->make(GroupsRepository::class);
}
protected function stubGroupsRepository(): void
{
$this->app->bind(GroupsRepository::class, static function () {
return new InMemoryGroupsRepositoryStub();
});
}
protected function handleTest(string $groupId): void
{
$this->response = $this->deleteJson('/groups/' . $groupId, [
'api_token' => $this->authToken,
]);
}
/**
* @test
*/
public function it_denies_unauthorized(): void
{
$this->handleTest('group id');
$this->response->assertStatus(401);
}
/**
* @test
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function it_protects_the_route(): void
{
$this->authorize(false);
$this->handleTest('group id');
$this->response->assertStatus(403);
}
/**
* @test
* @throws EntityNotFoundException
*/
public function it_deletes_a_non_existent_group(): void
{
$this->authenticate();
$this->handleTest('asdfasfd non existent group');
$this->response->assertStatus(200);
$this->response->assertJson(
[
'message' => 'Group deleted.',
'status' => 'success',
'code' => 200,
]
);
}
/**
* @test
* @throws EntityNotFoundException
*/
public function it_deletes_a_group(): void
{
$this->authenticate();
$group = new Group(69, '', '');
$this->groups->create($group);
$this->handleTest('69');
$this->response->assertStatus(200);
$this->response->assertJson([
'message' => 'Group deleted.',
'status' => 'success',
'code' => 200,
]);
$this->assertCount(0, $this->groups->all());
}
/**
* @test
* @throws EntityNotFoundException
*/
public function it_handles_a_failed_delete(): void
{
$this->authenticate();
$this->stubGroupsRepository();
$this->handleTest('asdf');
$this->response->assertStatus(200);
$this->response->assertJson([
'message' => 'Unable to delete group.',
'status' => 'error',
'code' => 200,
]);
}
}
<?php
namespace Tests\Unit\Source\UseCases\Groups\DeleteGroup;
use Source\UseCases\Groups\DeleteGroup\Presenter;
use Source\UseCases\Groups\DeleteGroup\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 Unit\Source\UseCases\Groups\DeleteGroup;
use PHPUnit\Framework\TestCase;
use Source\UseCases\Groups\DeleteGroup\APIPresenter;
use Source\UseCases\Groups\DeleteGroup\ResponseModel;
class PresenterTest extends TestCase
{
protected APIPresenter $presenter;
protected ResponseModel $model;
protected array $response;
public function setUp(): void
{
parent::setUp();
$this->presenter = new APIPresenter();
}
public function handleTest(string $message): void
{
$this->model = new ResponseModel($message);
$this->presenter->present($this->model);
$this->response = $this->presenter->getViewModel();
}
/** @test */
public function it_formats_a_message(): void
{
$this->handleTest('hello');
$this->assertEquals(['message' => 'hello'], $this->response);
}
}
<?php
namespace Tests\Unit\Source\UseCases\Groups\DeleteGroup;
use Source\Entities\Group;
use PHPUnit\Framework\TestCase;
use Source\Exceptions\DeleteFailedException;
use Source\UseCases\Groups\DeleteGroup\DeleteGroup;
use Source\Gateways\Groups\InMemoryGroupsRepository;
use Source\UseCases\Groups\DeleteGroup\ResponseModel;
class UseCaseTest extends TestCase
{
protected InMemoryGroupsRepository $groupsRepository;
protected DeleteGroup $useCase;
protected ResponseModel $response;
protected PresenterStub $presenter;
public function setUp(): void
{
parent::setUp();
$this->groupsRepository = new InMemoryGroupsRepository();
$this->useCase = new DeleteGroup($this->groupsRepository);
$this->presenter = new PresenterStub();
}
/**
* @param string $groupId
* @throws DeleteFailedException
*/
public function handleTest(string $groupId): void
{
$this->useCase->delete($groupId, $this->presenter);
$this->response = $this->presenter->response;
}
/**
* @test
* @throws DeleteFailedException
*/
public function presenter_was_called(): void
{
$this->handleTest('123');