diff --git a/app/Http/Controllers/Api/V1/UserTimeEntryController.php b/app/Http/Controllers/Api/V1/UserTimeEntryController.php new file mode 100644 index 00000000..9264c411 --- /dev/null +++ b/app/Http/Controllers/Api/V1/UserTimeEntryController.php @@ -0,0 +1,50 @@ +whereBelongsTo($user, 'user') + ->whereNull('end') + ->orderBy('start', 'desc') + ->get(); + + if ($activeTimeEntriesOfUser->count() > 1) { + Log::warning('User has more than one active time entry.', [ + 'user' => $user->getKey(), + ]); + } + + $activeTimeEntry = $activeTimeEntriesOfUser->first(); + + if ($activeTimeEntry !== null) { + return new TimeEntryResource($activeTimeEntry); + } else { + throw new ModelNotFoundException('No active time entry'); + } + } +} diff --git a/routes/api.php b/routes/api.php index 36e4cb0b..e1463176 100644 --- a/routes/api.php +++ b/routes/api.php @@ -11,6 +11,7 @@ use App\Http\Controllers\Api\V1\TagController; use App\Http\Controllers\Api\V1\TaskController; use App\Http\Controllers\Api\V1\TimeEntryController; +use App\Http\Controllers\Api\V1\UserTimeEntryController; use Illuminate\Support\Facades\Route; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -63,6 +64,10 @@ Route::delete('/organizations/{organization}/time-entries/{timeEntry}', [TimeEntryController::class, 'destroy'])->name('destroy'); }); + Route::name('users.time-entries.')->group(static function () { + Route::get('/users/me/time-entries/active', [UserTimeEntryController::class, 'myActive'])->name('my-active'); + }); + // Tag routes Route::name('tags.')->group(static function () { Route::get('/organizations/{organization}/tags', [TagController::class, 'index'])->name('index'); diff --git a/tests/Unit/Endpoint/Api/V1/UserTimeEntryEndpointTest.php b/tests/Unit/Endpoint/Api/V1/UserTimeEntryEndpointTest.php new file mode 100644 index 00000000..a2b245b4 --- /dev/null +++ b/tests/Unit/Endpoint/Api/V1/UserTimeEntryEndpointTest.php @@ -0,0 +1,55 @@ +createUserWithPermission([ + ]); + + // Act + $response = $this->getJson(route('api.v1.users.time-entries.my-active')); + + // Assert + $response->assertUnauthorized(); + } + + public function test_my_active_endpoint_returns_current_time_entry_of_logged_in_user(): void + { + // Arrange + $data = $this->createUserWithPermission([ + ]); + $activeTimeEntry = TimeEntry::factory()->forUser($data->user)->active()->create(); + $inactiveTimeEntry = TimeEntry::factory()->forUser($data->user)->create(); + Passport::actingAs($data->user); + + // Act + $response = $this->getJson(route('api.v1.users.time-entries.my-active')); + + // Assert + $response->assertSuccessful(); + } + + public function test_my_active_endpoint_returns_not_found_if_user_has_no_active_time_entry(): void + { + // Arrange + $data = $this->createUserWithPermission([ + ]); + $inactiveTimeEntry = TimeEntry::factory()->forUser($data->user)->create(); + Passport::actingAs($data->user); + + // Act + $response = $this->getJson(route('api.v1.users.time-entries.my-active')); + + // Assert + $response->assertNotFound(); + } +}