feat(scanner): implement scan attempt endpoint and update routes and tests

This commit is contained in:
2026-09-07 16:56:12 -03:00
parent 8a65d358a4
commit 34058e6a81
6 changed files with 46 additions and 31 deletions

View File

@@ -1249,7 +1249,7 @@
"name": "Ticket", "name": "Ticket",
"item": [ "item": [
{ {
"name": "List Ticket", "name": "List Scan Attempt",
"request": { "request": {
"method": "GET", "method": "GET",
"header": [ "header": [
@@ -5998,9 +5998,9 @@
"type": "text" "type": "text"
} }
], ],
"description": "Ruta Laravel: `GET /api/v1/scanner/tickets`\n\nControlador: `App\\Domains\\Ticket\\Controllers\\Scanner\\TicketController@index`\n\nRequiere autenticación Sanctum.", "description": "Ruta Laravel: `GET /api/v1/scanner/attempts`\n\nControlador: `App\\Domains\\Ticket\\Controllers\\Scanner\\ScanAttemptController`\n\nRequiere autenticación Sanctum.",
"url": { "url": {
"raw": "{{base_url}}/api/v1/scanner/tickets?page=1&per_page=20", "raw": "{{base_url}}/api/v1/scanner/attempts?page=1&per_page=20",
"host": [ "host": [
"{{base_url}}" "{{base_url}}"
], ],
@@ -6008,7 +6008,7 @@
"api", "api",
"v1", "v1",
"scanner", "scanner",
"tickets" "attempts"
], ],
"query": [ "query": [
{ {

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Domains\Ticket\Controllers\Scanner;
use App\Domains\Auth\Models\User;
use App\Domains\Ticket\Requests\ScanAttemptIndexRequest;
use App\Domains\Ticket\Resources\Scanner\ScanAttemptResource;
use App\Domains\Ticket\Services\ScannerTicketService;
use App\Http\Controllers\Controller;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
class ScanAttemptController extends Controller
{
public function __construct(private readonly ScannerTicketService $ticketService) {}
public function __invoke(ScanAttemptIndexRequest $request): AnonymousResourceCollection
{
/** @var User $scanner */
$scanner = $request->user();
return ScanAttemptResource::collection(
$this->ticketService->attemptsBy($scanner, $request->validated())
);
}
}

View File

@@ -3,28 +3,15 @@
namespace App\Domains\Ticket\Controllers\Scanner; namespace App\Domains\Ticket\Controllers\Scanner;
use App\Domains\Auth\Models\User; use App\Domains\Auth\Models\User;
use App\Domains\Ticket\Requests\ScanAttemptIndexRequest;
use App\Domains\Ticket\Resources\Scanner\ScanAttemptResource;
use App\Domains\Ticket\Resources\TicketResource; use App\Domains\Ticket\Resources\TicketResource;
use App\Domains\Ticket\Services\ScannerTicketService; use App\Domains\Ticket\Services\ScannerTicketService;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
class TicketController extends Controller class TicketController extends Controller
{ {
public function __construct(private readonly ScannerTicketService $ticketService) {} public function __construct(private readonly ScannerTicketService $ticketService) {}
public function index(ScanAttemptIndexRequest $request): AnonymousResourceCollection
{
/** @var User $scanner */
$scanner = $request->user();
return ScanAttemptResource::collection(
$this->ticketService->attemptsBy($scanner, $request->validated())
);
}
public function show(Request $request, string $ticketUuid): TicketResource public function show(Request $request, string $ticketUuid): TicketResource
{ {
/** @var User $scanner */ /** @var User $scanner */

View File

@@ -1,13 +1,16 @@
<?php <?php
use App\Domains\Ticket\Controllers\Scanner\ScanAttemptController;
use App\Domains\Ticket\Controllers\Scanner\TicketController; use App\Domains\Ticket\Controllers\Scanner\TicketController;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
Route::prefix('v1/scanner/tickets') Route::middleware(['auth:sanctum', 'scanner.tenant'])
->middleware(['auth:sanctum', 'scanner.tenant'])
->group(function (): void { ->group(function (): void {
Route::get('/', [TicketController::class, 'index']); Route::get('v1/scanner/attempts', ScanAttemptController::class);
Route::post('scan', [TicketController::class, 'scan']);
Route::get('{ticketUuid}', [TicketController::class, 'show']) Route::prefix('v1/scanner/tickets')->group(function (): void {
->whereUuid('ticketUuid'); Route::post('scan', [TicketController::class, 'scan']);
Route::get('{ticketUuid}', [TicketController::class, 'show'])
->whereUuid('ticketUuid');
});
}); });

View File

@@ -202,7 +202,7 @@ function queryFor(string $uri): array
['key' => 'page', 'value' => '1'], ['key' => 'page', 'value' => '1'],
['key' => 'per_page', 'value' => '20'], ['key' => 'per_page', 'value' => '20'],
], ],
'api/v1/scanner/tickets' => [['key' => 'q', 'value' => '', 'disabled' => true], ['key' => 'page', 'value' => '1'], ['key' => 'per_page', 'value' => '20']], 'api/v1/scanner/attempts' => [['key' => 'q', 'value' => '', 'disabled' => true], ['key' => 'page', 'value' => '1'], ['key' => 'per_page', 'value' => '20']],
'api/storage-test/s3/temporary-url' => [['key' => 'path', 'value' => '{{s3_path}}'], ['key' => 'expires_in_minutes', 'value' => '60']], 'api/storage-test/s3/temporary-url' => [['key' => 'path', 'value' => '{{s3_path}}'], ['key' => 'expires_in_minutes', 'value' => '60']],
default => [], default => [],
}; };

View File

@@ -52,14 +52,14 @@ class ScannerTicketControllerTest extends TestCase
public function test_scanner_routes_require_authentication_and_scan_permission(): void public function test_scanner_routes_require_authentication_and_scan_permission(): void
{ {
$this->getJson('/api/v1/scanner/tickets')->assertUnauthorized(); $this->getJson('/api/v1/scanner/attempts')->assertUnauthorized();
Sanctum::actingAs(User::factory()->create([ Sanctum::actingAs(User::factory()->create([
'rol_codigo' => RoleCode::User->value, 'rol_codigo' => RoleCode::User->value,
'tenant_codigo' => $this->tenant->codigo, 'tenant_codigo' => $this->tenant->codigo,
])); ]));
$this->getJson('/api/v1/scanner/tickets')->assertForbidden(); $this->getJson('/api/v1/scanner/attempts')->assertForbidden();
} }
public function test_scanner_can_list_only_its_scan_attempts(): void public function test_scanner_can_list_only_its_scan_attempts(): void
@@ -82,7 +82,7 @@ class ScannerTicketControllerTest extends TestCase
Sanctum::actingAs($this->scanner); Sanctum::actingAs($this->scanner);
$this->getJson('/api/v1/scanner/tickets') $this->getJson('/api/v1/scanner/attempts')
->assertOk() ->assertOk()
->assertJsonCount(2, 'data') ->assertJsonCount(2, 'data')
->assertJsonPath('data.0.id', $newer->id) ->assertJsonPath('data.0.id', $newer->id)
@@ -102,7 +102,7 @@ class ScannerTicketControllerTest extends TestCase
Sanctum::actingAs($this->scanner); Sanctum::actingAs($this->scanner);
$this->getJson('/api/v1/scanner/tickets?q=aaaaaaaa&per_page=1') $this->getJson('/api/v1/scanner/attempts?q=aaaaaaaa&per_page=1')
->assertOk() ->assertOk()
->assertJsonCount(1, 'data') ->assertJsonCount(1, 'data')
->assertJsonPath('data.0.data', $matching->data) ->assertJsonPath('data.0.data', $matching->data)
@@ -118,7 +118,7 @@ class ScannerTicketControllerTest extends TestCase
Sanctum::actingAs($this->scanner); Sanctum::actingAs($this->scanner);
$this->getJson("/api/v1/scanner/tickets?q={$matching->id}") $this->getJson("/api/v1/scanner/attempts?q={$matching->id}")
->assertOk() ->assertOk()
->assertJsonCount(1, 'data') ->assertJsonCount(1, 'data')
->assertJsonPath('data.0.id', $matching->id); ->assertJsonPath('data.0.id', $matching->id);
@@ -135,7 +135,7 @@ class ScannerTicketControllerTest extends TestCase
Sanctum::actingAs($this->scanner); Sanctum::actingAs($this->scanner);
$this->getJson('/api/v1/scanner/tickets?q=11%2F08%2F26') $this->getJson('/api/v1/scanner/attempts?q=11%2F08%2F26')
->assertOk() ->assertOk()
->assertJsonCount(1, 'data') ->assertJsonCount(1, 'data')
->assertJsonPath('data.0.id', $matching->id); ->assertJsonPath('data.0.id', $matching->id);
@@ -350,7 +350,7 @@ class ScannerTicketControllerTest extends TestCase
$ticket = $this->createTicket((string) Str::uuid()); $ticket = $this->createTicket((string) Str::uuid());
Sanctum::actingAs($admin); Sanctum::actingAs($admin);
$this->getJson('/api/v1/scanner/tickets')->assertForbidden(); $this->getJson('/api/v1/scanner/attempts')->assertForbidden();
$this->getJson("/api/v1/scanner/tickets/{$ticket->ticket}")->assertForbidden(); $this->getJson("/api/v1/scanner/tickets/{$ticket->ticket}")->assertForbidden();
$this->postJson('/api/v1/scanner/tickets/scan', ['data' => $ticket->ticket])->assertForbidden(); $this->postJson('/api/v1/scanner/tickets/scan', ['data' => $ticket->ticket])->assertForbidden();
$this->assertNull($ticket->fresh()->used_at); $this->assertNull($ticket->fresh()->used_at);