Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.24% covered (success)
95.24%
20 / 21
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
UpdateExpenseController
95.24% covered (success)
95.24%
20 / 21
50.00% covered (danger)
50.00%
1 / 2
7
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 update
95.00% covered (success)
95.00%
19 / 20
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3declare(strict_types=1);
4
5namespace App\Expense\Infrastructure\Controllers;
6
7use App\Expense\Application\Command\UpdateExpenseCommand;
8use App\Expense\Application\Command\UpdateExpenseHandler;
9use App\Expense\Domain\Exceptions\ExpenseNotFound;
10use App\Expense\Infrastructure\Http\ValidatesExpenseId;
11use App\Identity\Infrastructure\Http\ResolvesAuthenticatedUser;
12use App\Shared\Infrastructure\Http\ReadsJsonPayload;
13use App\Trip\Domain\Exceptions\TripAccessDenied;
14use App\Trip\Domain\Exceptions\TripNotFound;
15use App\Trip\Infrastructure\Http\ValidatesTripId;
16use InvalidArgumentException;
17use OpenApi\Attributes as OA;
18use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
19use Symfony\Component\HttpFoundation\JsonResponse;
20use Symfony\Component\HttpFoundation\Request;
21use Symfony\Component\Routing\Attribute\Route;
22
23#[OA\Tag(name: 'Expenses')]
24class UpdateExpenseController extends AbstractController
25{
26    use ReadsJsonPayload;
27    use ResolvesAuthenticatedUser;
28    use ValidatesExpenseId;
29    use ValidatesTripId;
30
31    public function __construct(
32        private readonly UpdateExpenseHandler $handler,
33    ) {
34    }
35
36    #[Route('/trips/{tripId}/expenses/{id}', name: 'expenses_update', methods: ['PUT'])]
37    public function update(string $tripId, string $id, Request $request): JsonResponse
38    {
39        $securityUser = $this->securityUser();
40
41        if ($this->parseTripId($tripId) === null || $this->parseExpenseId($id) === null) {
42            return new JsonResponse(['error' => 'Expense not found.'], 404);
43        }
44
45        try {
46            $payload = $this->decodeJsonBody($request);
47
48            $view = $this->handler->handle(new UpdateExpenseCommand(
49                tripId: $tripId,
50                userId: $securityUser->userId(),
51                expenseId: $id,
52                amount: $this->intField($payload, 'amount'),
53                category: $this->stringField($payload, 'category'),
54                description: $this->stringField($payload, 'description'),
55                spentAt: $this->stringField($payload, 'spentAt'),
56            ));
57        } catch (ExpenseNotFound|TripNotFound $e) {
58            return new JsonResponse(['error' => $e->getMessage()], 404);
59        } catch (TripAccessDenied $e) {
60            return new JsonResponse(['error' => $e->getMessage()], 403);
61        } catch (InvalidArgumentException $e) {
62            return new JsonResponse(['error' => $e->getMessage()], 400);
63        }
64
65        return new JsonResponse($view->toArray(), 200);
66    }
67}