Commit 399980c3 authored by Jacob Priddy's avatar Jacob Priddy 👌
Browse files

Refactor group schedule adding to be like the other group attachments

parent e547c48d
......@@ -57,7 +57,6 @@ class GroupScheduleController extends ApiController
$presenter = new GroupScheduleAttachApiPresenter();
$useCase->attach($presenter);
$this->setStatusCode($presenter->getReturnCode());
return $this->respondWithData($presenter->getViewModel());
}
......@@ -67,6 +66,7 @@ class GroupScheduleController extends ApiController
* This endpoint returns all groups that a specified schedule is attached to.
*
* @authenticated
* @paginated
* @urlParam scheduleId required The id of the schedule to get the groups for. Example: 1
*
* @param string $scheduleId
......
......@@ -195,6 +195,12 @@ class DoorsController extends Controller
return redirect(route('web.admin.doors.groups', ['doorId' => $doorId]))->with($presenter->getViewModel());
}
/**
* @param string $doorId
* @param string $groupId
* @param \Source\UseCases\DoorGroup\RemoveDoorFromGroup\RemoveDoorFromGroupUseCase $doorGroup
* @return \Illuminate\Http\RedirectResponse
*/
public function removeGroup(string $doorId, string $groupId, RemoveDoorFromGroupUseCase $doorGroup): RedirectResponse
{
$presenter = new RemoveDoorGroupPresenter();
......
......@@ -7,16 +7,24 @@ use Illuminate\View\View;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Validation\ValidationException;
use Source\UseCases\Groups\GetGroups\GetGroupsUseCase;
use Source\UseCases\Schedules\ScheduleGet\ScheduleGetUseCase;
use Source\UseCases\Schedules\SchedulesGet\SchedulesGetUseCase;
use Source\UseCases\Schedules\APIPresenter as SchedulePresenter;
use Source\UseCases\GroupSchedule\GetScheduleGroups\WebPresenter;
use Source\UseCases\Schedules\ScheduleCreate\ScheduleCreateUseCase;
use Source\UseCases\Schedules\ScheduleDelete\ScheduleDeleteUseCase;
use Source\UseCases\Schedules\ScheduleUpdate\ScheduleUpdateUseCase;
use Source\UseCases\Groups\GetGroups\WebPresenter as GroupsPresenter;
use Source\UseCases\GroupSchedule\RemoveSchedulesFromGroups\ApiPresenter as RemoveScheduleGroupPresenter;
use Source\UseCases\GroupSchedule\AddSchedulesToGroups\ApiPresenter as ScheduleGroupPresenter;
use Source\UseCases\Schedules\SchedulesGet\WebPresenter as SchedulesPresenter;
use Source\UseCases\GroupSchedule\GetScheduleGroups\GetScheduleGroupsUseCase;
use Source\UseCases\GroupSchedule\AddSchedulesToGroups\AddSchedulesToGroupsUseCase;
use Source\UseCases\Schedules\ScheduleCreate\WebPresenter as ScheduleCreatePresenter;
use Source\UseCases\Schedules\ScheduleDelete\ApiPresenter as DeleteSchedulePresenter;
use Source\UseCases\Schedules\ScheduleUpdate\WebPresenter as ScheduleUpdatePresenter;
use Source\UseCases\GroupSchedule\RemoveSchedulesFromGroups\RemoveSchedulesFromGroupsUseCase;
class SchedulesController extends Controller
{
......@@ -142,4 +150,65 @@ class SchedulesController extends Controller
{
return view('admin.entities.scheduleEvents', ['id' => $scheduleId]);
}
/**
* @param string $scheduleId
* @param \Source\UseCases\GroupSchedule\GetScheduleGroups\GetScheduleGroupsUseCase $scheduleGroups
* @param \Source\UseCases\Groups\GetGroups\GetGroupsUseCase $groups
* @return \Illuminate\View\View
*/
public function groups(string $scheduleId, GetScheduleGroupsUseCase $scheduleGroups, GetGroupsUseCase $groups): View
{
$groupsPresenter = new GroupsPresenter();
$groups->search(null, $groupsPresenter);
$presenter = new WebPresenter();
$scheduleGroups->getGroupsForSchedule($scheduleId, $presenter);
return view('layouts.admin.groups', array_merge(
$presenter->getViewModel(),
$groupsPresenter->all('allGroups'),
[
'resourceId' => $scheduleId,
'control' => 'detachScheduleGroup',
'resource' => 'schedule',
],
));
}
/**
* @param string $scheduleId
* @param \Source\UseCases\GroupSchedule\AddSchedulesToGroups\AddSchedulesToGroupsUseCase $scheduleGroup
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Validation\ValidationException
*/
public function addGroup(string $scheduleId, AddSchedulesToGroupsUseCase $scheduleGroup): RedirectResponse
{
$this->validate($this->request, [
'group_id' => 'required|string',
]);
$presenter = new ScheduleGroupPresenter();
$scheduleGroup->addSchedule($scheduleId);
$scheduleGroup->addGroup($this->request->input('group_id'));
$scheduleGroup->attach($presenter);
return redirect(route('web.admin.schedules.groups', ['scheduleId' => $scheduleId]))->with($presenter->getViewModel());
}
public function removeGroup(string $scheduleId, string $groupId, RemoveSchedulesFromGroupsUseCase $scheduleGroup): RedirectResponse
{
$presenter = new RemoveScheduleGroupPresenter();
$scheduleGroup->addSchedule($scheduleId);
$scheduleGroup->addGroup($groupId);
$scheduleGroup->detach($presenter);
return redirect(route('web.admin.schedules.groups', ['scheduleId' => $scheduleId]))->with($presenter->getViewModel());
}
}
<form action="{{ route('web.admin.schedules.removeGroup', ['scheduleId' => $resourceId, 'groupId' => $id]) }}" method="POST">
@csrf
@method('DELETE')
<button class="btn btn-danger" type="submit" data-toggle="tooltip" title="Detach schedule from group.">
<i class="fas fa-trash"></i>
</button>
</form>
<a class="btn btn-secondary" href="{{ route('web.admin.schedules.groups', ['scheduleId' => $id]) }}" data-toggle="tooltip" title="View groups for this schedule.">
<i class="fas fa-object-group"></i>
</a>
<a class="btn btn-dark" href="{{ route('web.admin.schedules.events', ['scheduleId' => $id]) }}" data-toggle="tooltip" title="View Schedule Events">
<i class="fas fa-calendar-alt"></i>
</a>
......
......@@ -128,9 +128,9 @@ Route::name('web.')->middleware(['auth:api'])->group(static function () {
Route::delete('/{scheduleId}', [SchedulesController::class, 'destroy'])->name('destroy');
Route::get('/{scheduleId}/events', [SchedulesController::class, 'events'])->name('events');
// Route::get('/{scheduleId}/groups', [SchedulesController::class, 'groups'])->name('groups');
// Route::post('/{scheduleId}/groups', [SchedulesController::class, 'addGroup'])->name('addGroup');
// Route::delete('/{scheduleId}/group/{groupId}', [SchedulesController::class, 'removeGroup'])->name('removeGroup');
Route::get('/{scheduleId}/groups', [SchedulesController::class, 'groups'])->name('groups');
Route::post('/{scheduleId}/groups', [SchedulesController::class, 'addGroup'])->name('addGroup');
Route::delete('/{scheduleId}/group/{groupId}', [SchedulesController::class, 'removeGroup'])->name('removeGroup');
});
Route::name('overrides.')
......
......@@ -7,9 +7,6 @@ use App\Group;
use App\Schedule;
use Carbon\Carbon;
use Source\Sanitize\CastsTo;
use Illuminate\Support\Facades\Log;
use Source\Gateways\PostgresSQLCodes;
use Illuminate\Database\QueryException;
use Illuminate\Database\ConnectionInterface;
use Source\Exceptions\EntityNotFoundException;
use Source\Gateways\Groups\DatabaseGroupsRepository;
......@@ -69,37 +66,43 @@ class DatabaseGroupScheduleRepository implements GroupScheduleRepository
/**
* @inheritDoc
*/
public function addSchedulesToGroups(array $scheduleIds, array $groupIds): void
public function addScheduleToGroup(string $scheduleId, string $groupId): void
{
$groups = Group::query()->findMany(array_map(fn (string $id) => self::castToInt($id), $groupIds))->values()->all();
/** @var Group $group */
foreach ($groups as $group) {
try {
$group->schedules()->attach($scheduleIds);
} catch (QueryException $e) {
if ($e->getCode() === PostgresSQLCodes::FOREIGN_KEY_VIOLATION) {
throw new EntityNotFoundException('Unable to add schedules to groups. One of the schedules does not exist.');
}
Log::error('Unable to add schedules to group ' . $e);
/** @var Schedule|null $schedule */
$schedule = Schedule::query()->find(self::castToInt($scheduleId));
/** @var Group|null $group */
$group = Group::query()->find(self::castToInt($groupId));
throw $e;
if (!$schedule) {
throw new EntityNotFoundException('Schedule not found.');
}
if (!$group) {
throw new EntityNotFoundException('Group not found.');
}
$schedule->groups()->attach($groupId);
}
/**
* @inheritDoc
*/
public function removeSchedulesFromGroups(array $scheduleIds, array $groupIds): void
public function removeSchedulesFromGroups(string $scheduleId, string $groupId): void
{
$groups = Group::query()->findMany(array_map(fn (string $id) => self::castToInt($id), $groupIds))->values()->all();
/** @var Schedule|null $schedule */
$schedule = Schedule::query()->find(self::castToInt($scheduleId));
/** @var Group|null $group */
$group = Group::query()->find(self::castToInt($groupId));
/** @var Group $group */
foreach ($groups as $group) {
$group->schedules()->detach($scheduleIds);
if (!$schedule) {
throw new EntityNotFoundException('Schedule not found.');
}
if (!$group) {
throw new EntityNotFoundException('Group not found.');
}
$schedule->groups()->detach($groupId);
}
/**
......
......@@ -23,19 +23,20 @@ interface GroupScheduleRepository
/**
* Attaches all specified schedules to all specified groups
*
* @param string[] $scheduleIds
* @param string[] $groupIds
* @param string $scheduleId
* @param string $groupId
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function addSchedulesToGroups(array $scheduleIds, array $groupIds): void;
public function addScheduleToGroup(string $scheduleId, string $groupId): void;
/**
* Makes sure none of the specified schedules and groups are attached.
*
* @param string[] $scheduleIds
* @param string[] $groupIds
* @param string $scheduleId
* @param string $groupId
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function removeSchedulesFromGroups(array $scheduleIds, array $groupIds): void;
public function removeSchedulesFromGroups(string $scheduleId, string $groupId): void;
/**
* Get all currently active schedule sets
......
......@@ -6,6 +6,7 @@ namespace Source\Gateways\GroupSchedule;
use Carbon\Carbon;
use Source\Entities\Schedule;
use Source\Gateways\Groups\GroupsRepository;
use Source\Exceptions\EntityNotFoundException;
use Source\Gateways\Schedules\SchedulesRepository;
class InMemoryGroupScheduleRepository implements GroupScheduleRepository
......@@ -23,7 +24,7 @@ class InMemoryGroupScheduleRepository implements GroupScheduleRepository
/**
* array of groupIds mapped to schedule Ids
*
* @var string[]
* @var array
*/
protected array $groupMap = [];
......@@ -72,29 +73,34 @@ class InMemoryGroupScheduleRepository implements GroupScheduleRepository
/**
* @inheritDoc
*/
public function addSchedulesToGroups(array $scheduleIds, array $groupIds): void
public function addScheduleToGroup(string $scheduleId, string $groupId): void
{
foreach ($groupIds as $groupId) {
if (isset($this->groupMap[$groupId])) {
$this->groupMap[$groupId] = array_merge($this->groupMap[$groupId], $scheduleIds);
$this->groupMap[$groupId][] = $scheduleId;
return;
}
$this->groupMap[$groupId] = $scheduleIds;
}
$this->groupMap[$groupId] = [$scheduleId];
}
/**
* @inheritDoc
*/
public function removeSchedulesFromGroups(array $scheduleIds, array $groupIds): void
public function removeSchedulesFromGroups(string $scheduleId, string $groupId): void
{
foreach ($groupIds as $groupId) {
if (isset($this->groupMap[$groupId])) {
$this->groupMap[$groupId] = array_diff($this->groupMap[$groupId], $scheduleIds);
$count = count($this->groupMap[$groupId]);
$this->groupMap[$groupId] = array_values(array_filter(
$this->groupMap[$groupId],
fn (string $searchSchedule): bool => $searchSchedule !== $scheduleId
));
if (count($this->groupMap[$groupId]) === $count) {
throw new EntityNotFoundException('Schedule not found.');
}
return;
}
throw new EntityNotFoundException('Group not found.');
}
/**
......
......@@ -13,7 +13,7 @@ class LocalGroupScheduleRepository extends InMemoryGroupScheduleRepository
{
parent::__construct($groups, $schedules);
$this->addSchedulesToGroups([1], [LocalGroupsRepository::getEngineeringLabAccessGroup()->getId()]);
$this->addSchedulesToGroups([2], [LocalGroupsRepository::getComputerScienceMajorGroup()->getId()]);
$this->addScheduleToGroup(1, LocalGroupsRepository::getEngineeringLabAccessGroup()->getId());
$this->addScheduleToGroup(2, LocalGroupsRepository::getComputerScienceMajorGroup()->getId());
}
}
......@@ -49,11 +49,16 @@ class AddSchedulesToGroups implements AddSchedulesToGroupsUseCase
public function attach(Presenter $presenter): void
{
$response = new ResponseModel();
foreach ($this->groupIds as $groupId) {
foreach ($this->scheduleIds as $scheduleId) {
try {
$this->groupScheduleRepository->addSchedulesToGroups($this->scheduleIds, $this->groupIds);
$response->setMessage('Successfully attached the groups to the schedules.');
$this->groupScheduleRepository->addScheduleToGroup($scheduleId, $groupId);
$response->addMessage("Attached schedule $scheduleId to group $groupId");
} catch (EntityNotFoundException $e) {
$response->setError($e->getMessage());
$response->addMessage("Failed to attach schedule $scheduleId to group $groupId. " . $e->getMessage());
}
}
}
$presenter->present($response);
......
......@@ -8,25 +8,10 @@ class ApiPresenter extends BasePresenter implements Presenter
{
protected array $viewModel = [];
protected int $status = 200;
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
if ($responseModel->hasError()) {
$this->status = 422;
$this->viewModel['error'] = $responseModel->getError();
} else {
$this->viewModel['message'] = $responseModel->getMessage();
}
}
/**
* @return int
*/
public function getReturnCode(): int
{
return $this->status;
$this->viewModel['messages'] = $responseModel->getMessages();
}
/** @inheritDoc */
......
......@@ -2,27 +2,9 @@
namespace Source\UseCases\GroupSchedule\AddSchedulesToGroups;
use Source\UseCases\HasErrors;
use Source\UseCases\HasMessages;
class ResponseModel
{
use HasErrors;
protected string $message = '';
/**
* @param string $message
*/
public function setMessage(string $message): void
{
$this->message = $message;
}
/**
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
use HasMessages;
}
......@@ -3,21 +3,24 @@
namespace Source\UseCases\GroupSchedule\GetScheduleGroups;
use Source\Entities\Group;
use Source\Sanitize\Paginates;
use Source\UseCases\BasePresenter;
class ApiPresenter extends BasePresenter implements Presenter
{
protected array $viewModel = [];
use Paginates;
protected array $groups = [];
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
$this->viewModel['groups'] = array_map(fn (Group $group): array => self::formatGroup($group), $responseModel->getGroups());
$this->groups = array_map(fn (Group $group): array => self::formatGroup($group), $responseModel->getGroups());
}
/** @inheritDoc */
public function getViewModel(): array
{
return $this->viewModel;
return $this->paginate($this->groups);
}
}
<?php
namespace Source\UseCases\GroupSchedule\GetScheduleGroups;
use Source\Entities\Group;
use Source\Sanitize\Paginates;
use Source\UseCases\BasePresenter;
class WebPresenter extends BasePresenter implements Presenter
{
use Paginates;
protected array $groups = [];
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
$this->groups = array_map(static function (Group $group): array {
return [
'id' => $group->getId(),
'title' => $group->getTitle(),
'description' => $group->getDescription(),
];
}, $responseModel->getGroups());
}
/** @inheritDoc */
public function getViewModel(): array
{
return [
'groups' => $this->webPaginate($this->groups),
'headers' => [
'ID' => 'id',
'Title' => 'title',
'Description' => 'description',
],
];
}
}
......@@ -11,7 +11,7 @@ class ApiPresenter extends BasePresenter implements Presenter
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
$this->viewModel['message'] = $responseModel->getMessage();
$this->viewModel['messages'] = $responseModel->getMessages();
}
/** @inheritDoc */
......
......@@ -2,6 +2,7 @@
namespace Source\UseCases\GroupSchedule\RemoveSchedulesFromGroups;
use Source\Exceptions\EntityNotFoundException;
use Source\Gateways\GroupSchedule\GroupScheduleRepository;
class RemoveSchedulesFromGroups implements RemoveSchedulesFromGroupsUseCase
......@@ -47,10 +48,19 @@ class RemoveSchedulesFromGroups implements RemoveSchedulesFromGroupsUseCase
*/
public function detach(Presenter $presenter): void
{
$this->groupScheduleRepository->removeSchedulesFromGroups($this->schedules, $this->groups);
$response = new ResponseModel();
$response->setMessage('Successfully removed the schedules from the groups.');
foreach ($this->groups as $groupId) {
foreach ($this->schedules as $scheduleId) {
try {
$this->groupScheduleRepository->removeSchedulesFromGroups($scheduleId, $groupId);
$response->addMessage("Removed schedule $scheduleId from group $groupId.");
} catch (EntityNotFoundException $e) {
$response->addMessage("Failed to remove schedule $scheduleId from group $groupId. " . $e->getMessage());
}
}
}
$presenter->present($response);
}
}
......@@ -2,23 +2,9 @@
namespace Source\UseCases\GroupSchedule\RemoveSchedulesFromGroups;
use Source\UseCases\HasMessages;
class ResponseModel
{
public string $message = '';
/**
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
/**
* @param string $message
*/
public function setMessage(string $message): void
{
$this->message = $message;
}
use HasMessages;
}
......@@ -125,8 +125,9 @@ class DoorScheduleDatabaseTest extends DatabaseTestCase
$s2 = $this->schedules->create(new Schedule(0, Schedule::TYPE_OPEN_MODE, '', 1235, '', Carbon::now()->subYear(), Carbon::now()->addYear()));
$s3 = $this->schedules->create(new Schedule(0, Schedule::TYPE_USER_ACCESS, '', 1235, '', Carbon::now()->subDay()));
$this->groupSchedule->addSchedulesToGroups([$s1->getId(), $s2->getId()], [$g1->getId()]);
$this->groupSchedule->addSchedulesToGroups([$s3->getId()], [$g2->getId()]);
$this->groupSchedule->addScheduleToGroup($s1->getId(), $g1->getId());
$this->groupSchedule->addScheduleToGroup($s2->getId(), $g1->getId());
$this->groupSchedule->addScheduleToGroup($s3->getId(), $g2->getId());
$schedules = $this->db->getActiveSchedulesForDoor($d1->getId());
......@@ -237,8 +238,12 @@ class DoorScheduleDatabaseTest extends DatabaseTestCase
// Different type, don't see this one
$s6 = $this->schedules->create(new Schedule(0, Schedule::TYPE_USER_ACCESS, '', 1235, '', Carbon::now()->subSecond(), Carbon::now()->addSecond()));
$this->groupSchedule->addSchedulesToGroups([$s1->getId(), $s2->getId(), $s3->getId(), $s4->getId(), $s6->getId()], [$g1->getId()]);
$this->groupSchedule->addSchedulesToGroups([$s5->getId()], [$g2->getId()]);
$this->groupSchedule->addScheduleToGroup($s1->getId(), $g1->getId());
$this->groupSchedule->addScheduleToGroup($s2->getId(), $g1->getId());
$this->groupSchedule->addScheduleToGroup($s3->getId(), $g1->getId());
$this->groupSchedule->addScheduleToGroup($s4->getId(), $g1->getId());
$this->groupSchedule->addScheduleToGroup($s6->getId(), $g1->getId());
$this->groupSchedule->addScheduleToGroup($s5->getId(), $g2->getId());
$schedules = $this->db->getSchedulesForDoorBetween($d1->getId(), Carbon::now()->subMinute(), Carbon::now()->addMinute());
......@@ -304,8 +309,9 @@ class DoorScheduleDatabaseTest extends DatabaseTestCase
$s2 = $this->schedules->create(new Schedule(0, Schedule::TYPE_USER_ACCESS, '', 1235, '', Carbon::now()->subYear(), Carbon::now()->addYear()));
$s3 = $this->schedules->create(new Schedule(0, Schedule::TYPE_USER_ACCESS, '', 1235, '', Carbon::now()->subSecond(), Carbon::now()->addSecond()));