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\BadRequestException;
|
||||||
use App\Exceptions\NotFoundException;
|
use App\Exceptions\NotFoundException;
|
||||||
|
use App\Schedule\AssignmentForDate;
|
||||||
use App\Schedule\Schedule;
|
use App\Schedule\Schedule;
|
||||||
use App\Schedule\ScheduleAssignment;
|
use App\Schedule\ScheduleAssignment;
|
||||||
use App\Schedule\UseCases\CreateSchedule\CreateSchedule;
|
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\GetSchedule;
|
||||||
use App\Schedule\UseCases\GetSchedule\GetScheduleRequest;
|
use App\Schedule\UseCases\GetSchedule\GetScheduleRequest;
|
||||||
use App\Schedule\UseCases\ListSchedules\ListSchedules;
|
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\Shared\Http\RequestInput;
|
||||||
use App\User\User;
|
use App\User\User;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
@ -22,6 +25,7 @@ class ScheduleController extends Controller
|
||||||
private CreateSchedule $createSchedule,
|
private CreateSchedule $createSchedule,
|
||||||
private ListSchedules $listSchedules,
|
private ListSchedules $listSchedules,
|
||||||
private GetSchedule $getSchedule,
|
private GetSchedule $getSchedule,
|
||||||
|
private ListAssignmentsForDate $listAssignmentsForDate,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function store(Request $request): JsonResponse
|
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>
|
* @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
|
private function user(Request $request): User
|
||||||
{
|
{
|
||||||
/** @var User $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;
|
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
|
private function toDomain(ScheduleModel $model, User $user): Schedule
|
||||||
{
|
{
|
||||||
$assignmentModels = ScheduleAssignmentModel::query()
|
$assignmentModels = ScheduleAssignmentModel::query()
|
||||||
|
|
@ -71,14 +105,7 @@ class EloquentScheduleRepository implements ScheduleRepository
|
||||||
$assignments = [];
|
$assignments = [];
|
||||||
|
|
||||||
foreach ($assignmentModels as $assignmentModel) {
|
foreach ($assignmentModels as $assignmentModel) {
|
||||||
$assignments[] = new ScheduleAssignment(
|
$assignments[] = $this->assignmentToDomain($assignmentModel);
|
||||||
id: $assignmentModel->id,
|
|
||||||
name: $assignmentModel->element_name,
|
|
||||||
kind: $assignmentModel->element_kind,
|
|
||||||
path: $assignmentModel->element_path,
|
|
||||||
scheduledDate: $this->date($assignmentModel->scheduled_date),
|
|
||||||
position: $assignmentModel->position,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Schedule(
|
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
|
private function date(string $value): DateTimeImmutable
|
||||||
{
|
{
|
||||||
return new DateTimeImmutable($value, new DateTimeZone('UTC'));
|
return new DateTimeImmutable($value, new DateTimeZone('UTC'));
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ namespace App\Schedule;
|
||||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
|
|
@ -14,6 +15,7 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
* @property list<string> $element_path
|
* @property list<string> $element_path
|
||||||
* @property string $scheduled_date
|
* @property string $scheduled_date
|
||||||
* @property int $position
|
* @property int $position
|
||||||
|
* @property-read ScheduleModel $schedule
|
||||||
*
|
*
|
||||||
* @method static Builder<static>|ScheduleAssignmentModel newModelQuery()
|
* @method static Builder<static>|ScheduleAssignmentModel newModelQuery()
|
||||||
* @method static Builder<static>|ScheduleAssignmentModel newQuery()
|
* @method static Builder<static>|ScheduleAssignmentModel newQuery()
|
||||||
|
|
@ -46,4 +48,12 @@ class ScheduleAssignmentModel extends Model
|
||||||
'position' => 'integer',
|
'position' => 'integer',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return BelongsTo<ScheduleModel, $this>
|
||||||
|
*/
|
||||||
|
public function schedule(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(ScheduleModel::class, 'schedule_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace App\Schedule;
|
namespace App\Schedule;
|
||||||
|
|
||||||
use App\User\User;
|
use App\User\User;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
|
||||||
interface ScheduleRepository
|
interface ScheduleRepository
|
||||||
{
|
{
|
||||||
|
|
@ -14,4 +15,12 @@ interface ScheduleRepository
|
||||||
* @return list<Schedule>
|
* @return list<Schedule>
|
||||||
*/
|
*/
|
||||||
public function findAllForUser(User $user): array;
|
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', [SetController::class, 'index']);
|
||||||
Route::get('/sets/{setId}', [SetController::class, 'show'])
|
Route::get('/sets/{setId}', [SetController::class, 'show'])
|
||||||
->whereNumber('setId');
|
->whereNumber('setId');
|
||||||
|
Route::get('/assignments', [ScheduleController::class, 'assignments']);
|
||||||
Route::post('/schedules', [ScheduleController::class, 'store']);
|
Route::post('/schedules', [ScheduleController::class, 'store']);
|
||||||
Route::get('/schedules', [ScheduleController::class, 'index']);
|
Route::get('/schedules', [ScheduleController::class, 'index']);
|
||||||
Route::get('/schedules/{scheduleId}', [ScheduleController::class, 'show'])
|
Route::get('/schedules/{scheduleId}', [ScheduleController::class, 'show'])
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@
|
||||||
namespace Tests\Fakes;
|
namespace Tests\Fakes;
|
||||||
|
|
||||||
use App\Schedule\CreateScheduleDto;
|
use App\Schedule\CreateScheduleDto;
|
||||||
|
use App\Schedule\AssignmentForDate;
|
||||||
use App\Schedule\Schedule;
|
use App\Schedule\Schedule;
|
||||||
use App\Schedule\ScheduleAssignment;
|
use App\Schedule\ScheduleAssignment;
|
||||||
use App\Schedule\ScheduleRepository;
|
use App\Schedule\ScheduleRepository;
|
||||||
use App\User\User;
|
use App\User\User;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
|
||||||
class FakeScheduleRepository implements ScheduleRepository
|
class FakeScheduleRepository implements ScheduleRepository
|
||||||
{
|
{
|
||||||
|
|
@ -72,6 +74,31 @@ class FakeScheduleRepository implements ScheduleRepository
|
||||||
}, array_values($schedules));
|
}, 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
|
private function copy(Schedule $schedule): Schedule
|
||||||
{
|
{
|
||||||
$assignments = array_map(
|
$assignments = array_map(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue