Commit 07c50338 authored by Jacob Priddy's avatar Jacob Priddy 👌

Add search and pagination for users as well as make database searches

case insensitive
parent b4439d40
......@@ -10,13 +10,13 @@ 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\GetUsers\GetUsersUseCase;
use Source\UseCases\GroupUser\GetUserGroups\GetUserGroupsUseCase;
use Source\UseCases\DoorUser\UserDoorAccess\UserDoorAccessUseCase;
use Source\UseCases\DoorUser\UserDoorAccess\PrivilegedApiPresenter;
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\GetUsers\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;
......@@ -28,20 +28,27 @@ use Source\UseCases\GroupUser\RemoveUserFromGroup\APIPresenter as RemoveUserFrom
class UsersController extends ApiController
{
/**
* @param \Source\UseCases\Users\GetAllUsers\GetAllUsersUseCase $getAllUsers
* @param \Source\UseCases\Users\GetUsers\GetUsersUseCase $getAllUsers
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Illuminate\Validation\ValidationException
*/
public function index(GetAllUsersUseCase $getAllUsers): JsonResponse
public function index(GetUsersUseCase $getAllUsers): JsonResponse
{
$this->authorizer->protect(Permissions::MANAGE_USERS);
$this->validate($this->request, [
'query' => 'string'
]);
$presenter = new AllUsersAPIPresenter();
$getAllUsers->all($presenter);
$getAllUsers->search($this->request->input('query'), $presenter);
return $this->respondWithData($presenter->getViewModel());
return $this->respondWithData($presenter->getViewModel([
'query' => $this->request->input('query'),
]));
}
/**
......
......@@ -33,7 +33,7 @@ use Source\UseCases\Users\DeleteUser\DeleteUserUseCaseServiceProvider;
use Source\UseCases\Users\UpdateUser\UpdateUserUseCaseServiceProvider;
use Source\Gateways\DoorSchedule\DoorScheduleRepositoryServiceProvider;
use Source\UseCases\Entries\GetEntries\GetEntriesUseCaseServiceProvider;
use Source\UseCases\Users\GetAllUsers\GetAllUsersUseCaseServiceProvider;
use Source\UseCases\Users\GetUsers\GetAllUsersUseCaseServiceProvider;
use Source\Gateways\RecurrenceSet\RecurrenceSetRepositoryServiceProvider;
use Source\UseCases\Door\UpdateBinary\UpdateBinaryUseCaseServiceProvider;
use Source\UseCases\Groups\CreateGroup\CreateGroupUseCaseServiceProvider;
......
......@@ -18,7 +18,7 @@ class UsersSeeder extends Seeder
*/
public function run(DatabaseUsersRepository $users, DatabaseGroupUserRepository $repository): void
{
if ($users->all()) {
if ($users->search()) {
throw new RuntimeException('Cannot seed database as it not empty!');
}
......
......@@ -19,7 +19,7 @@ class LocalDoorScheduleRepository extends InMemoryDoorScheduleRepository
* then we can add all the schedules to the doors here.
*/
try {
foreach ($doors->query() as $door) {
foreach ($doors->search() as $door) {
foreach ($doorGroups->getGroupsForDoor($door->getId()) as $group) {
foreach ($schedules->filter($group->getId()) as $schedule) {
$this->attachScheduleToDoor($door->getId(), $schedule);
......
......@@ -18,7 +18,7 @@ class LocalDoorUserRepository extends InMemoryDoorUserRepository
DoorGroupRepository $doorGroup
) {
// Automatically add all users to all doors, and all doors to all users
foreach ($users->all() as $user) {
foreach ($users->search() as $user) {
try {
foreach ($groupUser->getGroupsForUser($user->getId()) as $group) {
$this->addGroupToUser($user->getId(), $group);
......@@ -28,7 +28,7 @@ class LocalDoorUserRepository extends InMemoryDoorUserRepository
}
}
foreach ($doors->query() as $door) {
foreach ($doors->search() as $door) {
try {
foreach ($doorGroup->getGroupsForDoor($door->getId()) as $group) {
$this->addGroupToDoor($door->getId(), $group);
......
......@@ -104,16 +104,15 @@ class DatabaseDoorsRepository implements DoorsRepository
/**
* @inheritDoc
*/
public function query(?string $query = null): array
public function search(?string $query = null): array
{
$doors = [];
if (!$query) {
$doors = \App\Door::all()->values()->all();
} else {
$doors = \App\Door::query()
->where('location', 'LIKE', "%$query%")
->where('name', 'LIKE', "%$query%")
->where('version', 'LIKE', "%$query%")
->where('location', 'ILIKE', "%$query%")
->where('name', 'ILIKE', "%$query%")
->where('version', 'ILIKE', "%$query%")
->get()->values()->all();
}
......
......@@ -24,7 +24,7 @@ interface DoorsRepository
* @param string|null $query
* @return Door[]
*/
public function query(?string $query = null): array;
public function search(?string $query = null): array;
/**
* Attempt to find a door by a token
......
......@@ -73,16 +73,16 @@ class InMemoryDoorsRepository implements DoorsRepository
/**
* @inheritDoc
*/
public function query(?string $query = null): array
public function search(?string $query = null): array
{
if (!$query) {
return $this->doors;
}
return array_filter($this->doors, static function (Door $door) use ($query) {
return strpos($door->getLocation(), $query) !== false
|| strpos($door->getName(), $query) !== false
|| strpos($door->getVersion(), $query) !== false;
return stripos($door->getLocation(), $query) !== false
|| stripos($door->getName(), $query) !== false
|| stripos($door->getVersion(), $query) !== false;
});
}
......
......@@ -43,14 +43,12 @@ class DatabaseGroupsRepository implements GroupsRepository
/**
* @inheritDoc
*/
public function search(?string $query): array
public function search(?string $query = null): array
{
$groups = [];
if ($query) {
$groups = \App\Group::query()
->where('title', 'LIKE', "%$query%")
->orWhere('description', 'LIKE', "%$query%")
->where('title', 'ILIKE', "%$query%")
->orWhere('description', 'ILIKE', "%$query%")
->get()->values()->all();
} else {
$groups = \App\Group::all()->values()->all();
......
......@@ -19,7 +19,7 @@ interface GroupsRepository
* @param string|null $query
* @return Group[]
*/
public function search(?string $query): array;
public function search(?string $query = null): array;
/**
* It is the callers responsibility to check the exists function first to make sure it does not exist.
......
......@@ -20,15 +20,15 @@ class InMemoryGroupsRepository implements GroupsRepository
/**
* @inheritDoc
*/
public function search(?string $query): array
public function search(?string $query = null): array
{
if (!$query) {
return $this->groups;
}
return array_filter($this->groups, static function (Group $group) use ($query) {
return strpos($group->getTitle(), $query) !== false ||
strpos($group->getDescription(), $query) !== false;
return stripos($group->getTitle(), $query) !== false ||
stripos($group->getDescription(), $query) !== false;
});
}
......
......@@ -52,16 +52,23 @@ class DatabaseUsersRepository implements UsersRepository
/**
* @inheritDoc
*/
public function all(): array
public function search(?string $query = null): array
{
$users = \App\User::all()->values()->all();
if (!$query) {
$users = \App\User::all()->values()->all();
} else {
$users = \App\User::query()
->where('first_name', 'ILIKE', "%$query%")
->orWhere('last_name', 'ILIKE', "%$query%")
->orWhere('display_name', 'ILIKE', "%$query%")
->orWhere('email', 'ILIKE', "%$query%")
->orWhere('emplid', 'ILIKE', "%$query%")
->get()->values()->all();
}
return array_map(
static function (\App\User $user) {
return self::makeUserFromDbUser($user);
},
$users
);
return array_map(static function (\App\User $user) {
return self::makeUserFromDbUser($user);
}, $users);
}
/**
......
......@@ -21,9 +21,21 @@ class InMemoryUsersRepository implements UsersRepository
/**
* @inheritDoc
*/
public function all(): array
public function search(?string $query = null): array
{
return $this->users;
$query = strtolower($query);
if (!$query) {
return $this->users;
}
return array_filter($this->users, static function (User $user) use ($query) {
return stripos($user->getFirstName(), $query) !== false
|| stripos($user->getLastName(), $query) !== false
|| stripos($user->getDisplayName(), $query) !== false
|| stripos($user->getEmail(), $query) !== false
|| stripos($user->getEmplid(), $query) !== false;
});
}
/**
......
......@@ -15,9 +15,10 @@ interface UsersRepository
public function get(string $userId): ?User;
/**
* @param string|null $query
* @return User[]
*/
public function all(): array;
public function search(?string $query = null): array;
/**
* @param User $user
......
......@@ -23,7 +23,7 @@ class GetDoors implements GetDoorsUseCase
{
$response = new ResponseModel();
foreach ($this->doorsRepository->query($query) as $door) {
foreach ($this->doorsRepository->search($query) as $door) {
$response->addDoor($door);
}
......
<?php
namespace Source\UseCases\Users\GetAllUsers;
interface GetAllUsersUseCase
{
/**
* @param \Source\UseCases\Users\GetAllUsers\Presenter $presenter
*/
public function all(Presenter $presenter): void;
}
<?php
namespace Source\UseCases\Users\GetAllUsers;
namespace Source\UseCases\Users\GetUsers;
use Source\Entities\User;
use Source\Sanitize\Paginates;
use Source\UseCases\BasePresenter;
class APIPresenter extends BasePresenter implements Presenter
{
protected array $viewModel = [];
use Paginates;
protected array $users = [];
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
$this->viewModel['users'] = array_map(function (User $user) {
$this->users = array_map(function (User $user) {
return $this->formatPartialUser($user);
}, $responseModel->getUsers());
}
/** @inheritDoc */
public function getViewModel(): array
public function getViewModel(array $appends = []): array
{
return $this->paginate($this->users, $appends);
}
public function raw(): array
{
return $this->viewModel;
return ['users' => $this->users];
}
}
<?php
namespace Source\UseCases\Users\GetAllUsers;
namespace Source\UseCases\Users\GetUsers;
use Illuminate\Support\ServiceProvider;
use Source\Gateways\Users\UsersRepository;
......@@ -21,9 +21,9 @@ class GetAllUsersUseCaseServiceProvider extends ServiceProvider implements Defer
public function register()
{
$this->app->bind(
GetAllUsersUseCase::class,
GetUsersUseCase::class,
static function (Application $app) {
return new GetAllUsers($app->make(UsersRepository::class));
return new GetUsers($app->make(UsersRepository::class));
}
);
}
......@@ -42,6 +42,6 @@ class GetAllUsersUseCaseServiceProvider extends ServiceProvider implements Defer
*/
public function provides()
{
return [GetAllUsersUseCase::class];
return [GetUsersUseCase::class];
}
}
<?php
namespace Source\UseCases\Users\GetAllUsers;
namespace Source\UseCases\Users\GetUsers;
use Source\Gateways\Users\UsersRepository;
class GetAllUsers implements GetAllUsersUseCase
class GetUsers implements GetUsersUseCase
{
/**
* @var UsersRepository
......@@ -19,11 +19,11 @@ class GetAllUsers implements GetAllUsersUseCase
/**
* @inheritDoc
*/
public function all(Presenter $presenter): void
public function search(?string $query, Presenter $presenter): void
{
$response = new ResponseModel();
foreach ($this->usersRepository->all() as $user) {
foreach ($this->usersRepository->search($query) as $user) {
$response->addUser($user);
}
......
<?php
namespace Source\UseCases\Users\GetUsers;
interface GetUsersUseCase
{
/**
* Will return all users if query is empty
*
* @param string|null $query
* @param \Source\UseCases\Users\GetUsers\Presenter $presenter
*/
public function search(?string $query, Presenter $presenter): void;
}
<?php
namespace Source\UseCases\Users\GetAllUsers;
namespace Source\UseCases\Users\GetUsers;
interface Presenter
{
......@@ -11,7 +11,8 @@ interface Presenter
public function present(ResponseModel $responseModel): void;
/**
* @param array $appends
* @return array
*/
public function getViewModel(): array;
public function getViewModel(array $appends = []): array;
}
<?php
namespace Source\UseCases\Users\GetAllUsers;
namespace Source\UseCases\Users\GetUsers;
use Source\Entities\User;
......
......@@ -37,7 +37,7 @@ class DoorDatabaseTest extends DatabaseTestCase
$this->assertNull($this->doors->getByToken(new HashedSearchable('i am autistic')));
$this->assertNotNull($this->doors->get($d->getId()));
$this->assertNull($this->doors->get('asdf'));
$this->assertCount(1, $this->doors->query());
$this->assertCount(1, $this->doors->search());
$this->assertNotNull($this->doors->findByName('name'));
$this->assertNull($this->doors->findByName('ree'));
$this->assertNull($this->doors->findByName(null));
......@@ -51,9 +51,9 @@ class DoorDatabaseTest extends DatabaseTestCase
{
$d = $this->doors->create(new Door(0, 'loc', 'name', new HashedSearchable('token')));
$this->assertFalse($this->doors->delete('ah idk if im gonna finish this project. Im sorry'));
$this->assertCount(1, $this->doors->query());
$this->assertCount(1, $this->doors->search());
$this->assertTrue($this->doors->delete($d->getId()));
$this->assertCount(0, $this->doors->query());
$this->assertCount(0, $this->doors->search());
}
/**
......
......@@ -42,14 +42,14 @@ class UserDatabaseTest extends DatabaseTestCase
*/
public function it_gets_all_users(): void
{
$all = $this->users->all();
$all = $this->users->search();
$this->assertEquals([], $all);
$this->users->create(new User(0, '', '', '', 'email@email.com', null, null, null));
$this->users->create(new User(0, '', '', '', 'email2@email.com', null, null, null));
$all = $this->users->all();
$all = $this->users->search();
$this->assertCount(2, $all);
}
......@@ -190,4 +190,26 @@ class UserDatabaseTest extends DatabaseTestCase
$this->assertNull($this->users->findByDoorcode(new HashedSearchable('what doorcode')));
$this->assertNull($this->users->findByDoorcode(null));
}
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
*/
public function it_can_search_users(): void
{
$this->users->create($this->createUser());
$this->assertCount(1, $this->users->search('DISP'));
}
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
*/
public function it_wont_find_users_on_search(): void
{
$this->users->create($this->createUser());
$this->assertCount(0, $this->users->search('IN VALID REEE'));
}
}
......@@ -32,7 +32,7 @@ class InMemoryGroupsRepositoryStub implements GroupsRepository
* @param string|null $query
* @return array
*/
public function search(?string $query): array
public function search(?string $query = null): array
{
return [];
}
......
......@@ -29,9 +29,10 @@ class InMemoryUsersRepositoryStub implements UsersRepository
}
/**
* @param string|null $query
* @return array
*/
public function all(): array
public function search(?string $query = null): array
{
return [];
}
......
......@@ -228,6 +228,6 @@ class CreateUserApiTest extends AuthenticatesWithApplicationTestCase
]
);
$this->assertCount(2, $this->usersRepository->all());
$this->assertCount(2, $this->usersRepository->search());
}
}
......@@ -76,7 +76,7 @@ class DeleteUserApiTest extends AuthenticatesWithApplicationTestCase
'status' => 'success',
'code' => 200,
]);
$this->assertCount(1, $this->usersRepository->all());
$this->assertCount(1, $this->usersRepository->search());
}
/**
......
......@@ -72,7 +72,7 @@ class GetAllUsersApiTest extends AuthenticatesWithApplicationTestCase
$this->handleTest();
$this->response->assertStatus(200);
$this->response->assertJsonCount(2, 'users');
$this->response->assertJsonCount(2, 'data');
$this->response->assertJson(['status' => 'success']);
}
}
......@@ -115,9 +115,9 @@ class SamlUseCaseTest extends UseCaseBaseTest
$this->handleLoginTest($samlUser);
$this->assertCount(1, $this->users->all());
$this->assertCount(1, $this->users->search());
$user = $this->users->all()[0];
$user = $this->users->search()[0];
$this->assertEquals('First', $user->getFirstName());
$this->assertEquals('Last', $user->getLastName());
......
......@@ -84,7 +84,7 @@ class UseCaseTest extends TestCase
$this->handleTest($this->createUserAttributes($user));
$this->assertEquals($user->getFirstName(), $this->response->getUser()->getFirstName());
$this->assertCount(1, $this->usersRepository->all());
$this->assertCount(1, $this->usersRepository->search());
}
/**
......
......@@ -78,7 +78,7 @@ class UseCaseTest extends TestCase
$this->handleTest('69');
$this->assertCount(1, $this->usersRepository->all());
$this->assertCount(1, $this->usersRepository->search());