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

Merge branch '4-get-users-api-implemented' into 'master'

Resolve "get users API implemented"

Closes #4

See merge request kretschmar/doorcode!10
parents 8ffbbed6 509c85b1
Pipeline #2228 passed with stages
in 1 minute and 56 seconds
......@@ -3,7 +3,9 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Source\Authorization\Authorizer;
abstract class ApiController extends Controller
{
......@@ -12,6 +14,22 @@ abstract class ApiController extends Controller
*/
protected int $status = 200;
/**
* @var \Illuminate\Http\Request
*/
protected Request $request;
/**
* @var \Source\Authorization\Authorizer
*/
protected Authorizer $authorizer;
public function __construct(Request $request, Authorizer $authorizer)
{
$this->request = $request;
$this->authorizer = $authorizer;
}
/**
* @param int $code
*/
......@@ -31,7 +49,7 @@ abstract class ApiController extends Controller
$data,
[
'status' => 'success',
'code' => $this->status,
'code' => $this->status,
]
),
$this->status
......@@ -40,14 +58,11 @@ abstract class ApiController extends Controller
public function respondWithMessage(string $message): JsonResponse
{
return response()->json(
[
'message' => $message,
'status' => 'success',
'code' => $this->status,
],
$this->status
);
return response()->json([
'message' => $message,
'status' => 'success',
'code' => $this->status,
], $this->status);
}
/**
......@@ -56,13 +71,10 @@ abstract class ApiController extends Controller
*/
public function respondWithError(string $message): JsonResponse
{
return response()->json(
[
'status' => 'error',
'code' => $this->status,
'message' => $message,
],
$this->status
);
return response()->json([
'status' => 'error',
'code' => $this->status,
'message' => $message,
], $this->status);
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Source\Authorization\Permissions;
use Source\Exceptions\DeleteFailedException;
use Source\UseCases\Groups\GetGroup\GetGroupUseCase;
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\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;
class GroupsController extends ApiController
{
/**
* @param \Source\UseCases\Groups\CreateGroup\CreateGroupUseCase $useCase
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Illuminate\Validation\ValidationException
* @throws \Source\Exceptions\EntityExistsException
*/
public function store(CreateGroupUseCase $useCase): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_GROUPS]);
$this->validate($this->request, [
'title' => 'required|string|max:255',
'description' => 'required|string',
]);
$presenter = new CreateGroupAPIPresenter();
$useCase->create($this->request->all(), $presenter);
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\Groups\DeleteGroup\DeleteGroupUseCase $useCase
* @param string $groupId
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
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());
}
/**
* @param \Source\UseCases\Groups\GetAllGroups\GetAllGroupsUseCase $useCase
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function index(GetAllGroupsUseCase $useCase): JsonResponse
{
$this->authorizer->protectOne([Permissions::MANAGE_GROUPS, Permissions::MANAGE_USERS]);
$presenter = new AllGroupsAPIPresenter();
$useCase->all($presenter);
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\Groups\GetGroup\GetGroupUseCase $useCase
* @param string $groupId
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function get(GetGroupUseCase $useCase, string $groupId): JsonResponse
{
$this->authorizer->protectOne([Permissions::MANAGE_GROUPS, Permissions::MANAGE_USERS]);
$presenter = new GetGroupAPIPresenter();
$useCase->get($groupId, $presenter);
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\Groups\UpdateGroup\UpdateGroupUseCase $useCase
* @param string $groupId
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Validation\ValidationException
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function update(UpdateGroupUseCase $useCase, string $groupId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_GROUPS]);
$this->validate($this->request, [
'title' => 'required|string|max:255',
'description' => 'required|string',
]);
$presenter = new UpdateGroupAPIPresenter();
$useCase->update($groupId, $this->request->all(), $presenter);
if ($presenter->hasError()) {
return $this->respondWithError($presenter->getViewModel()['message']);
}
return $this->respondWithData($presenter->getViewModel());
}
}
......@@ -3,44 +3,26 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Source\Authorization\Authorizer;
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\GroupUser\AddUserToGroup\AddUserToGroupUseCase;
use Source\UseCases\Users\GetUser\APIPresenter as GetUserAPIPresenter;
use Source\UseCases\Users\GetAllUsers\APIPresenter as AllUsersAPIPresenter;
use Source\UseCases\Users\CreateUser\APIPresenter as CreateUserAPIPresenter;
use Source\UseCases\Users\DeleteUser\APIPresenter as DeleteUserAPIPresenter;
use Source\UseCases\Users\UpdateUser\APIPresenter as UpdateUserAPIPresenter;
use Source\UseCases\GroupUser\RemoveUserFromGroup\RemoveUserFromGroupUseCase;
use Source\UseCases\GroupUser\AddUserToGroup\APIPresenter as AddUserToGroupAPIPresenter;
use Source\UseCases\GroupUser\RemoveUserFromGroup\APIPresenter as RemoveUserFromGroupAPIPresenter;
class UsersController extends ApiController
{
/**
* @var \Illuminate\Http\Request
*/
protected Request $request;
/**
* @var \Source\Authorization\Authorizer
*/
protected Authorizer $authorizer;
/**
* @param \Illuminate\Http\Request $request
* @param \Source\Authorization\Authorizer $authorizer
*/
public function __construct(Request $request, Authorizer $authorizer)
{
$this->request = $request;
$this->authorizer = $authorizer;
}
/**
* @param \Source\UseCases\Users\GetAllUsers\GetAllUsersUseCase $getAllUsers
* @return \Illuminate\Http\JsonResponse
......@@ -88,19 +70,16 @@ class UsersController extends ApiController
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$this->validate(
$this->request,
[
'first_name' => 'required|string|max:255',
'last_name' => 'required|string|max:255',
'display_name' => 'required|string|max:255',
'emplid' => 'nullable|string|max:7|min:6',
'email' => 'required|email|max:255',
'password' => 'nullable|string|min:15|max:255',
'doorcode' => 'required|string|numeric|digits_between:4,255',
'expires_at' => 'nullable|string|date|max:255',
]
);
$this->validate($this->request, [
'first_name' => 'required|string|max:255',
'last_name' => 'required|string|max:255',
'display_name' => 'required|string|max:255',
'emplid' => 'nullable|string|max:7|min:6',
'email' => 'required|email|max:255',
'password' => 'nullable|string|min:15|max:255',
'doorcode' => 'required|string|numeric|digits_between:4,255',
'expires_at' => 'nullable|string|date|max:255',
]);
$presenter = new CreateUserAPIPresenter();
......@@ -121,24 +100,25 @@ class UsersController extends ApiController
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$this->validate(
$this->request,
[
'first_name' => 'required|string|max:255',
'last_name' => 'required|string|max:255',
'display_name' => 'required|string|max:255',
'emplid' => 'nullable|string|max:7|min:6',
'email' => 'required|email|max:255',
'password' => 'nullable|string|max:255',
'doorcode' => 'nullable|string|numeric|digits_between:4,255',
'expires_at' => 'nullable|string|date|max:255',
]
);
$this->validate($this->request, [
'first_name' => 'required|string|max:255',
'last_name' => 'required|string|max:255',
'display_name' => 'required|string|max:255',
'emplid' => 'nullable|string|max:7|min:6',
'email' => 'required|email|max:255',
'password' => 'nullable|string|max:255',
'doorcode' => 'nullable|string|numeric|digits_between:4,255',
'expires_at' => 'nullable|string|date|max:255',
]);
$presenter = new UpdateUserAPIPresenter();
$updateUser->update($userId, $this->request->all(), $presenter);
if ($presenter->hasError()) {
return $this->respondWithError($presenter->getViewModel()['message']);
}
return $this->respondWithData($presenter->getViewModel());
}
......@@ -163,4 +143,42 @@ class UsersController extends ApiController
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\GroupUser\AddUserToGroup\AddUserToGroupUseCase $useCase
* @param string $userId
* @param string $groupId
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function addUserToGroup(AddUserToGroupUseCase $useCase, string $userId, string $groupId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$presenter = new AddUserToGroupAPIPresenter();
$useCase->addUserToGroup($userId, $groupId, $presenter);
return $this->respondWithMessage($presenter->getViewModel()['message']);
}
/**
* @param \Source\UseCases\GroupUser\RemoveUserFromGroup\RemoveUserFromGroupUseCase $useCase
* @param string $userId
* @param string $groupId
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function removeUserFromGroup(RemoveUserFromGroupUseCase $useCase, string $userId, string $groupId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$presenter = new RemoveUserFromGroupAPIPresenter();
$useCase->removeUserFromGroup($userId, $groupId, $presenter);
return $this->respondWithMessage($presenter->getViewModel()['message']);
}
}
......@@ -11,11 +11,18 @@ use Source\Gateways\Groups\GroupsRepositoryServiceProvider;
use Source\Gateways\Tokens\TokensRepositoryServiceProvider;
use Source\UseCases\Users\GetUser\GetUserUseCaseServiceProvider;
use Source\Gateways\GroupUser\GroupUserRepositoryServiceProvider;
use Source\UseCases\Groups\GetGroup\GetGroupUseCaseServiceProvider;
use Source\UseCases\Users\CreateUser\CreateUserUseCaseServiceProvider;
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\Groups\UpdateGroup\UpdateGroupUseCaseServiceProvider;
use Source\UseCases\Token\Authenticate\AuthenticateUseCaseServiceProvider;
use Source\UseCases\Groups\GetAllGroups\GetAllGroupsUseCaseServiceProvider;
use Source\UseCases\GroupUser\AddUserToGroup\AddUserToGroupUseCaseServiceProvider;
use Source\UseCases\GroupUser\RemoveUserFromGroup\RemoveUserFromGroupUseCaseServiceProvider;
use Source\UseCases\Doors\Authenticate\AuthenticateUseCaseServiceProvider as DoorAuthenticateUseCaseServiceProvider;
use Source\UseCases\Users\Authenticate\AuthenticateUseCaseServiceProvider as UserAuthenticateUseCaseServiceProvider;
......@@ -38,12 +45,25 @@ class AppServiceProvider extends ServiceProvider
* @var string[]
*/
protected array $useCaseProviders = [
// Users
GetUserUseCaseServiceProvider::class,
DeleteUserUseCaseServiceProvider::class,
UpdateUserUseCaseServiceProvider::class,
CreateUserUseCaseServiceProvider::class,
GetAllUsersUseCaseServiceProvider::class,
// Groups
GetGroupUseCaseServiceProvider::class,
CreateGroupUseCaseServiceProvider::class,
DeleteGroupUseCaseServiceProvider::class,
UpdateGroupUseCaseServiceProvider::class,
GetAllGroupsUseCaseServiceProvider::class,
// GroupUser
AddUserToGroupUseCaseServiceProvider::class,
RemoveUserFromGroupUseCaseServiceProvider::class,
// Doors
AuthenticateUseCaseServiceProvider::class,
DoorAuthenticateUseCaseServiceProvider::class,
UserAuthenticateUseCaseServiceProvider::class,
......
......@@ -4,6 +4,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;
use App\Http\Controllers\UsersController;
use App\Http\Controllers\GroupsController;
/*
|--------------------------------------------------------------------------
......@@ -28,6 +29,19 @@ Route::group(['middleware' => 'auth:api'], static function () {
Route::get('{userId}', [UsersController::class, 'get']);
Route::put('{userId}', [UsersController::class, 'update']);
Route::delete('{userId}', [UsersController::class, 'delete']);
Route::post('{userId}/group/{groupId}', [UsersController::class, 'addUserToGroup']);
Route::delete('{userId}/group/{groupId}', [UsersController::class, 'removeUserFromGroup']);
});
Route::group([
'prefix' => 'groups',
], static function () {
Route::get('/', [GroupsController::class, 'index']);
Route::post('/', [GroupsController::class, 'store']);
Route::get('{groupId}', [GroupsController::class, 'get']);
Route::put('{groupId}', [GroupsController::class, 'update']);
Route::delete('{groupId}', [GroupsController::class, 'delete']);
});
Route::get('/user', static function (Request $request) {
......
......@@ -10,5 +10,6 @@ class Permissions
public const MANAGE_DOORS = 'manage-doors';
public const MANAGE_TOKEN = 'manage-token';
public const TOKEN_CREATE = 'token-create';
public const MANAGE_GROUPS = 'manage-groups';
public const CODE_QUERY = 'code-query';
}
<?php
namespace Source\UseCases\Users\DeleteUser;
namespace Source\Exceptions;
use Exception;
use Throwable;
......
......@@ -4,12 +4,12 @@
namespace Source\Gateways\GroupUser;
use Illuminate\Support\ServiceProvider;
use Source\Gateways\Users\UsersRepository;
use Source\Gateways\Groups\GroupsRepository;
use Source\Gateways\Users\LocalUsersRepository;
use Illuminate\Contracts\Foundation\Application;
use Source\Gateways\Groups\LocalGroupsRepository;
use Source\Gateways\Users\InMemoryUsersRepository;
use Illuminate\Contracts\Support\DeferrableProvider;
use Source\Gateways\Groups\InMemoryGroupsRepository;
/**
* Service provider must be registered in AppServiceProvider
......@@ -33,8 +33,8 @@ class GroupUserRepositoryServiceProvider extends ServiceProvider implements Defe
if (env('APP_ENV') === 'testing') {
return new InMemoryGroupUserRepository(
new InMemoryUsersRepository(),
new InMemoryGroupsRepository()
$app->make(UsersRepository::class),
$app->make(GroupsRepository::class)
);
}
......
......@@ -24,6 +24,7 @@ class LocalGroupUserRepository extends InMemoryGroupUserRepository
$this->addUserToGroup(LocalUsersRepository::getSemiPrivilegedUser()->getId(), LocalGroupsRepository::getManageTokenGroup()->getId());
$this->addUserToGroup(LocalUsersRepository::getSemiPrivilegedUser()->getId(), LocalGroupsRepository::getTokenCreateGroup()->getId());
$this->addUserToGroup(LocalUsersRepository::getSemiPrivilegedUser()->getId(), LocalGroupsRepository::getCodeQueryGroup()->getId());
$this->addUserToGroup(LocalUsersRepository::getSemiPrivilegedUser()->getId(), LocalGroupsRepository::getManageGroupsGroup()->getId());
$this->addUserToGroup(
LocalUsersRepository::getComputerScienceStudent()->getId(),
......
......@@ -25,6 +25,8 @@ class LocalGroupsRepository extends InMemoryGroupsRepository
$this->create(static::getComputerScienceMajorGroup());
$this->create(static::getEngineeringLabAccessGroup());
$this->create(static::getManageGroupsGroup());
}
/**
......@@ -122,4 +124,13 @@ class LocalGroupsRepository extends InMemoryGroupsRepository
'Gives access to the Electrical Engineering Labs'
);
}
public static function getManageGroupsGroup(): Group
{
return new Group(
9,
Permissions::MANAGE_GROUPS,
'Gives permission to manage stored groups'
);
}
}
......@@ -5,6 +5,7 @@ namespace Source\UseCases;
use Carbon\Carbon;
use Source\Entities\User;
use Source\Entities\Group;
abstract class BasePresenter
{
......@@ -70,6 +71,17 @@ abstract class BasePresenter
];
}
public function formatGroup(Group $group): array
{
return [
'id' => $group->getId(),
'title' => $group->getTitle(),
'description' => $group->getDescription(),
'created_at' => $this->formatDateTime($group->getCreatedAt()),
'updated_at' => $this->formatDateTime($group->getUpdatedAt()),
];
}
/**
* @param Carbon|null $date
* @param string $format
......
<?php
namespace Source\UseCases\GroupUser\AddUserToGroup;
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\GroupUser\AddUserToGroup;
use Source\Gateways\GroupUser\GroupUserRepository;
class AddUserToGroup implements AddUserToGroupUseCase
{
/**
* @var \Source\Gateways\GroupUser\GroupUserRepository
*/
protected GroupUserRepository $repository;
public function __construct(GroupUserRepository $repository)
{
$this->repository = $repository;
}
/**
* @inheritDoc
*/
public function addUserToGroup(string $userId, string $groupId, Presenter $presenter): void
{