Commit 4e0f1ee0 authored by Jacob Priddy's avatar Jacob Priddy 👌
Browse files

pagination for entries

parent 3edce393
......@@ -97,6 +97,7 @@ class DoorsController extends ApiController
* @throws \Illuminate\Validation\ValidationException
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityExistsException
*/
public function update(UpdateDoorUseCase $updateDoor, string $doorId): JsonResponse
{
......
......@@ -5,126 +5,43 @@ namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Source\Authorization\Permissions;
use Source\UseCases\Entries\APIPresenter;
use Source\UseCases\Entries\GetEntriesForDoor\GetEntriesForDoorUseCase;
use Source\UseCases\Entries\GetEntriesForUser\GetEntriesForUserUseCase;
use Source\UseCases\Entries\GetEntriesForDateRange\GetEntriesForDateRangeUseCase;
use Source\UseCases\Entries\GetEntriesForDoorAndUser\GetEntriesForDoorAndUserUseCase;
use Source\UseCases\Entries\GetEntries\GetEntriesUseCase;
class EntriesController extends ApiController
{
/**
* @param \Source\UseCases\Entries\GetEntriesForDateRange\GetEntriesForDateRangeUseCase $entriesBetween
* @param \Source\UseCases\Entries\GetEntries\GetEntriesUseCase $userEntries
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Validation\ValidationException
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Exception
*/
public function index(GetEntriesForDateRangeUseCase $entriesBetween): JsonResponse
public function index(GetEntriesUseCase $userEntries): JsonResponse
{
$this->authorizer->protect(Permissions::LOGS_READ);
$this->validate($this->request, [
'start' => 'required|date|before:end',
'end' => 'required|date|after:start',
'start' => 'date',
'end' => 'date',
'door_id' => 'integer',
'user_id' => 'integer',
]);
$presenter = new APIPresenter();
$entriesBetween->getForRange($this->request->input('start'), $this->request->input('end'), $presenter);
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\Entries\GetEntriesForDoor\GetEntriesForDoorUseCase $doorEntries
* @param string $doorId
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Validation\ValidationException
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function doorEntries(GetEntriesForDoorUseCase $doorEntries, string $doorId): JsonResponse
{
$this->authorizer->protect(Permissions::LOGS_READ);
$this->validate($this->request, [
'start' => 'required|date|before:end',
'end' => 'required|date|after:start',
]);
$presenter = new APIPresenter();
$doorEntries->getEntriesForDoorBetween(
$doorId,
$this->request->input('start'),
$this->request->input('end'),
$presenter
);
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\Entries\GetEntriesForUser\GetEntriesForUserUseCase $userEntries
* @param string $userId
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Validation\ValidationException
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function userEntries(GetEntriesForUserUseCase $userEntries, string $userId): JsonResponse
{
$this->authorizer->protect(Permissions::LOGS_READ);
$this->validate($this->request, [
'start' => 'required|date|before:end',
'end' => 'required|date|after:start',
]);
$presenter = new APIPresenter();
$userEntries->getEntriesForUserBetween(
$userId,
$this->request->input('start'),
$this->request->input('end'),
$presenter
);
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\Entries\GetEntriesForDoorAndUser\GetEntriesForDoorAndUserUseCase $doorUserEntries
* @param string $doorId
* @param string $userId
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Validation\ValidationException
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function doorUserEntries(
GetEntriesForDoorAndUserUseCase $doorUserEntries,
string $doorId,
string $userId
): JsonResponse {
$this->authorizer->protect(Permissions::LOGS_READ);
$this->validate($this->request, [
'start' => 'required|date|before:end',
'end' => 'required|date|after:start',
]);
$presenter = new APIPresenter();
$doorUserEntries->getEntriesForDoorAndUserBetween(
$doorId,
$userId,
$userEntries->getEntries(
$this->request->input('user_id'),
$this->request->input('door_id'),
$this->request->input('start'),
$this->request->input('end'),
$presenter
);
return $this->respondWithData($presenter->getViewModel());
return $this->respondWithData($presenter->getViewModel([
'start' => $this->request->input('start'),
'end' => $this->request->input('end'),
'door_id' => $this->request->input('door_id'),
'user_id' => $this->request->input('user_id'),
]));
}
}
......@@ -20,6 +20,7 @@ use Source\Gateways\DoorGroup\DoorGroupRepositoryServiceProvider;
use Source\Gateways\GroupUser\GroupUserRepositoryServiceProvider;
use Source\Gateways\Overrides\OverridesRepositoryServiceProvider;
use Source\Gateways\Schedules\SchedulesRepositoryServiceProvider;
use Source\UseCases\Doors\GetDoors\GetDoorsUseCaseServiceProvider;
use Source\Gateways\Filesystem\FilesystemRepositoryServiceProvider;
use Source\UseCases\Groups\GetGroup\GetGroupUseCaseServiceProvider;
use Source\UseCases\Tokens\GetToken\GetTokenUseCaseServiceProvider;
......@@ -30,8 +31,8 @@ use Source\UseCases\Users\CreateUser\CreateUserUseCaseServiceProvider;
use Source\UseCases\Users\DeleteUser\DeleteUserUseCaseServiceProvider;
use Source\UseCases\Users\UpdateUser\UpdateUserUseCaseServiceProvider;
use Source\Gateways\DoorSchedule\DoorScheduleRepositoryServiceProvider;
use Source\UseCases\Doors\GetDoors\GetDoorsUseCaseServiceProvider;
use Source\UseCases\Users\GetAllUsers\GetAllUsersUseCaseServiceProvider;
use Source\UseCases\Entries\GetEntries\GetEntriesUseCaseServiceProvider;
use Source\Gateways\RecurrenceSet\RecurrenceSetRepositoryServiceProvider;
use Source\UseCases\Door\UpdateBinary\UpdateBinaryUseCaseServiceProvider;
use Source\UseCases\Groups\CreateGroup\CreateGroupUseCaseServiceProvider;
......@@ -60,15 +61,11 @@ use Source\UseCases\Overrides\OverrideUpdate\OverrideUpdateUseCaseServiceProvide
use Source\UseCases\Schedules\ScheduleCreate\ScheduleCreateUseCaseServiceProvider;
use Source\UseCases\Schedules\ScheduleUpdate\ScheduleUpdateUseCaseServiceProvider;
use Source\UseCases\Doors\GenerateDoorToken\GenerateDoorTokenUseCaseServiceProvider;
use Source\UseCases\Entries\GetEntriesForDoor\GetEntriesForDoorUseCaseServiceProvider;
use Source\UseCases\Entries\GetEntriesForUser\GetEntriesForUserUseCaseServiceProvider;
use Source\UseCases\Overrides\OverridesForDoor\OverridesForDoorUseCaseServiceProvider;
use Source\UseCases\DoorGroup\RemoveDoorFromGroup\RemoveDoorFromGroupUseCaseServiceProvider;
use Source\UseCases\GroupSchedule\SchedulesForGroup\SchedulesForGroupUseCaseServiceProvider;
use Source\UseCases\GroupUser\RemoveUserFromGroup\RemoveUserFromGroupUseCaseServiceProvider;
use Source\UseCases\Entries\GetEntriesForDateRange\GetEntriesForDateRangeUseCaseServiceProvider;
use Source\UseCases\Overrides\OverridesForDateRange\OverridesForDateRangeUseCaseServiceProvider;
use Source\UseCases\Entries\GetEntriesForDoorAndUser\GetEntriesForDoorAndUserUseCaseServiceProvider;
use Source\UseCases\GroupSchedule\ActiveSchedulesForGroup\ActiveSchedulesForGroupUseCaseServiceProvider;
use Source\UseCases\Door\Authenticate\AuthenticateUseCaseServiceProvider as DoorAuthenticateUseCaseServiceProvider;
use Source\UseCases\Users\Authenticate\AuthenticateUseCaseServiceProvider as UserAuthenticateUseCaseServiceProvider;
......@@ -161,10 +158,7 @@ class AppServiceProvider extends ServiceProvider
GetAttemptsUseCaseServiceProvider::class,
// Entries
GetEntriesForDoorUseCaseServiceProvider::class,
GetEntriesForUserUseCaseServiceProvider::class,
GetEntriesForDateRangeUseCaseServiceProvider::class,
GetEntriesForDoorAndUserUseCaseServiceProvider::class,
GetEntriesUseCaseServiceProvider::class,
// Schedule
ScheduleGetUseCaseServiceProvider::class,
......
......@@ -36,8 +36,6 @@ Route::group(['middleware' => 'auth:api'], static function () {
Route::patch('{userId}', [UsersController::class, 'update']);
Route::delete('{userId}', [UsersController::class, 'delete']);
Route::get('{userId}/entries', [EntriesController::class, 'userEntries']);
Route::get('{userId}/groups', [UsersController::class, 'getGroupsForUser']);
Route::post('{userId}/group/{groupId}', [UsersController::class, 'addUserToGroup']);
Route::delete('{userId}/group/{groupId}', [UsersController::class, 'removeUserFromGroup']);
......@@ -71,8 +69,6 @@ Route::group(['middleware' => 'auth:api'], static function () {
Route::post('/{doorId}/regenerate-token', [DoorsController::class, 'regenerateToken']);
Route::delete('{doorId}', [DoorsController::class, 'delete']);
Route::get('{doorId}/entries', [EntriesController::class, 'doorEntries']);
Route::get('{doorId}/groups', [DoorsController::class, 'getGroupsForDoor']);
Route::post('{doorId}/group/{groupId}', [DoorsController::class, 'addDoorToGroup']);
Route::delete('{doorId}/group/{groupId}', [DoorsController::class, 'removeDoorFromGroup']);
......@@ -103,7 +99,6 @@ Route::group(['middleware' => 'auth:api'], static function () {
'prefix' => 'entries',
], static function () {
Route::get('/', [EntriesController::class, 'index']);
Route::get('door/{doorId}/user/{userId}', [EntriesController::class, 'doorUserEntries']);
});
Route::group([
......
......@@ -106,9 +106,21 @@ class Entry
* @param \Carbon\Carbon $end
* @return bool
*/
public function isBetween(Carbon $begin, Carbon $end): bool
public function isBetween(?Carbon $begin, ?Carbon $end): bool
{
return $this->createdAt->isBetween($begin, $end);
if ($begin && $end) {
return $this->createdAt->isBetween($begin, $end);
}
if ($begin) {
return $this->createdAt->greaterThan($begin);
}
if ($end) {
return $this->createdAt->lessThan($end);
}
return true;
}
/**
......
......@@ -27,49 +27,6 @@ class DatabaseEntriesRepository implements EntriesRepository
);
}
/**
* @inheritDoc
*/
public function getForUserBetween(string $userId, Carbon $begin, Carbon $end): array
{
$entries = \App\Entry::query()->where('user_id', $this->castToInt($userId))
->whereBetween('created_at', [$begin, $end])
->get()->values()->all();
return array_map(static function (\App\Entry $entry) {
return self::toEntry($entry);
}, $entries);
}
/**
* @inheritDoc
*/
public function getForDoorBetween(string $doorId, Carbon $begin, Carbon $end): array
{
$entries = \App\Entry::query()->where('door_id', $this->castToInt($doorId))
->whereBetween('created_at', [$begin, $end])
->get()->values()->all();
return array_map(static function (\App\Entry $entry) {
return self::toEntry($entry);
}, $entries);
}
/**
* @inheritDoc
*/
public function getForUserAndDoorBetween(string $userId, string $doorId, Carbon $begin, Carbon $end): array
{
$entries = \App\Entry::query()->where('user_id', $this->castToInt($userId))
->where('door_id', $this->castToInt($doorId))
->whereBetween('created_at', [$begin, $end])
->get()->values()->all();
return array_map(static function (\App\Entry $entry) {
return self::toEntry($entry);
}, $entries);
}
/**
* @inheritDoc
*/
......@@ -90,12 +47,28 @@ class DatabaseEntriesRepository implements EntriesRepository
/**
* @inheritDoc
*/
public function getBetween(Carbon $begin, Carbon $end): array
public function get(?Carbon $begin = null, ?Carbon $end = null, ?string $userId = null, ?string $doorId = null): array
{
$entries = \App\Entry::query()->whereBetween('created_at', [$begin, $end])->get()->values()->all();
$query = \App\Entry::query()->orderByDesc('created_at');
if ($userId) {
$query->where('user_id', $this->castToInt($userId));
}
if ($doorId) {
$query->where('door_id', $this->castToInt($doorId));
}
if ($begin && $end) {
$query->whereBetween('created_at', [$begin, $end]);
} else if ($begin) {
$query->where('created_at', '>', $begin);
} else if ($end) {
$query->where('created_at', '<', $end);
}
return array_map(static function (\App\Entry $entry) {
return self::toEntry($entry);
}, $entries);
}, $query->get()->values()->all());
}
}
......@@ -17,42 +17,16 @@ interface EntriesRepository
public function add(Entry $entry): ?Entry;
/**
* Gets all entries for a user between the given date range.
*
* @param string $userId
* @param \Carbon\Carbon $begin
* @param \Carbon\Carbon $end
* @return \Source\Entities\Entry[]
*/
public function getForUserBetween(string $userId, Carbon $begin, Carbon $end): array;
/**
* Gets all entries into a door between a given date range.
*
* @param string $doorId
* @param \Carbon\Carbon $begin
* @param \Carbon\Carbon $end
* @return \Source\Entities\Entry[]
*/
public function getForDoorBetween(string $doorId, Carbon $begin, Carbon $end): array;
/**
* Gets all entries for a user and door between the given date range.
*
* @param string $userId
* @param string $doorId
* @param \Carbon\Carbon $begin
* @param \Carbon\Carbon $end
* @return \Source\Entities\Entry[]
*/
public function getForUserAndDoorBetween(string $userId, string $doorId, Carbon $begin, Carbon $end): array;
/**
* Gets all entries between given date range.
*
* @param \Carbon\Carbon $begin
* @param \Carbon\Carbon $end
* @return \Source\Entities\Entry[]
* @param \Carbon\Carbon|null $begin
* @param \Carbon\Carbon|null $end
* @param string|null $userId
* @param string|null $doorId
* @return array
*/
public function getBetween(Carbon $begin, Carbon $end): array;
public function get(
?Carbon $begin = null,
?Carbon $end = null,
?string $userId = null,
?string $doorId = null
): array;
}
......@@ -34,40 +34,20 @@ class InMemoryEntriesRepository implements EntriesRepository
/**
* @inheritDoc
*/
public function getForUserBetween(string $userId, Carbon $begin, Carbon $end): array
public function get(?Carbon $begin = null, ?Carbon $end = null, ?string $userId = null, ?string $doorId = null): array
{
return array_values(array_filter($this->entries, static function (Entry $entry) use ($userId, $begin, $end) {
return $entry->isBetween($begin, $end) && $entry->hasUserIdOf($userId);
}));
}
return array_values(array_filter($this->entries, static function (Entry $entry) use ($begin, $end, $userId, $doorId) {
$include = $entry->isBetween($begin, $end);
/**
* @inheritDoc
*/
public function getForDoorBetween(string $doorId, Carbon $begin, Carbon $end): array
{
return array_values(array_filter($this->entries, static function (Entry $entry) use ($doorId, $begin, $end) {
return $entry->isBetween($begin, $end) && $entry->hasDoorIdOf($doorId);
}));
}
if ($userId) {
$include = $include && $entry->hasUserIdOf($userId);
}
/**
* @inheritDoc
*/
public function getForUserAndDoorBetween(string $userId, string $doorId, Carbon $begin, Carbon $end): array
{
return array_values(array_filter($this->entries, static function (Entry $entry) use ($userId, $doorId, $begin, $end) {
return $entry->isBetween($begin, $end) && $entry->hasDoorIdOf($doorId) && $entry->hasUserIdOf($userId);
}));
}
if ($doorId) {
$include = $include && $entry->hasDoorIdOf($doorId);
}
/**
* @inheritDoc
*/
public function getBetween(Carbon $begin, Carbon $end): array
{
return array_values(array_filter($this->entries, static function (Entry $entry) use ($begin, $end) {
return $entry->isBetween($begin, $end);
return $include;
}));
}
}
......@@ -3,23 +3,26 @@
namespace Source\UseCases\Entries;
use Source\Entities\Entry;
use Source\Sanitize\Paginates;
use Source\UseCases\BasePresenter;
class APIPresenter extends BasePresenter implements Presenter
{
protected array $viewModel = [];
use Paginates;
protected array $entries = [];
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
$this->viewModel['entries'] = array_map(function (Entry $entry) {
$this->entries = array_map(function (Entry $entry) {
return $this->formatEntry($entry);
}, $responseModel->getEntries());
}
/** @inheritDoc */
public function getViewModel(): array
public function getViewModel(array $appends = []): array
{
return $this->viewModel;
return $this->paginate($this->entries, $appends);
}
}
<?php
namespace Source\UseCases\Entries\GetEntriesForDateRange;
namespace Source\UseCases\Entries\GetEntries;
use Carbon\Carbon;
use Source\Sanitize\CastsTo;
use Source\UseCases\Entries\Presenter;
use Source\Gateways\Users\UsersRepository;
use Source\UseCases\Entries\ResponseModel;
use Source\Exceptions\EntityNotFoundException;
use Source\Gateways\Entries\EntriesRepository;
class GetEntriesForDateRange implements GetEntriesForDateRangeUseCase
class GetEntries implements GetEntriesUseCase
{
use CastsTo;
/**
* @var \Source\Gateways\Entries\EntriesRepository
*/
protected EntriesRepository $entries;
/**
* @param \Source\Gateways\Entries\EntriesRepository $entries
*/
public function __construct(EntriesRepository $entries)
{
$this->entries = $entries;
......@@ -25,9 +27,14 @@ class GetEntriesForDateRange implements GetEntriesForDateRangeUseCase
/**
* @inheritDoc
*/
public function getForRange(string $start, string $end, Presenter $presenter): void
public function getEntries(?string $userId, ?string $doorId, ?string $begin, ?string $end, Presenter $presenter): void
{
$entries = $this->entries->getBetween(new Carbon($start), new Carbon($end));
$entries = $this->entries->get(
$this->liberalCastToCarbon($begin),
$this->liberalCastToCarbon($end),
$userId,
$doorId
);
$response = new ResponseModel();
......
<?php
namespace Source\UseCases\Entries\GetEntriesForUser;
namespace Source\UseCases\Entries\GetEntries;
use Source\UseCases\Entries\Presenter;
interface GetEntriesForUserUseCase
interface GetEntriesUseCase
{
/**
* Begin and end must be parsable by datetime
*
* @param string $userId
* @param string|null $doorId
* @param string $begin
* @param string $end
* @param \Source\UseCases\Entries\Presenter $presenter
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Exception
*/
public function getEntriesForUserBetween(string $userId, string $begin, string $end, Presenter $presenter): void;
public function getEntries(?string $userId, ?string $doorId, ?string $begin, ?string $end, Presenter $presenter): void;
}
<?php
namespace Source\UseCases\Entries\GetEntriesForUser;
namespace Source\UseCases\Entries\GetEntries;
use Illuminate\Support\ServiceProvider;
use Source\Gateways\Users\UsersRepository;
......@@ -12,7 +12,7 @@ use Illuminate\Contracts\Support\DeferrableProvider;
/**
* Service provider must be registered in AppServiceProvider
*/
class GetEntriesForUserUseCaseServiceProvider extends ServiceProvider implements DeferrableProvider
class GetEntriesUseCaseServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register any application services.
......@@ -21,8 +21,8 @@ class GetEntriesForUserUseCaseServiceProvider extends ServiceProvider implements
*/
public function register()
{
$this->app->bind(GetEntriesForUserUseCase::class, static function (Application $app) {
return new GetEntriesForUser($app->make(EntriesRepository::class), $app->make(UsersRepository::class));
$this->app->bind(GetEntriesUseCase::class, static function (Application $app) {
return new GetEntries($app->make(EntriesRepository::class));
});
}
......@@ -40,6 +40,6 @@ class GetEntriesForUserUseCaseServiceProvider extends ServiceProvider implements
*/
public function provides()
{
return [GetEntriesForUserUseCase::class];
return [GetEntriesUseCase::class];
}
}