add dated assignment endpoint
This commit is contained in:
parent
6afe7864be
commit
483ca6fc1d
9 changed files with 252 additions and 8 deletions
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers;
|
|||
|
||||
use App\Exceptions\BadRequestException;
|
||||
use App\Exceptions\NotFoundException;
|
||||
use App\Schedule\AssignmentForDate;
|
||||
use App\Schedule\Schedule;
|
||||
use App\Schedule\ScheduleAssignment;
|
||||
use App\Schedule\UseCases\CreateSchedule\CreateSchedule;
|
||||
|
|
@ -11,6 +12,8 @@ use App\Schedule\UseCases\CreateSchedule\CreateScheduleRequest;
|
|||
use App\Schedule\UseCases\GetSchedule\GetSchedule;
|
||||
use App\Schedule\UseCases\GetSchedule\GetScheduleRequest;
|
||||
use App\Schedule\UseCases\ListSchedules\ListSchedules;
|
||||
use App\Schedule\UseCases\ListAssignmentsForDate\ListAssignmentsForDate;
|
||||
use App\Schedule\UseCases\ListAssignmentsForDate\ListAssignmentsForDateRequest;
|
||||
use App\Shared\Http\RequestInput;
|
||||
use App\User\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
|
@ -22,6 +25,7 @@ class ScheduleController extends Controller
|
|||
private CreateSchedule $createSchedule,
|
||||
private ListSchedules $listSchedules,
|
||||
private GetSchedule $getSchedule,
|
||||
private ListAssignmentsForDate $listAssignmentsForDate,
|
||||
) {}
|
||||
|
||||
public function store(Request $request): JsonResponse
|
||||
|
|
@ -90,6 +94,36 @@ class ScheduleController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
public function assignments(Request $request): JsonResponse
|
||||
{
|
||||
$input = new RequestInput($request);
|
||||
$date = $input->string('date');
|
||||
|
||||
try {
|
||||
$assignments = $this->listAssignmentsForDate->execute(
|
||||
new ListAssignmentsForDateRequest(
|
||||
user: $this->user($request),
|
||||
date: $date,
|
||||
),
|
||||
);
|
||||
} catch (BadRequestException $exception) {
|
||||
return new JsonResponse(
|
||||
['error' => $exception->getMessage()],
|
||||
400,
|
||||
);
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'date' => $date,
|
||||
'assignments' => array_map(
|
||||
function (AssignmentForDate $assignment): array {
|
||||
return $this->assignmentForDatePayload($assignment);
|
||||
},
|
||||
$assignments,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
|
|
@ -165,6 +199,30 @@ class ScheduleController extends Controller
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function assignmentForDatePayload(
|
||||
AssignmentForDate $assignmentForDate,
|
||||
): array {
|
||||
$assignment = $assignmentForDate->getAssignment();
|
||||
|
||||
return [
|
||||
'id' => $assignment->getId(),
|
||||
'schedule' => [
|
||||
'id' => $assignmentForDate->getScheduleId(),
|
||||
'set' => [
|
||||
'name' => $assignmentForDate->getSetName(),
|
||||
],
|
||||
],
|
||||
'element' => [
|
||||
'name' => $assignment->getName(),
|
||||
'kind' => $assignment->getKind(),
|
||||
'path' => $assignment->getPath(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function user(Request $request): User
|
||||
{
|
||||
/** @var User $user */
|
||||
|
|
|
|||
27
backend/app/Schedule/AssignmentForDate.php
Normal file
27
backend/app/Schedule/AssignmentForDate.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Schedule;
|
||||
|
||||
final readonly class AssignmentForDate
|
||||
{
|
||||
public function __construct(
|
||||
private int $scheduleId,
|
||||
private string $setName,
|
||||
private ScheduleAssignment $assignment,
|
||||
) {}
|
||||
|
||||
public function getScheduleId(): int
|
||||
{
|
||||
return $this->scheduleId;
|
||||
}
|
||||
|
||||
public function getSetName(): string
|
||||
{
|
||||
return $this->setName;
|
||||
}
|
||||
|
||||
public function getAssignment(): ScheduleAssignment
|
||||
{
|
||||
return $this->assignment;
|
||||
}
|
||||
}
|
||||
|
|
@ -61,6 +61,40 @@ class EloquentScheduleRepository implements ScheduleRepository
|
|||
return $schedules;
|
||||
}
|
||||
|
||||
public function findAssignmentsForUserOnDate(
|
||||
User $user,
|
||||
DateTimeImmutable $date,
|
||||
): array {
|
||||
$assignmentModels = ScheduleAssignmentModel::query()
|
||||
->select('schedule_assignments.*')
|
||||
->join(
|
||||
'schedules',
|
||||
'schedules.id',
|
||||
'=',
|
||||
'schedule_assignments.schedule_id',
|
||||
)
|
||||
->where('schedules.user_id', $user->getId())
|
||||
->where('schedule_assignments.scheduled_date', $date->format(
|
||||
'Y-m-d',
|
||||
))
|
||||
->with('schedule')
|
||||
->orderByDesc('schedules.id')
|
||||
->orderBy('schedule_assignments.position')
|
||||
->orderBy('schedule_assignments.id')
|
||||
->get();
|
||||
$assignments = [];
|
||||
|
||||
foreach ($assignmentModels as $assignmentModel) {
|
||||
$assignments[] = new AssignmentForDate(
|
||||
scheduleId: $assignmentModel->schedule->id,
|
||||
setName: $assignmentModel->schedule->set_name,
|
||||
assignment: $this->assignmentToDomain($assignmentModel),
|
||||
);
|
||||
}
|
||||
|
||||
return $assignments;
|
||||
}
|
||||
|
||||
private function toDomain(ScheduleModel $model, User $user): Schedule
|
||||
{
|
||||
$assignmentModels = ScheduleAssignmentModel::query()
|
||||
|
|
@ -71,14 +105,7 @@ class EloquentScheduleRepository implements ScheduleRepository
|
|||
$assignments = [];
|
||||
|
||||
foreach ($assignmentModels as $assignmentModel) {
|
||||
$assignments[] = new ScheduleAssignment(
|
||||
id: $assignmentModel->id,
|
||||
name: $assignmentModel->element_name,
|
||||
kind: $assignmentModel->element_kind,
|
||||
path: $assignmentModel->element_path,
|
||||
scheduledDate: $this->date($assignmentModel->scheduled_date),
|
||||
position: $assignmentModel->position,
|
||||
);
|
||||
$assignments[] = $this->assignmentToDomain($assignmentModel);
|
||||
}
|
||||
|
||||
return new Schedule(
|
||||
|
|
@ -92,6 +119,19 @@ class EloquentScheduleRepository implements ScheduleRepository
|
|||
);
|
||||
}
|
||||
|
||||
private function assignmentToDomain(
|
||||
ScheduleAssignmentModel $model,
|
||||
): ScheduleAssignment {
|
||||
return new ScheduleAssignment(
|
||||
id: $model->id,
|
||||
name: $model->element_name,
|
||||
kind: $model->element_kind,
|
||||
path: $model->element_path,
|
||||
scheduledDate: $this->date($model->scheduled_date),
|
||||
position: $model->position,
|
||||
);
|
||||
}
|
||||
|
||||
private function date(string $value): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable($value, new DateTimeZone('UTC'));
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Schedule;
|
|||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
|
|
@ -14,6 +15,7 @@ use Illuminate\Database\Eloquent\Model;
|
|||
* @property list<string> $element_path
|
||||
* @property string $scheduled_date
|
||||
* @property int $position
|
||||
* @property-read ScheduleModel $schedule
|
||||
*
|
||||
* @method static Builder<static>|ScheduleAssignmentModel newModelQuery()
|
||||
* @method static Builder<static>|ScheduleAssignmentModel newQuery()
|
||||
|
|
@ -46,4 +48,12 @@ class ScheduleAssignmentModel extends Model
|
|||
'position' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<ScheduleModel, $this>
|
||||
*/
|
||||
public function schedule(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ScheduleModel::class, 'schedule_id');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Schedule;
|
||||
|
||||
use App\User\User;
|
||||
use DateTimeImmutable;
|
||||
|
||||
interface ScheduleRepository
|
||||
{
|
||||
|
|
@ -14,4 +15,12 @@ interface ScheduleRepository
|
|||
* @return list<Schedule>
|
||||
*/
|
||||
public function findAllForUser(User $user): array;
|
||||
|
||||
/**
|
||||
* @return list<AssignmentForDate>
|
||||
*/
|
||||
public function findAssignmentsForUserOnDate(
|
||||
User $user,
|
||||
DateTimeImmutable $date,
|
||||
): array;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Schedule\UseCases\ListAssignmentsForDate;
|
||||
|
||||
use App\Exceptions\BadRequestException;
|
||||
use App\Schedule\AssignmentForDate;
|
||||
use App\Schedule\ScheduleRepository;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
|
||||
class ListAssignmentsForDate
|
||||
{
|
||||
public function __construct(
|
||||
private ScheduleRepository $scheduleRepository,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return list<AssignmentForDate>
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
public function execute(ListAssignmentsForDateRequest $request): array
|
||||
{
|
||||
$date = $this->parseDate($request->date);
|
||||
|
||||
return $this->scheduleRepository->findAssignmentsForUserOnDate(
|
||||
$request->user,
|
||||
$date,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
private function parseDate(?string $value): DateTimeImmutable
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
throw new BadRequestException('date is required');
|
||||
}
|
||||
|
||||
$date = DateTimeImmutable::createFromFormat(
|
||||
'!Y-m-d',
|
||||
$value,
|
||||
new DateTimeZone('UTC'),
|
||||
);
|
||||
$errors = DateTimeImmutable::getLastErrors();
|
||||
if (
|
||||
$date === false
|
||||
|| $date->format('Y-m-d') !== $value
|
||||
|| ($errors !== false
|
||||
&& ($errors['warning_count'] > 0 || $errors['error_count'] > 0))
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
'date must be a valid date in YYYY-MM-DD format',
|
||||
);
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Schedule\UseCases\ListAssignmentsForDate;
|
||||
|
||||
use App\User\User;
|
||||
|
||||
final readonly class ListAssignmentsForDateRequest
|
||||
{
|
||||
public function __construct(
|
||||
public User $user,
|
||||
public ?string $date,
|
||||
) {}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ Route::middleware(AuthMiddleware::class)->group(function (): void {
|
|||
Route::get('/sets', [SetController::class, 'index']);
|
||||
Route::get('/sets/{setId}', [SetController::class, 'show'])
|
||||
->whereNumber('setId');
|
||||
Route::get('/assignments', [ScheduleController::class, 'assignments']);
|
||||
Route::post('/schedules', [ScheduleController::class, 'store']);
|
||||
Route::get('/schedules', [ScheduleController::class, 'index']);
|
||||
Route::get('/schedules/{scheduleId}', [ScheduleController::class, 'show'])
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
namespace Tests\Fakes;
|
||||
|
||||
use App\Schedule\CreateScheduleDto;
|
||||
use App\Schedule\AssignmentForDate;
|
||||
use App\Schedule\Schedule;
|
||||
use App\Schedule\ScheduleAssignment;
|
||||
use App\Schedule\ScheduleRepository;
|
||||
use App\User\User;
|
||||
use DateTimeImmutable;
|
||||
|
||||
class FakeScheduleRepository implements ScheduleRepository
|
||||
{
|
||||
|
|
@ -72,6 +74,31 @@ class FakeScheduleRepository implements ScheduleRepository
|
|||
}, array_values($schedules));
|
||||
}
|
||||
|
||||
public function findAssignmentsForUserOnDate(
|
||||
User $user,
|
||||
DateTimeImmutable $date,
|
||||
): array {
|
||||
$assignments = [];
|
||||
|
||||
foreach ($this->findAllForUser($user) as $schedule) {
|
||||
foreach ($schedule->getAssignments() as $assignment) {
|
||||
if ($assignment->getScheduledDate()->format('Y-m-d')
|
||||
!== $date->format('Y-m-d')
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$assignments[] = new AssignmentForDate(
|
||||
scheduleId: $schedule->getId(),
|
||||
setName: $schedule->getSetName(),
|
||||
assignment: $assignment,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $assignments;
|
||||
}
|
||||
|
||||
private function copy(Schedule $schedule): Schedule
|
||||
{
|
||||
$assignments = array_map(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue