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 ...@@ -57,7 +57,6 @@ class GroupScheduleController extends ApiController
$presenter = new GroupScheduleAttachApiPresenter(); $presenter = new GroupScheduleAttachApiPresenter();
$useCase->attach($presenter); $useCase->attach($presenter);
$this->setStatusCode($presenter->getReturnCode());
return $this->respondWithData($presenter->getViewModel()); return $this->respondWithData($presenter->getViewModel());
} }
...@@ -67,6 +66,7 @@ class GroupScheduleController extends ApiController ...@@ -67,6 +66,7 @@ class GroupScheduleController extends ApiController
* This endpoint returns all groups that a specified schedule is attached to. * This endpoint returns all groups that a specified schedule is attached to.
* *
* @authenticated * @authenticated
* @paginated
* @urlParam scheduleId required The id of the schedule to get the groups for. Example: 1 * @urlParam scheduleId required The id of the schedule to get the groups for. Example: 1
* *
* @param string $scheduleId * @param string $scheduleId
......
...@@ -195,6 +195,12 @@ class DoorsController extends Controller ...@@ -195,6 +195,12 @@ class DoorsController extends Controller
return redirect(route('web.admin.doors.groups', ['doorId' => $doorId]))->with($presenter->getViewModel()); 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 public function removeGroup(string $doorId, string $groupId, RemoveDoorFromGroupUseCase $doorGroup): RedirectResponse
{ {
$presenter = new RemoveDoorGroupPresenter(); $presenter = new RemoveDoorGroupPresenter();
......
...@@ -7,16 +7,24 @@ use Illuminate\View\View; ...@@ -7,16 +7,24 @@ use Illuminate\View\View;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use Source\UseCases\Groups\GetGroups\GetGroupsUseCase;
use Source\UseCases\Schedules\ScheduleGet\ScheduleGetUseCase; use Source\UseCases\Schedules\ScheduleGet\ScheduleGetUseCase;
use Source\UseCases\Schedules\SchedulesGet\SchedulesGetUseCase; use Source\UseCases\Schedules\SchedulesGet\SchedulesGetUseCase;
use Source\UseCases\Schedules\APIPresenter as SchedulePresenter; use Source\UseCases\Schedules\APIPresenter as SchedulePresenter;
use Source\UseCases\GroupSchedule\GetScheduleGroups\WebPresenter;
use Source\UseCases\Schedules\ScheduleCreate\ScheduleCreateUseCase; use Source\UseCases\Schedules\ScheduleCreate\ScheduleCreateUseCase;
use Source\UseCases\Schedules\ScheduleDelete\ScheduleDeleteUseCase; use Source\UseCases\Schedules\ScheduleDelete\ScheduleDeleteUseCase;
use Source\UseCases\Schedules\ScheduleUpdate\ScheduleUpdateUseCase; 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\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\ScheduleCreate\WebPresenter as ScheduleCreatePresenter;
use Source\UseCases\Schedules\ScheduleDelete\ApiPresenter as DeleteSchedulePresenter; use Source\UseCases\Schedules\ScheduleDelete\ApiPresenter as DeleteSchedulePresenter;
use Source\UseCases\Schedules\ScheduleUpdate\WebPresenter as ScheduleUpdatePresenter; use Source\UseCases\Schedules\ScheduleUpdate\WebPresenter as ScheduleUpdatePresenter;
use Source\UseCases\GroupSchedule\RemoveSchedulesFromGroups\RemoveSchedulesFromGroupsUseCase;
class SchedulesController extends Controller class SchedulesController extends Controller
{ {
...@@ -142,4 +150,65 @@ class SchedulesController extends Controller ...@@ -142,4 +150,65 @@ class SchedulesController extends Controller
{ {
return view('admin.entities.scheduleEvents', ['id' => $scheduleId]); 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"> <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> <i class="fas fa-calendar-alt"></i>
</a> </a>
......
...@@ -128,9 +128,9 @@ Route::name('web.')->middleware(['auth:api'])->group(static function () { ...@@ -128,9 +128,9 @@ Route::name('web.')->middleware(['auth:api'])->group(static function () {
Route::delete('/{scheduleId}', [SchedulesController::class, 'destroy'])->name('destroy'); Route::delete('/{scheduleId}', [SchedulesController::class, 'destroy'])->name('destroy');
Route::get('/{scheduleId}/events', [SchedulesController::class, 'events'])->name('events'); Route::get('/{scheduleId}/events', [SchedulesController::class, 'events'])->name('events');
// Route::get('/{scheduleId}/groups', [SchedulesController::class, 'groups'])->name('groups'); Route::get('/{scheduleId}/groups', [SchedulesController::class, 'groups'])->name('groups');
// Route::post('/{scheduleId}/groups', [SchedulesController::class, 'addGroup'])->name('addGroup'); Route::post('/{scheduleId}/groups', [SchedulesController::class, 'addGroup'])->name('addGroup');
// Route::delete('/{scheduleId}/group/{groupId}', [SchedulesController::class, 'removeGroup'])->name('removeGroup'); Route::delete('/{scheduleId}/group/{groupId}', [SchedulesController::class, 'removeGroup'])->name('removeGroup');
}); });
Route::name('overrides.') Route::name('overrides.')
......
...@@ -7,9 +7,6 @@ use App\Group; ...@@ -7,9 +7,6 @@ use App\Group;
use App\Schedule; use App\Schedule;
use Carbon\Carbon; use Carbon\Carbon;
use Source\Sanitize\CastsTo; use Source\Sanitize\CastsTo;
use Illuminate\Support\Facades\Log;
use Source\Gateways\PostgresSQLCodes;
use Illuminate\Database\QueryException;
use Illuminate\Database\ConnectionInterface; use Illuminate\Database\ConnectionInterface;
use Source\Exceptions\EntityNotFoundException; use Source\Exceptions\EntityNotFoundException;
use Source\Gateways\Groups\DatabaseGroupsRepository; use Source\Gateways\Groups\DatabaseGroupsRepository;
...@@ -69,37 +66,43 @@ class DatabaseGroupScheduleRepository implements GroupScheduleRepository ...@@ -69,37 +66,43 @@ class DatabaseGroupScheduleRepository implements GroupScheduleRepository
/** /**
* @inheritDoc * @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 Schedule|null $schedule */
$schedule = Schedule::query()->find(self::castToInt($scheduleId));
/** @var Group $group */ /** @var Group|null $group */
foreach ($groups as $group) { $group = Group::query()->find(self::castToInt($groupId));
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); if (!$schedule) {
throw new EntityNotFoundException('Schedule not found.');
}
throw $e; if (!$group) {
} throw new EntityNotFoundException('Group not found.');
} }
$schedule->groups()->attach($groupId);
} }
/** /**
* @inheritDoc * @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 */ if (!$schedule) {
foreach ($groups as $group) { throw new EntityNotFoundException('Schedule not found.');
$group->schedules()->detach($scheduleIds);
} }
if (!$group) {
throw new EntityNotFoundException('Group not found.');
}
$schedule->groups()->detach($groupId);
} }
/** /**
......
...@@ -23,19 +23,20 @@ interface GroupScheduleRepository ...@@ -23,19 +23,20 @@ interface GroupScheduleRepository
/** /**
* Attaches all specified schedules to all specified groups * Attaches all specified schedules to all specified groups
* *
* @param string[] $scheduleIds * @param string $scheduleId
* @param string[] $groupIds * @param string $groupId
* @throws \Source\Exceptions\EntityNotFoundException * @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. * Makes sure none of the specified schedules and groups are attached.
* *
* @param string[] $scheduleIds * @param string $scheduleId
* @param string[] $groupIds * @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 * Get all currently active schedule sets
......
...@@ -6,6 +6,7 @@ namespace Source\Gateways\GroupSchedule; ...@@ -6,6 +6,7 @@ namespace Source\Gateways\GroupSchedule;
use Carbon\Carbon; use Carbon\Carbon;
use Source\Entities\Schedule; use Source\Entities\Schedule;
use Source\Gateways\Groups\GroupsRepository; use Source\Gateways\Groups\GroupsRepository;
use Source\Exceptions\EntityNotFoundException;
use Source\Gateways\Schedules\SchedulesRepository; use Source\Gateways\Schedules\SchedulesRepository;
class InMemoryGroupScheduleRepository implements GroupScheduleRepository class InMemoryGroupScheduleRepository implements GroupScheduleRepository
...@@ -23,7 +24,7 @@ class InMemoryGroupScheduleRepository implements GroupScheduleRepository ...@@ -23,7 +24,7 @@ class InMemoryGroupScheduleRepository implements GroupScheduleRepository
/** /**
* array of groupIds mapped to schedule Ids * array of groupIds mapped to schedule Ids
* *
* @var string[] * @var array
*/ */
protected array $groupMap = []; protected array $groupMap = [];
...@@ -72,29 +73,34 @@ class InMemoryGroupScheduleRepository implements GroupScheduleRepository ...@@ -72,29 +73,34 @@ class InMemoryGroupScheduleRepository implements GroupScheduleRepository
/** /**
* @inheritDoc * @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])) {
if (isset($this->groupMap[$groupId])) { $this->groupMap[$groupId][] = $scheduleId;
$this->groupMap[$groupId] = array_merge($this->groupMap[$groupId], $scheduleIds);
return; return;
}
$this->groupMap[$groupId] = $scheduleIds;
} }
$this->groupMap[$groupId] = [$scheduleId];
} }
/** /**
* @inheritDoc * @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])) {
if (isset($this->groupMap[$groupId])) { $count = count($this->groupMap[$groupId]);
$this->groupMap[$groupId] = array_diff($this->groupMap[$groupId], $scheduleIds); $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 ...@@ -13,7 +13,7 @@ class LocalGroupScheduleRepository extends InMemoryGroupScheduleRepository
{ {
parent::__construct($groups, $schedules); parent::__construct($groups, $schedules);
$this->addSchedulesToGroups([1], [LocalGroupsRepository::getEngineeringLabAccessGroup()->getId()]); $this->addScheduleToGroup(1, LocalGroupsRepository::getEngineeringLabAccessGroup()->getId());
$this->addSchedulesToGroups([2], [LocalGroupsRepository::getComputerScienceMajorGroup()->getId()]); $this->addScheduleToGroup(2, LocalGroupsRepository::getComputerScienceMajorGroup()->getId());
} }
} }
...@@ -49,11 +49,16 @@ class AddSchedulesToGroups implements AddSchedulesToGroupsUseCase ...@@ -49,11 +49,16 @@ class AddSchedulesToGroups implements AddSchedulesToGroupsUseCase
public function attach(Presenter $presenter): void public function attach(Presenter $presenter): void
{ {
$response = new ResponseModel(); $response = new ResponseModel();
try {
$this->groupScheduleRepository->addSchedulesToGroups($this->scheduleIds, $this->groupIds); foreach ($this->groupIds as $groupId) {
$response->setMessage('Successfully attached the groups to the schedules.'); foreach ($this->scheduleIds as $scheduleId) {
} catch (EntityNotFoundException $e) { try {
$response->setError($e->getMessage()); $this->groupScheduleRepository->addScheduleToGroup($scheduleId, $groupId);
$response->addMessage("Attached schedule $scheduleId to group $groupId");
} catch (EntityNotFoundException $e) {
$response->addMessage("Failed to attach schedule $scheduleId to group $groupId. " . $e->getMessage());
}
}
} }
$presenter->present($response); $presenter->present($response);
......
...@@ -8,25 +8,10 @@ class ApiPresenter extends BasePresenter implements Presenter ...@@ -8,25 +8,10 @@ class ApiPresenter extends BasePresenter implements Presenter
{ {
protected array $viewModel = []; protected array $viewModel = [];
protected int $status = 200;
/** @inheritDoc */ /** @inheritDoc */
public function present(ResponseModel $responseModel): void public function present(ResponseModel $responseModel): void
{ {
if ($responseModel->hasError()) { $this->viewModel['messages'] = $responseModel->getMessages();
$this->status = 422;
$this->viewModel['error'] = $responseModel->getError();
} else {
$this->viewModel['message'] = $responseModel->getMessage();
}
}
/**
* @return int
*/
public function getReturnCode(): int
{
return $this->status;
} }
/** @inheritDoc */ /** @inheritDoc */
......
...@@ -2,27 +2,9 @@ ...@@ -2,27 +2,9 @@
namespace Source\UseCases\GroupSchedule\AddSchedulesToGroups; namespace Source\UseCases\GroupSchedule\AddSchedulesToGroups;
use Source\UseCases\HasErrors; use Source\UseCases\HasMessages;
class ResponseModel class ResponseModel
{ {
use HasErrors; use HasMessages;
protected string $message = '';
/**
* @param string $message
*/
public function setMessage(string $message): void
{
$this->message = $message;
}
/**
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
} }
...@@ -3,21 +3,24 @@ ...@@ -3,21 +3,24 @@
namespace Source\UseCases\GroupSchedule\GetScheduleGroups; namespace Source\UseCases\GroupSchedule\GetScheduleGroups;
use Source\Entities\Group; use Source\Entities\Group;
use Source\Sanitize\Paginates;
use Source\UseCases\BasePresenter; use Source\UseCases\BasePresenter;
class ApiPresenter extends BasePresenter implements Presenter class ApiPresenter extends BasePresenter implements Presenter
{ {
protected array $viewModel = []; use Paginates;
protected array $groups = [];
/** @inheritDoc */ /** @inheritDoc */
public function present(ResponseModel $responseModel): void 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 */ /** @inheritDoc */
public function getViewModel(): array 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
{