diff --git a/README.md b/README.md index c1f38b5d..bbdddcd0 100644 --- a/README.md +++ b/README.md @@ -27,52 +27,58 @@ Documentation is available at: https://docs.dotkernel.org/api-documentation/ ## Getting Started -## Step 1: Clone the project +### Clone the project -Using your terminal, navigate inside the directory you want to download the project files into. Make sure that the directory is empty before proceeding to the download process. Once there, run the following command: +Using your terminal, navigate inside the directory you want to download the project files into. +Make sure that the directory is empty before proceeding to the download process. +Once there, run the following command: ```shell git clone https://github.com/dotkernel/api.git . ``` -## Step 2: Install project's dependencies +### Install the project dependencies ```shell composer install ``` -## Step 3: Development mode +### Development mode -If you're installing the project for development, make sure you have development mode enabled, by running: +> **Do not enable development mode in production!** + +If you're installing the project for development, you should **enable** development mode, by running: ```shell composer development-enable ``` -You can disable development mode by running: +You can **disable** development mode by running: ```shell composer development-disable ``` -You can check if you have development mode enabled by running: +You can **check** development status by running: ```shell composer development-status ``` -## Step 4: Prepare config files +### Prepare config files * duplicate `config/autoload/cors.local.php.dist` as `config/autoload/cors.local.php` <- if your API will be consumed by another application, make sure configure the `allowed_origins` * duplicate `config/autoload/local.php.dist` as `config/autoload/local.php` -* duplicate `config/autoload/mail.local.php.dist` as `config/autoload/mail.local.php` <- if your API will send emails, make sure you fill in SMTP connection params * **optional**: in order to run/create tests, duplicate `config/autoload/local.test.php.dist` as `config/autoload/local.test.php` <- this creates a new in-memory database that your tests will run on -## Step 5: Setup database +### Setup database + +Use an existing empty one or create a new **MariaDB**/**MySQL** database. + +> Recommended collation: `utf8mb4_general_ci`. -## Running migrations +#### Running migrations -* create a new MySQL database - set collation to `utf8mb4_general_ci` * fill out the database connection params in `config/autoload/local.php` under `$databases['default']` * run the database migrations by using the following command: @@ -82,13 +88,13 @@ php ./vendor/bin/doctrine-migrations migrate This command will prompt you to confirm that you want to run it: -> WARNING! You are about to execute a migration in database "..." that could result in schema changes and data loss. Are you sure you wish to continue? (yes/no) [yes]: +> WARNING! You are about to execute a migration in database "``" that could result in schema changes and data loss. Are you sure you wish to continue? (yes/no) [yes]: Hit `Enter` to confirm the operation. -## Executing fixtures +#### Executing fixtures -**Fixtures are used to seed the database with initial values and should be executed after migrating the database.** +Fixtures are used to seed the database with initial values and must be executed after migrating the database. To list all the fixtures, run: @@ -110,29 +116,26 @@ To execute a specific fixture, run: php ./bin/doctrine fixtures:execute --class=FixtureClassName ``` -More details on how fixtures work can be found here: https://github.com/dotkernel/dot-data-fixtures#creating-fixtures +More details on how fixtures work can be found in `dotkernel/dot-data-fixtures` [documentation](https://github.com/dotkernel/dot-data-fixtures#creating-fixtures). + +### Mail configuration + +If your application will send emails, you must configure an outgoing mail server under `config/autoload/mail.global.php`. + +### Test the installation -## Step 6: Test the installation +Run the following command in your project's directory to start PHPs built-in server: ```shell php -S 0.0.0.0:8080 -t public ``` -Sending a GET request to the [home page](http://0.0.0.0:8080/) should output the following message: +> Running command `composer serve` will do the same thing, but the server will time out after a couple of minutes. -```text +Sending a **GET** request to the application's [home page](http://localhost:8080/) should output the following message: + +```json { - "message": "Dotkernel API version 5" + "message": "Dotkernel API version 6" } ``` - -## Documentation - -In order to access Dotkernel API documentation, check the provided [readme file](documentation/README.md). - -Additionally, each CLI command available has it's own documentation: - -* [Create admin account](documentation/command/admin-create.md) -* [Generate database migrations](documentation/command/migrations-diff.md) -* [Display available endpoints](documentation/command/route-list.md) -* [Generate tokens](documentation/command/token-generate.md) diff --git a/config/autoload/error-handling.global.php b/config/autoload/error-handling.global.php index cf4044f8..80938967 100644 --- a/config/autoload/error-handling.global.php +++ b/config/autoload/error-handling.global.php @@ -81,5 +81,10 @@ * 3. If you want to whitelist only specific IP addresses, add them to ip_whitelist. */ 'ip_whitelist' => [], + + /** + * Documentation URL for problem details response + */ + 'documentation_url' => 'https://docs.dotkernel.org/api-documentation/v5/core-features/error-reporting/', ], ]; diff --git a/config/autoload/problem-details.global.php b/config/autoload/problem-details.global.php new file mode 100644 index 00000000..d471e92c --- /dev/null +++ b/config/autoload/problem-details.global.php @@ -0,0 +1,36 @@ + [ + 'default_types_map' => [ + StatusCodeInterface::STATUS_BAD_REQUEST + => 'https://datatracker.ietf.org/doc/html/rfc9110#name-400-bad-request', + StatusCodeInterface::STATUS_UNAUTHORIZED + => 'https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized', + StatusCodeInterface::STATUS_FORBIDDEN + => 'https://datatracker.ietf.org/doc/html/rfc9110#name-403-forbidden', + StatusCodeInterface::STATUS_NOT_FOUND + => 'https://datatracker.ietf.org/doc/html/rfc9110#name-404-not-found', + StatusCodeInterface::STATUS_METHOD_NOT_ALLOWED + => 'https://datatracker.ietf.org/doc/html/rfc9110#name-405-method-not-allowed', + StatusCodeInterface::STATUS_NOT_ACCEPTABLE + => 'https://datatracker.ietf.org/doc/html/rfc9110#name-406-not-acceptable', + StatusCodeInterface::STATUS_CONFLICT + => 'https://datatracker.ietf.org/doc/html/rfc9110#name-409-conflict', + StatusCodeInterface::STATUS_GONE + => 'https://datatracker.ietf.org/doc/html/rfc9110#name-410-gone', + StatusCodeInterface::STATUS_UNSUPPORTED_MEDIA_TYPE + => 'https://datatracker.ietf.org/doc/html/rfc9110#name-415-unsupported-media-type', + StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR + => 'https://datatracker.ietf.org/doc/html/rfc9110#name-500-internal-server-error', + ], + ], +]; diff --git a/config/pipeline.php b/config/pipeline.php index 015c3ccd..eccd7606 100644 --- a/config/pipeline.php +++ b/config/pipeline.php @@ -15,6 +15,8 @@ use Mezzio\Helper\BodyParams\BodyParamsMiddleware; use Mezzio\Helper\ServerUrlMiddleware; use Mezzio\Helper\UrlHelperMiddleware; +use Mezzio\ProblemDetails\ProblemDetailsMiddleware; +use Mezzio\ProblemDetails\ProblemDetailsNotFoundHandler; use Mezzio\Router\Middleware\DispatchMiddleware; use Mezzio\Router\Middleware\ImplicitHeadMiddleware; use Mezzio\Router\Middleware\ImplicitOptionsMiddleware; @@ -25,6 +27,7 @@ // The error handler should be the first (most outer) middleware to catch // all Exceptions. $app->pipe(ErrorHandlerInterface::class); + $app->pipe(ProblemDetailsMiddleware::class); $app->pipe(BodyParamsMiddleware::class); $app->pipe(ServerUrlMiddleware::class); @@ -87,5 +90,6 @@ // At this point, if no Response is returned by any middleware, the // NotFoundHandler kicks in; alternately, you can provide other fallback // middleware to execute. + $app->pipe(ProblemDetailsNotFoundHandler::class); $app->pipe(GetNotFoundViewHandler::class); }; diff --git a/documentation/Dotkernel_API.postman_collection.json b/documentation/Dotkernel_API.postman_collection.json index 26901446..58c87ab1 100644 --- a/documentation/Dotkernel_API.postman_collection.json +++ b/documentation/Dotkernel_API.postman_collection.json @@ -129,21 +129,25 @@ { "key": "page", "value": "1", + "description": "Page number", "disabled": true }, { "key": "limit", "value": "10", + "description": "Items per page", "disabled": true }, { "key": "sort", "value": "admin.created", + "description": "Sort items by this field", "disabled": true }, { "key": "dir", "value": "desc", + "description": "Sort items in this direction", "disabled": true }, { @@ -182,13 +186,13 @@ } }, "url": { - "raw": "{{APPLICATION_URL}}/admin/abd444ed-cc65-41e7-96c7-2391a9103f15", + "raw": "{{APPLICATION_URL}}/admin/{{$randomUUID}}", "host": [ "{{APPLICATION_URL}}" ], "path": [ "admin", - "abd444ed-cc65-41e7-96c7-2391a9103f15" + "{{$randomUUID}}" ] }, "description": "Admin updates admin account.\n\nReplace random UUID in URL with a valid user UUID." @@ -1268,13 +1272,13 @@ { "key": "sort", "value": "user.created", - "description": "order results by this field", + "description": "Sort items by this field", "disabled": true }, { "key": "dir", "value": "desc", - "description": "order results direction", + "description": "Sort items in this direction", "disabled": true }, { @@ -1471,4 +1475,4 @@ "value": "597155ec70defb9f969c9beaf609814933db53cbcb8b9be6db5e0bf7e051e1e4" } ] -} +} \ No newline at end of file diff --git a/src/Admin/src/Command/AdminCreateCommand.php b/src/Admin/src/Command/AdminCreateCommand.php index fc21a0f7..afb2a0bd 100644 --- a/src/Admin/src/Command/AdminCreateCommand.php +++ b/src/Admin/src/Command/AdminCreateCommand.php @@ -7,13 +7,15 @@ use Api\Admin\InputFilter\CreateAdminInputFilter; use Api\Admin\Service\AdminRoleServiceInterface; use Api\Admin\Service\AdminServiceInterface; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Core\Admin\Entity\AdminRole; use Core\Admin\Enum\AdminRoleEnum; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; +use Core\Admin\Enum\AdminStatusEnum; use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; +use Exception; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -63,6 +65,7 @@ protected function configure(): void /** * @throws BadRequestException * @throws ConflictException + * @throws Exception * @throws NotFoundException */ protected function execute(InputInterface $input, OutputInterface $output): int @@ -76,7 +79,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } - throw new BadRequestException(implode(PHP_EOL, $messages)); + throw new Exception(implode(PHP_EOL, $messages)); } $this->adminService->saveAdmin($inputFilter->getValues()); @@ -87,13 +90,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int } /** - * @throws NotFoundException + * @throws Exception */ private function getData(InputInterface $input): array { $adminRole = $this->adminRoleService->getAdminRoleRepository()->findOneBy(['name' => AdminRoleEnum::Admin]); if (! $adminRole instanceof AdminRole) { - throw new NotFoundException(Message::ROLE_NOT_FOUND); + throw new Exception(Message::ROLE_NOT_FOUND); } return [ @@ -102,6 +105,7 @@ private function getData(InputInterface $input): array 'passwordConfirm' => $input->getOption('password'), 'firstName' => $input->getOption('firstName'), 'lastName' => $input->getOption('lastName'), + 'status' => AdminStatusEnum::Active->value, 'roles' => [ ['uuid' => $adminRole->getUuid()->toString()], ], diff --git a/src/Admin/src/Handler/Account/PatchAdminAccountResourceHandler.php b/src/Admin/src/Handler/Account/PatchAdminAccountResourceHandler.php index 1bbfd039..a6e39811 100644 --- a/src/Admin/src/Handler/Account/PatchAdminAccountResourceHandler.php +++ b/src/Admin/src/Handler/Account/PatchAdminAccountResourceHandler.php @@ -6,11 +6,12 @@ use Api\Admin\InputFilter\UpdateAdminInputFilter; use Api\Admin\Service\AdminServiceInterface; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Core\Admin\Entity\Admin; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; +use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -36,7 +37,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } return $this->createResponse( diff --git a/src/Admin/src/Handler/Admin/DeleteAdminResourceHandler.php b/src/Admin/src/Handler/Admin/DeleteAdminResourceHandler.php index c5e4d5a9..bedddafd 100644 --- a/src/Admin/src/Handler/Admin/DeleteAdminResourceHandler.php +++ b/src/Admin/src/Handler/Admin/DeleteAdminResourceHandler.php @@ -5,8 +5,8 @@ namespace Api\Admin\Handler\Admin; use Api\Admin\Service\AdminServiceInterface; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; -use Core\App\Exception\NotFoundException; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; diff --git a/src/Admin/src/Handler/Admin/GetAdminResourceHandler.php b/src/Admin/src/Handler/Admin/GetAdminResourceHandler.php index ae68718b..53693ccc 100644 --- a/src/Admin/src/Handler/Admin/GetAdminResourceHandler.php +++ b/src/Admin/src/Handler/Admin/GetAdminResourceHandler.php @@ -5,8 +5,8 @@ namespace Api\Admin\Handler\Admin; use Api\Admin\Service\AdminServiceInterface; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; -use Core\App\Exception\NotFoundException; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; diff --git a/src/Admin/src/Handler/Admin/PatchAdminResourceHandler.php b/src/Admin/src/Handler/Admin/PatchAdminResourceHandler.php index 906eba6b..e19faeb3 100644 --- a/src/Admin/src/Handler/Admin/PatchAdminResourceHandler.php +++ b/src/Admin/src/Handler/Admin/PatchAdminResourceHandler.php @@ -6,10 +6,11 @@ use Api\Admin\InputFilter\UpdateAdminInputFilter; use Api\Admin\Service\AdminServiceInterface; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; +use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -35,7 +36,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } return $this->createResponse( diff --git a/src/Admin/src/Handler/Admin/PostAdminResourceHandler.php b/src/Admin/src/Handler/Admin/PostAdminResourceHandler.php index c40c47f3..eab9f113 100644 --- a/src/Admin/src/Handler/Admin/PostAdminResourceHandler.php +++ b/src/Admin/src/Handler/Admin/PostAdminResourceHandler.php @@ -6,10 +6,11 @@ use Api\Admin\InputFilter\CreateAdminInputFilter; use Api\Admin\Service\AdminServiceInterface; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; +use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -35,7 +36,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } return $this->createdResponse( diff --git a/src/Admin/src/Handler/Admin/Role/GetAdminRoleResourceHandler.php b/src/Admin/src/Handler/Admin/Role/GetAdminRoleResourceHandler.php index c3260670..3c1f2c1d 100644 --- a/src/Admin/src/Handler/Admin/Role/GetAdminRoleResourceHandler.php +++ b/src/Admin/src/Handler/Admin/Role/GetAdminRoleResourceHandler.php @@ -5,8 +5,8 @@ namespace Api\Admin\Handler\Admin\Role; use Api\Admin\Service\AdminRoleServiceInterface; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; -use Core\App\Exception\NotFoundException; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; diff --git a/src/Admin/src/InputFilter/Input/StatusInput.php b/src/Admin/src/InputFilter/Input/StatusInput.php index 28565080..e8676a43 100644 --- a/src/Admin/src/InputFilter/Input/StatusInput.php +++ b/src/Admin/src/InputFilter/Input/StatusInput.php @@ -10,6 +10,7 @@ use Laminas\Filter\StripTags; use Laminas\InputFilter\Input; use Laminas\Validator\InArray; +use Laminas\Validator\NotEmpty; class StatusInput extends Input { @@ -21,12 +22,14 @@ public function __construct(?string $name = null, bool $isRequired = true) $this->getFilterChain() ->attachByName(StringTrim::class) - ->attachByName(StripTags::class) - ->attach(fn($value) => $value === null ? AdminStatusEnum::Active : AdminStatusEnum::from($value)); + ->attachByName(StripTags::class); $this->getValidatorChain() + ->attachByName(NotEmpty::class, [ + 'message' => Message::VALIDATOR_REQUIRED_FIELD, + ], true) ->attachByName(InArray::class, [ - 'haystack' => AdminStatusEnum::cases(), + 'haystack' => AdminStatusEnum::values(), 'message' => Message::invalidValue('status'), ], true); } diff --git a/src/Admin/src/Service/AdminRoleService.php b/src/Admin/src/Service/AdminRoleService.php index e57cda67..ebcac33a 100644 --- a/src/Admin/src/Service/AdminRoleService.php +++ b/src/Admin/src/Service/AdminRoleService.php @@ -4,9 +4,9 @@ namespace Api\Admin\Service; +use Api\App\Exception\NotFoundException; use Core\Admin\Entity\AdminRole; use Core\Admin\Repository\AdminRoleRepository; -use Core\App\Exception\NotFoundException; use Core\App\Helper\Paginator; use Core\App\Message; use Doctrine\ORM\QueryBuilder; @@ -36,7 +36,7 @@ public function findAdminRole(string $id): AdminRole { $adminRole = $this->adminRoleRepository->find($id); if (! $adminRole instanceof AdminRole) { - throw new NotFoundException(Message::ROLE_NOT_FOUND); + throw NotFoundException::create(Message::ROLE_NOT_FOUND); } return $adminRole; diff --git a/src/Admin/src/Service/AdminRoleServiceInterface.php b/src/Admin/src/Service/AdminRoleServiceInterface.php index bf6003c8..458dbad2 100644 --- a/src/Admin/src/Service/AdminRoleServiceInterface.php +++ b/src/Admin/src/Service/AdminRoleServiceInterface.php @@ -4,9 +4,9 @@ namespace Api\Admin\Service; +use Api\App\Exception\NotFoundException; use Core\Admin\Entity\AdminRole; use Core\Admin\Repository\AdminRoleRepository; -use Core\App\Exception\NotFoundException; use Doctrine\ORM\QueryBuilder; interface AdminRoleServiceInterface diff --git a/src/Admin/src/Service/AdminService.php b/src/Admin/src/Service/AdminService.php index ebe557f7..af928cbc 100644 --- a/src/Admin/src/Service/AdminService.php +++ b/src/Admin/src/Service/AdminService.php @@ -4,14 +4,14 @@ namespace Api\Admin\Service; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Core\Admin\Entity\Admin; use Core\Admin\Entity\AdminRole; use Core\Admin\Enum\AdminStatusEnum; use Core\Admin\Repository\AdminRepository; use Core\Admin\Repository\AdminRoleRepository; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; use Core\App\Helper\Paginator; use Core\App\Message; use Doctrine\ORM\QueryBuilder; @@ -51,7 +51,7 @@ public function findAdmin(string $uuid): Admin { $admin = $this->adminRepository->find($uuid); if (! $admin instanceof Admin) { - throw new NotFoundException(Message::ADMIN_NOT_FOUND); + throw NotFoundException::create(Message::ADMIN_NOT_FOUND); } return $admin; @@ -110,7 +110,10 @@ public function saveAdmin(array $data, ?Admin $admin = null): Admin $status = AdminStatusEnum::tryFrom($status); } if (! $status instanceof AdminStatusEnum) { - throw new BadRequestException(Message::invalidValue('status')); + throw BadRequestException::create( + detail: Message::invalidValue('status'), + additional: ['errors' => ['status' => $data['status']]] + ); } $admin->setStatus($status); } @@ -122,14 +125,14 @@ public function saveAdmin(array $data, ?Admin $admin = null): Admin foreach ($data['roles'] as $roleData) { $adminRole = $this->adminRoleRepository->find($roleData['uuid']); if (! $adminRole instanceof AdminRole) { - throw new NotFoundException(Message::ROLE_NOT_FOUND); + throw NotFoundException::create(Message::ROLE_NOT_FOUND); } $admin->addRole($adminRole); } } if (! $admin->hasRoles()) { - throw (new BadRequestException())->setMessages([Message::RESTRICTION_ROLES]); + throw BadRequestException::create(Message::RESTRICTION_ROLES); } $this->adminRepository->saveResource($admin); @@ -145,10 +148,10 @@ public function validateUniqueAdmin(string $identity, ?UuidInterface $uuid = nul $admin = $this->adminRepository->findOneBy(['identity' => $identity]); if ($admin instanceof Admin) { if ($uuid === null) { - throw new ConflictException(Message::DUPLICATE_IDENTITY); + throw ConflictException::create(Message::DUPLICATE_IDENTITY); } if ($admin->getUuid()->toString() !== $uuid->toString()) { - throw new ConflictException(Message::DUPLICATE_IDENTITY); + throw ConflictException::create(Message::DUPLICATE_IDENTITY); } } } diff --git a/src/Admin/src/Service/AdminServiceInterface.php b/src/Admin/src/Service/AdminServiceInterface.php index c8bf2b93..d5f5b391 100644 --- a/src/Admin/src/Service/AdminServiceInterface.php +++ b/src/Admin/src/Service/AdminServiceInterface.php @@ -4,11 +4,11 @@ namespace Api\Admin\Service; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Core\Admin\Entity\Admin; use Core\Admin\Repository\AdminRepository; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; use Doctrine\ORM\QueryBuilder; interface AdminServiceInterface diff --git a/src/App/src/Attribute/BaseDeprecation.php b/src/App/src/Attribute/BaseDeprecation.php index 3773697f..19111bd2 100644 --- a/src/App/src/Attribute/BaseDeprecation.php +++ b/src/App/src/Attribute/BaseDeprecation.php @@ -4,12 +4,10 @@ namespace Api\App\Attribute; -use Core\App\Exception\DeprecationSunsetException; +use Api\App\Exception\SunsetException; use Core\App\Message; use Laminas\Validator\Date; -use function sprintf; - readonly class BaseDeprecation { public function __construct( @@ -20,7 +18,7 @@ public function __construct( public string $type = 'text/html', ) { if (null !== $sunset && ! (new Date())->isValid($sunset)) { - throw new DeprecationSunsetException(sprintf(Message::INVALID_VALUE, 'sunset')); + throw new SunsetException(Message::invalidValue('sunset')); } } diff --git a/src/App/src/Command/TokenGenerateCommand.php b/src/App/src/Command/TokenGenerateCommand.php index 5f34f861..3801b107 100644 --- a/src/App/src/Command/TokenGenerateCommand.php +++ b/src/App/src/Command/TokenGenerateCommand.php @@ -5,8 +5,8 @@ namespace Api\App\Command; use Api\App\Service\ErrorReportServiceInterface; -use Core\App\Exception\NotFoundException; use Dot\DependencyInjection\Attribute\Inject; +use RuntimeException; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -57,16 +57,14 @@ protected function configure(): void } /** - * @throws NotFoundException + * @throws RuntimeException */ protected function execute(InputInterface $input, OutputInterface $output): int { - $io = new SymfonyStyle($input, $output); - $type = $input->getArgument('type'); match ($type) { - $this->typeErrorReporting => $this->generateErrorReportingToken($io), - default => throw new NotFoundException( + $this->typeErrorReporting => $this->generateErrorReportingToken(new SymfonyStyle($input, $output)), + default => throw new RuntimeException( sprintf('Unknown token type: %s', $type) ) }; diff --git a/src/App/src/ConfigProvider.php b/src/App/src/ConfigProvider.php index 8ac66e93..276a791f 100644 --- a/src/App/src/ConfigProvider.php +++ b/src/App/src/ConfigProvider.php @@ -13,6 +13,7 @@ use Api\App\Middleware\ContentNegotiationMiddleware; use Api\App\Middleware\DeprecationMiddleware; use Api\App\Middleware\ErrorReportPermissionMiddleware; +use Api\App\Middleware\ResponseMiddleware; use Api\App\Service\ErrorReportService; use Api\App\Service\ErrorReportServiceInterface; use Dot\DependencyInjection\Factory\AttributedServiceFactory; @@ -53,6 +54,7 @@ private function getDependencies(): array ContentNegotiationMiddleware::class => AttributedServiceFactory::class, DeprecationMiddleware::class => AttributedServiceFactory::class, ErrorReportPermissionMiddleware::class => AttributedServiceFactory::class, + ResponseMiddleware::class => AttributedServiceFactory::class, GetIndexResourceHandler::class => AttributedServiceFactory::class, PostErrorReportResourceHandler::class => AttributedServiceFactory::class, ErrorReportService::class => AttributedServiceFactory::class, diff --git a/src/App/src/Exception/BadRequestException.php b/src/App/src/Exception/BadRequestException.php new file mode 100644 index 00000000..e6b81b7d --- /dev/null +++ b/src/App/src/Exception/BadRequestException.php @@ -0,0 +1,28 @@ +type = $type; + $exception->detail = $detail; + $exception->status = StatusCodeInterface::STATUS_BAD_REQUEST; + $exception->title = $title; + $exception->additional = $additional; + + return $exception; + } +} diff --git a/src/App/src/Exception/ConflictException.php b/src/App/src/Exception/ConflictException.php new file mode 100644 index 00000000..17e9741b --- /dev/null +++ b/src/App/src/Exception/ConflictException.php @@ -0,0 +1,28 @@ +type = $type; + $exception->detail = $detail; + $exception->status = StatusCodeInterface::STATUS_CONFLICT; + $exception->title = $title; + $exception->additional = $additional; + + return $exception; + } +} diff --git a/src/App/src/Exception/ExpiredException.php b/src/App/src/Exception/ExpiredException.php new file mode 100644 index 00000000..6521a8c7 --- /dev/null +++ b/src/App/src/Exception/ExpiredException.php @@ -0,0 +1,28 @@ +type = $type; + $exception->detail = $detail; + $exception->status = StatusCodeInterface::STATUS_GONE; + $exception->title = $title; + $exception->additional = $additional; + + return $exception; + } +} diff --git a/src/App/src/Exception/ForbiddenException.php b/src/App/src/Exception/ForbiddenException.php new file mode 100644 index 00000000..d4781674 --- /dev/null +++ b/src/App/src/Exception/ForbiddenException.php @@ -0,0 +1,28 @@ +type = $type; + $exception->detail = $detail; + $exception->status = StatusCodeInterface::STATUS_FORBIDDEN; + $exception->title = $title; + $exception->additional = $additional; + + return $exception; + } +} diff --git a/src/App/src/Exception/NotFoundException.php b/src/App/src/Exception/NotFoundException.php new file mode 100644 index 00000000..7c792dff --- /dev/null +++ b/src/App/src/Exception/NotFoundException.php @@ -0,0 +1,28 @@ +type = $type; + $exception->detail = $detail; + $exception->status = StatusCodeInterface::STATUS_NOT_FOUND; + $exception->title = $title; + $exception->additional = $additional; + + return $exception; + } +} diff --git a/src/App/src/Exception/RuntimeException.php b/src/App/src/Exception/RuntimeException.php new file mode 100644 index 00000000..1c75e8eb --- /dev/null +++ b/src/App/src/Exception/RuntimeException.php @@ -0,0 +1,27 @@ +type = $type; + $exception->detail = $detail; + $exception->status = StatusCodeInterface::STATUS_BAD_REQUEST; + $exception->title = $title; + $exception->additional = $additional; + + return $exception; + } +} diff --git a/src/App/src/Exception/SunsetException.php b/src/App/src/Exception/SunsetException.php new file mode 100644 index 00000000..31883c38 --- /dev/null +++ b/src/App/src/Exception/SunsetException.php @@ -0,0 +1,11 @@ +type = $type; + $exception->detail = $detail; + $exception->status = StatusCodeInterface::STATUS_UNAUTHORIZED; + $exception->title = $title; + $exception->additional = $additional; + + return $exception; + } +} diff --git a/src/App/src/Factory/HandlerDelegatorFactory.php b/src/App/src/Factory/HandlerDelegatorFactory.php index 9a196ced..b870f63e 100644 --- a/src/App/src/Factory/HandlerDelegatorFactory.php +++ b/src/App/src/Factory/HandlerDelegatorFactory.php @@ -4,8 +4,8 @@ namespace Api\App\Factory; +use Api\App\Exception\RuntimeException; use Api\App\Handler\AbstractHandler; -use Core\App\Exception\RuntimeException; use Core\App\Message; use Mezzio\Hal\HalResponseFactory; use Mezzio\Hal\ResourceGenerator; @@ -15,7 +15,6 @@ use Psr\Http\Server\RequestHandlerInterface; use function assert; -use function sprintf; class HandlerDelegatorFactory { @@ -31,10 +30,10 @@ public function __invoke( callable $callback ): RequestHandlerInterface { if (! $container->has(HalResponseFactory::class)) { - throw new RuntimeException(sprintf(Message::SERVICE_NOT_FOUND, HalResponseFactory::class)); + throw RuntimeException::create(Message::serviceNotFound(HalResponseFactory::class)); } if (! $container->has(ResourceGenerator::class)) { - throw new RuntimeException(sprintf(Message::SERVICE_NOT_FOUND, ResourceGenerator::class)); + throw RuntimeException::create(Message::serviceNotFound(ResourceGenerator::class)); } $handler = $callback(); diff --git a/src/App/src/Handler/PostErrorReportResourceHandler.php b/src/App/src/Handler/PostErrorReportResourceHandler.php index b58dc670..71e66277 100644 --- a/src/App/src/Handler/PostErrorReportResourceHandler.php +++ b/src/App/src/Handler/PostErrorReportResourceHandler.php @@ -5,9 +5,9 @@ namespace Api\App\Handler; use Api\App\Attribute\MethodDeprecation; +use Api\App\Exception\BadRequestException; use Api\App\InputFilter\ErrorReportInputFilter; use Api\App\Service\ErrorReportServiceInterface; -use Core\App\Exception\BadRequestException; use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Fig\Http\Message\StatusCodeInterface; @@ -40,7 +40,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } $this->errorReportService->appendMessage($this->inputFilter->getValue('message')); diff --git a/src/App/src/Middleware/DeprecationMiddleware.php b/src/App/src/Middleware/DeprecationMiddleware.php index 2bd0367f..50c47d44 100644 --- a/src/App/src/Middleware/DeprecationMiddleware.php +++ b/src/App/src/Middleware/DeprecationMiddleware.php @@ -6,7 +6,8 @@ use Api\App\Attribute\MethodDeprecation; use Api\App\Attribute\ResourceDeprecation; -use Core\App\Exception\DeprecationConflictException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\RuntimeException; use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Dot\Router\Middleware\LazyLoadingMiddleware; @@ -20,7 +21,6 @@ use ReflectionClass; use ReflectionException; use ReflectionMethod; -use RuntimeException; use function array_column; use function array_filter; @@ -51,7 +51,9 @@ public function __construct( } /** + * @throws ConflictException * @throws ReflectionException + * @throws RuntimeException */ public function process( ServerRequestInterface $request, @@ -138,6 +140,7 @@ private function getReflectionAttributes(ReflectionClass $reflectionObject): arr /** * @throws ReflectionException + * @throws RuntimeException */ private function getHandler(MiddlewareInterface $routeMiddleware): ?ReflectionClass { @@ -162,17 +165,20 @@ private function getHandler(MiddlewareInterface $routeMiddleware): ?ReflectionCl } } } else { - throw new RuntimeException('Invalid route middleware provided.'); + throw RuntimeException::create('Invalid route middleware provided.'); } return $reflectionHandler; } + /** + * @throws ConflictException + */ private function validateAttributes(array $attributes): void { $intersect = array_intersect(self::DEPRECATION_ATTRIBUTES, array_column($attributes, 'deprecationType')); if (count($intersect) === count(self::DEPRECATION_ATTRIBUTES)) { - throw new DeprecationConflictException( + throw ConflictException::create( sprintf( Message::RESTRICTION_DEPRECATION, self::RESOURCE_DEPRECATION_ATTRIBUTE, diff --git a/src/App/src/Middleware/ErrorReportPermissionMiddleware.php b/src/App/src/Middleware/ErrorReportPermissionMiddleware.php index 32e0d1cc..f537338b 100644 --- a/src/App/src/Middleware/ErrorReportPermissionMiddleware.php +++ b/src/App/src/Middleware/ErrorReportPermissionMiddleware.php @@ -4,10 +4,10 @@ namespace Api\App\Middleware; +use Api\App\Exception\ForbiddenException; +use Api\App\Exception\RuntimeException; +use Api\App\Exception\UnauthorizedException; use Api\App\Service\ErrorReportServiceInterface; -use Core\App\Exception\ForbiddenException; -use Core\App\Exception\RuntimeException; -use Core\App\Exception\UnauthorizedException; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; diff --git a/src/App/src/Middleware/ResponseMiddleware.php b/src/App/src/Middleware/ResponseMiddleware.php index 9604a1a1..a68ab475 100644 --- a/src/App/src/Middleware/ResponseMiddleware.php +++ b/src/App/src/Middleware/ResponseMiddleware.php @@ -4,59 +4,38 @@ namespace Api\App\Middleware; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\ExpiredException; -use Core\App\Exception\ForbiddenException; -use Core\App\Exception\MethodNotAllowedException; -use Core\App\Exception\NotFoundException; -use Core\App\Exception\RuntimeException; -use Core\App\Exception\UnauthorizedException; -use Dot\Mail\Exception\MailException; -use Exception; +use Dot\DependencyInjection\Attribute\Inject; use Fig\Http\Message\StatusCodeInterface; -use Laminas\Diactoros\Response\JsonResponse; -use Mezzio\Hal\ResourceGenerator\Exception\OutOfBoundsException; +use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; +use Mezzio\ProblemDetails\ProblemDetailsResponseFactory; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Throwable; -use function is_array; - -class ResponseMiddleware implements MiddlewareInterface +readonly class ResponseMiddleware implements MiddlewareInterface { + #[Inject( + ProblemDetailsResponseFactory::class, + )] + public function __construct( + private ProblemDetailsResponseFactory $problemDetailsResponseFactory, + ) { + } + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { try { return $handler->handle($request); - } catch (ConflictException $exception) { - return $this->errorResponse($exception->getMessage(), StatusCodeInterface::STATUS_CONFLICT); - } catch (ForbiddenException $exception) { - return $this->errorResponse($exception->getMessage(), StatusCodeInterface::STATUS_FORBIDDEN); - } catch (ExpiredException $exception) { - return $this->errorResponse($exception->getMessage(), StatusCodeInterface::STATUS_GONE); - } catch (OutOfBoundsException | NotFoundException $exception) { - return $this->errorResponse($exception->getMessage(), StatusCodeInterface::STATUS_NOT_FOUND); - } catch (UnauthorizedException $exception) { - return $this->errorResponse($exception->getMessage(), StatusCodeInterface::STATUS_UNAUTHORIZED); - } catch (MethodNotAllowedException $exception) { - return $this->errorResponse($exception->getMessage(), StatusCodeInterface::STATUS_METHOD_NOT_ALLOWED); - } catch (BadRequestException $exception) { - return $this->errorResponse($exception->getMessages(), StatusCodeInterface::STATUS_BAD_REQUEST); - } catch (MailException | RuntimeException | Exception $exception) { - return $this->errorResponse($exception->getMessage()); + } catch (ProblemDetailsExceptionInterface $exception) { + return $this->problemDetailsResponseFactory->createResponseFromThrowable($request, $exception); + } catch (Throwable $exception) { + return $this->problemDetailsResponseFactory->createResponse( + $request, + StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR, + $exception->getMessage() + ); } } - - public function errorResponse( - array|string $messages = [], - int $status = StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR - ): ResponseInterface { - return new JsonResponse([ - 'error' => [ - 'messages' => is_array($messages) ? $messages : [$messages], - ], - ], $status); - } } diff --git a/src/App/src/Service/ErrorReportService.php b/src/App/src/Service/ErrorReportService.php index 746b3426..bb3d2a20 100644 --- a/src/App/src/Service/ErrorReportService.php +++ b/src/App/src/Service/ErrorReportService.php @@ -4,9 +4,9 @@ namespace Api\App\Service; -use Core\App\Exception\ForbiddenException; -use Core\App\Exception\RuntimeException; -use Core\App\Exception\UnauthorizedException; +use Api\App\Exception\ForbiddenException; +use Api\App\Exception\RuntimeException; +use Api\App\Exception\UnauthorizedException; use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ServerRequestInterface; @@ -28,6 +28,7 @@ class ErrorReportService implements ErrorReportServiceInterface { private const HEADER_NAME = 'Error-Reporting-Token'; + private Filesystem $fileSystem; private ?string $token = null; @@ -63,7 +64,7 @@ public function checkRequest(ServerRequestInterface $request): self $this->validateToken($request); if (! $this->isMatchingDomain($request) && ! $this->isMatchingIpAddress($request)) { - throw new ForbiddenException(Message::ERROR_REPORT_NOT_ALLOWED); + throw ForbiddenException::create(Message::ERROR_REPORT_NOT_ALLOWED); } return $this; @@ -75,18 +76,21 @@ public function generateToken(): string } /** - * @throws UnauthorizedException * @throws ForbiddenException + * @throws UnauthorizedException */ private function validateToken(ServerRequestInterface $request): void { $this->token = $request->getHeaderLine(self::HEADER_NAME); if (empty($this->token)) { - throw new UnauthorizedException(Message::ERROR_REPORT_NOT_ALLOWED); + throw UnauthorizedException::create( + Message::ERROR_REPORT_NOT_ALLOWED, + $this->config['documentation_url'] ?? '' + ); } if (! in_array($this->token, $this->config['tokens'])) { - throw new ForbiddenException(Message::ERROR_REPORT_NOT_ALLOWED); + throw ForbiddenException::create(Message::ERROR_REPORT_NOT_ALLOWED); } } @@ -114,54 +118,48 @@ private function isMatchingIpAddress(ServerRequestInterface $request): bool private function validateConfigs(): void { if (! array_key_exists('enabled', $this->config)) { - throw new RuntimeException( - sprintf(Message::MISSING_CONFIG, 'config.ErrorReportServiceInterface::class.enabled') + throw RuntimeException::create( + Message::missingConfig('config.ErrorReportServiceInterface::class.enabled') ); } if ($this->config['enabled'] !== true) { - throw new RuntimeException(Message::ERROR_REPORT_NOT_ENABLED); + throw RuntimeException::create(Message::ERROR_REPORT_NOT_ENABLED); } if (! array_key_exists('path', $this->config)) { - throw new RuntimeException( - sprintf(Message::MISSING_CONFIG, 'config.ErrorReportServiceInterface::class.path') + throw RuntimeException::create( + Message::missingConfig('config.ErrorReportServiceInterface::class.path') ); } if (empty($this->config['path'])) { - throw new RuntimeException( - sprintf(Message::INVALID_CONFIG, 'config.ErrorReportServiceInterface::class.path') + throw RuntimeException::create( + Message::invalidConfig('config.ErrorReportServiceInterface::class.path') ); } if (! array_key_exists('tokens', $this->config)) { - throw new RuntimeException( - sprintf(Message::MISSING_CONFIG, 'config.ErrorReportServiceInterface::class.tokens') + throw RuntimeException::create( + Message::missingConfig('config.ErrorReportServiceInterface::class.tokens') ); } if (empty($this->config['tokens'])) { - throw new RuntimeException( - sprintf(Message::INVALID_CONFIG, 'config.ErrorReportServiceInterface::class.tokens') + throw RuntimeException::create( + Message::invalidConfig('config.ErrorReportServiceInterface::class.tokens') ); } if (! array_key_exists('domain_whitelist', $this->config)) { - throw new RuntimeException( - sprintf( - Message::MISSING_CONFIG, - sprintf('config.%s.domain_whitelist', ErrorReportServiceInterface::class) - ) + throw RuntimeException::create( + Message::missingConfig('config.ErrorReportServiceInterface::class.domain_whitelist') ); } if (! array_key_exists('ip_whitelist', $this->config)) { - throw new RuntimeException( - sprintf( - Message::MISSING_CONFIG, - sprintf('config.%s.ip_whitelist', ErrorReportServiceInterface::class) - ) + throw RuntimeException::create( + Message::missingConfig('config.ErrorReportServiceInterface::class.ip_whitelist') ); } } diff --git a/src/App/src/Service/ErrorReportServiceInterface.php b/src/App/src/Service/ErrorReportServiceInterface.php index 8b8ec5d6..f6eb7865 100644 --- a/src/App/src/Service/ErrorReportServiceInterface.php +++ b/src/App/src/Service/ErrorReportServiceInterface.php @@ -4,9 +4,9 @@ namespace Api\App\Service; -use Core\App\Exception\ForbiddenException; -use Core\App\Exception\RuntimeException; -use Core\App\Exception\UnauthorizedException; +use Api\App\Exception\ForbiddenException; +use Api\App\Exception\RuntimeException; +use Api\App\Exception\UnauthorizedException; use Psr\Http\Message\ServerRequestInterface; use Symfony\Component\Filesystem\Exception\IOException; diff --git a/src/Core/src/App/src/Entity/TimestampsTrait.php b/src/Core/src/App/src/Entity/TimestampsTrait.php index e08bd2a7..0b6fa12a 100644 --- a/src/Core/src/App/src/Entity/TimestampsTrait.php +++ b/src/Core/src/App/src/Entity/TimestampsTrait.php @@ -7,7 +7,6 @@ use DateTimeImmutable; use Doctrine\ORM\Mapping as ORM; -#[ORM\HasLifecycleCallbacks] trait TimestampsTrait { #[ORM\Column(name: 'created', type: 'datetime_immutable')] diff --git a/src/Core/src/App/src/Exception/BadRequestException.php b/src/Core/src/App/src/Exception/BadRequestException.php deleted file mode 100644 index a001ae9c..00000000 --- a/src/Core/src/App/src/Exception/BadRequestException.php +++ /dev/null @@ -1,24 +0,0 @@ -messages; - } - - public function setMessages(array $messages): static - { - $this->messages = $messages; - - return $this; - } -} diff --git a/src/Core/src/App/src/Exception/ConflictException.php b/src/Core/src/App/src/Exception/ConflictException.php deleted file mode 100644 index 09044a2d..00000000 --- a/src/Core/src/App/src/Exception/ConflictException.php +++ /dev/null @@ -1,11 +0,0 @@ -getAttribute(User::class); if (! $user->hasAvatar()) { - throw new NotFoundException(Message::USER_AVATAR_MISSING); + throw NotFoundException::create(Message::USER_AVATAR_MISSING); } $this->userAvatarService->deleteAvatar($user); diff --git a/src/User/src/Handler/Account/Avatar/GetUserAccountAvatarHandler.php b/src/User/src/Handler/Account/Avatar/GetUserAccountAvatarHandler.php index a749c29b..a1c81a04 100644 --- a/src/User/src/Handler/Account/Avatar/GetUserAccountAvatarHandler.php +++ b/src/User/src/Handler/Account/Avatar/GetUserAccountAvatarHandler.php @@ -4,9 +4,9 @@ namespace Api\User\Handler\Account\Avatar; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\Service\UserAvatarServiceInterface; -use Core\App\Exception\NotFoundException; use Core\App\Message; use Core\User\Entity\User; use Dot\DependencyInjection\Attribute\Inject; @@ -30,7 +30,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $user = $request->getAttribute(User::class); if (! $user->hasAvatar()) { - throw new NotFoundException(Message::USER_AVATAR_MISSING); + throw NotFoundException::create(Message::USER_AVATAR_MISSING); } return $this->createResponse($request, $user->getAvatar()); diff --git a/src/User/src/Handler/Account/Avatar/PostUserAccountAvatarHandler.php b/src/User/src/Handler/Account/Avatar/PostUserAccountAvatarHandler.php index 9b55aa5e..78589945 100644 --- a/src/User/src/Handler/Account/Avatar/PostUserAccountAvatarHandler.php +++ b/src/User/src/Handler/Account/Avatar/PostUserAccountAvatarHandler.php @@ -4,10 +4,11 @@ namespace Api\User\Handler\Account\Avatar; +use Api\App\Exception\BadRequestException; use Api\App\Handler\AbstractHandler; use Api\User\InputFilter\UpdateAvatarInputFilter; use Api\User\Service\UserAvatarServiceInterface; -use Core\App\Exception\BadRequestException; +use Core\App\Message; use Core\User\Entity\User; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; @@ -32,7 +33,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData($request->getUploadedFiles()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } return $this->createdResponse( diff --git a/src/User/src/Handler/Account/PatchUserAccountActivateHandler.php b/src/User/src/Handler/Account/PatchUserAccountActivateHandler.php index 0685802e..81269954 100644 --- a/src/User/src/Handler/Account/PatchUserAccountActivateHandler.php +++ b/src/User/src/Handler/Account/PatchUserAccountActivateHandler.php @@ -4,10 +4,10 @@ namespace Api\User\Handler\Account; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; @@ -31,7 +31,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $user = $this->userService->findOneBy(['hash' => $request->getAttribute('hash')]); if ($user->isActive()) { - throw new ConflictException(Message::USER_ALREADY_ACTIVATED); + throw ConflictException::create(Message::USER_ALREADY_ACTIVATED); } $this->userService->activateUser($user); diff --git a/src/User/src/Handler/Account/PatchUserAccountResourceHandler.php b/src/User/src/Handler/Account/PatchUserAccountResourceHandler.php index dd9b39b2..c49f9564 100644 --- a/src/User/src/Handler/Account/PatchUserAccountResourceHandler.php +++ b/src/User/src/Handler/Account/PatchUserAccountResourceHandler.php @@ -4,12 +4,13 @@ namespace Api\User\Handler\Account; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\InputFilter\UpdateUserInputFilter; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; +use Core\App\Message; use Core\User\Entity\User; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; @@ -38,7 +39,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface ->setValidationGroup(['password', 'passwordConfirm', 'detail']) ->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } return $this->createResponse( diff --git a/src/User/src/Handler/Account/PostUserAccountActivateHandler.php b/src/User/src/Handler/Account/PostUserAccountActivateHandler.php index 1f521e26..d5f61db4 100644 --- a/src/User/src/Handler/Account/PostUserAccountActivateHandler.php +++ b/src/User/src/Handler/Account/PostUserAccountActivateHandler.php @@ -4,12 +4,12 @@ namespace Api\User\Handler\Account; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\InputFilter\ActivateAccountInputFilter; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; use Core\App\Message; use Core\App\Service\MailService; use Dot\DependencyInjection\Attribute\Inject; @@ -42,12 +42,15 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } $user = $this->userService->findByEmail($this->inputFilter->getValue('email')); if ($user->isActive()) { - throw new ConflictException(Message::USER_ALREADY_ACTIVATED); + throw ConflictException::create(Message::USER_ALREADY_ACTIVATED); } $this->userService->activateUser($user); diff --git a/src/User/src/Handler/Account/PostUserAccountRecoverHandler.php b/src/User/src/Handler/Account/PostUserAccountRecoverHandler.php index 0fbe5265..545fb634 100644 --- a/src/User/src/Handler/Account/PostUserAccountRecoverHandler.php +++ b/src/User/src/Handler/Account/PostUserAccountRecoverHandler.php @@ -4,11 +4,11 @@ namespace Api\User\Handler\Account; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\InputFilter\RecoverIdentityInputFilter; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\NotFoundException; use Core\App\Message; use Core\App\Service\MailService; use Dot\DependencyInjection\Attribute\Inject; @@ -39,7 +39,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } $user = $this->userService->findByEmail($this->inputFilter->getValue('email')); diff --git a/src/User/src/Handler/Account/PostUserAccountResourceHandler.php b/src/User/src/Handler/Account/PostUserAccountResourceHandler.php index 29409dcf..91487d8b 100644 --- a/src/User/src/Handler/Account/PostUserAccountResourceHandler.php +++ b/src/User/src/Handler/Account/PostUserAccountResourceHandler.php @@ -4,12 +4,13 @@ namespace Api\User\Handler\Account; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\InputFilter\CreateUserInputFilter; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; +use Core\App\Message; use Core\App\Service\MailService; use Dot\DependencyInjection\Attribute\Inject; use Dot\Mail\Exception\MailException; @@ -42,7 +43,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface ->setValidationGroup(['identity', 'password', 'passwordConfirm', 'detail']) ->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } $user = $this->userService->saveUser((array) $this->inputFilter->getValues()); diff --git a/src/User/src/Handler/Account/ResetPassword/GetUserAccountResetPasswordHandler.php b/src/User/src/Handler/Account/ResetPassword/GetUserAccountResetPasswordHandler.php index c1a95be5..c010fe40 100644 --- a/src/User/src/Handler/Account/ResetPassword/GetUserAccountResetPasswordHandler.php +++ b/src/User/src/Handler/Account/ResetPassword/GetUserAccountResetPasswordHandler.php @@ -4,10 +4,10 @@ namespace Api\User\Handler\Account\ResetPassword; +use Api\App\Exception\ExpiredException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\Service\UserResetPasswordServiceInterface; -use Core\App\Exception\ExpiredException; -use Core\App\Exception\NotFoundException; use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; @@ -33,10 +33,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface $userResetPassword = $this->userResetPasswordService->findOneBy(['hash' => $hash]); if (! $userResetPassword->isValid()) { - throw new ExpiredException(Message::RESET_PASSWORD_EXPIRED); + throw ExpiredException::create(Message::RESET_PASSWORD_EXPIRED); } if ($userResetPassword->isCompleted()) { - throw new ExpiredException(Message::RESET_PASSWORD_USED); + throw ExpiredException::create(Message::RESET_PASSWORD_USED); } return $this->infoResponse(Message::RESET_PASSWORD_VALID); diff --git a/src/User/src/Handler/Account/ResetPassword/PatchUserAccountResetPasswordHandler.php b/src/User/src/Handler/Account/ResetPassword/PatchUserAccountResetPasswordHandler.php index 301226ea..ea151e72 100644 --- a/src/User/src/Handler/Account/ResetPassword/PatchUserAccountResetPasswordHandler.php +++ b/src/User/src/Handler/Account/ResetPassword/PatchUserAccountResetPasswordHandler.php @@ -4,14 +4,14 @@ namespace Api\User\Handler\Account\ResetPassword; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\ExpiredException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\InputFilter\UpdatePasswordInputFilter; use Api\User\Service\UserResetPasswordServiceInterface; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\ExpiredException; -use Core\App\Exception\NotFoundException; use Core\App\Message; use Core\App\Service\MailService; use Dot\DependencyInjection\Attribute\Inject; @@ -46,17 +46,20 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } $hash = $request->getAttribute('hash'); $userResetPassword = $this->userResetPasswordService->findOneBy(['hash' => $hash]); if (! $userResetPassword->isValid()) { - throw new ExpiredException(Message::RESET_PASSWORD_EXPIRED); + throw ExpiredException::create(Message::RESET_PASSWORD_EXPIRED); } if ($userResetPassword->isCompleted()) { - throw new ConflictException(Message::RESET_PASSWORD_USED); + throw ConflictException::create(Message::RESET_PASSWORD_USED); } $this->userService->saveUser( diff --git a/src/User/src/Handler/Account/ResetPassword/PostUserAccountResetPasswordHandler.php b/src/User/src/Handler/Account/ResetPassword/PostUserAccountResetPasswordHandler.php index b7fe7a18..2455379c 100644 --- a/src/User/src/Handler/Account/ResetPassword/PostUserAccountResetPasswordHandler.php +++ b/src/User/src/Handler/Account/ResetPassword/PostUserAccountResetPasswordHandler.php @@ -4,12 +4,12 @@ namespace Api\User\Handler\Account\ResetPassword; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\InputFilter\ResetPasswordInputFilter; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; use Core\App\Message; use Core\App\Service\MailService; use Dot\DependencyInjection\Attribute\Inject; @@ -42,7 +42,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } if (! empty($this->inputFilter->getValue('email'))) { @@ -50,7 +53,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface } elseif (! empty($this->inputFilter->getValue('identity'))) { $user = $this->userService->findByIdentity($this->inputFilter->getValue('identity')); } else { - throw new NotFoundException(Message::USER_NOT_FOUND); + throw NotFoundException::create(Message::USER_NOT_FOUND); } $this->userService->saveUser([], $user->createResetPassword()); diff --git a/src/User/src/Handler/User/Avatar/DeleteUserAvatarResourceHandler.php b/src/User/src/Handler/User/Avatar/DeleteUserAvatarResourceHandler.php index 564eb56b..7a345b59 100644 --- a/src/User/src/Handler/User/Avatar/DeleteUserAvatarResourceHandler.php +++ b/src/User/src/Handler/User/Avatar/DeleteUserAvatarResourceHandler.php @@ -4,10 +4,10 @@ namespace Api\User\Handler\User\Avatar; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\Service\UserAvatarServiceInterface; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\NotFoundException; use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; @@ -32,7 +32,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $user = $this->userService->findUser($request->getAttribute('uuid')); if (! $user->hasAvatar()) { - throw new NotFoundException(Message::USER_AVATAR_MISSING); + throw NotFoundException::create(Message::USER_AVATAR_MISSING); } $this->userAvatarService->deleteAvatar($user); diff --git a/src/User/src/Handler/User/Avatar/GetUserAvatarResourceHandler.php b/src/User/src/Handler/User/Avatar/GetUserAvatarResourceHandler.php index 5d724191..c7006ca2 100644 --- a/src/User/src/Handler/User/Avatar/GetUserAvatarResourceHandler.php +++ b/src/User/src/Handler/User/Avatar/GetUserAvatarResourceHandler.php @@ -4,9 +4,9 @@ namespace Api\User\Handler\User\Avatar; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\NotFoundException; use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; @@ -29,7 +29,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $user = $this->userService->findUser($request->getAttribute('uuid')); if (! $user->hasAvatar()) { - throw new NotFoundException(Message::USER_AVATAR_MISSING); + throw NotFoundException::create(Message::USER_AVATAR_MISSING); } return $this->createResponse($request, $user->getAvatar()); diff --git a/src/User/src/Handler/User/Avatar/PostUserAvatarResourceHandler.php b/src/User/src/Handler/User/Avatar/PostUserAvatarResourceHandler.php index 8d7dbb23..c7cfcc52 100644 --- a/src/User/src/Handler/User/Avatar/PostUserAvatarResourceHandler.php +++ b/src/User/src/Handler/User/Avatar/PostUserAvatarResourceHandler.php @@ -4,12 +4,13 @@ namespace Api\User\Handler\User\Avatar; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\InputFilter\UpdateAvatarInputFilter; use Api\User\Service\UserAvatarServiceInterface; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\NotFoundException; +use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -36,7 +37,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData($request->getUploadedFiles()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } return $this->createdResponse( diff --git a/src/User/src/Handler/User/DeleteUserResourceHandler.php b/src/User/src/Handler/User/DeleteUserResourceHandler.php index 0d235fcc..cf3dd4bc 100644 --- a/src/User/src/Handler/User/DeleteUserResourceHandler.php +++ b/src/User/src/Handler/User/DeleteUserResourceHandler.php @@ -4,10 +4,10 @@ namespace Api\User\Handler\User; +use Api\App\Exception\NotFoundException; +use Api\App\Exception\RuntimeException; use Api\App\Handler\AbstractHandler; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\NotFoundException; -use Core\App\Exception\RuntimeException; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; diff --git a/src/User/src/Handler/User/GetUserResourceHandler.php b/src/User/src/Handler/User/GetUserResourceHandler.php index 9996c89d..4bd381e3 100644 --- a/src/User/src/Handler/User/GetUserResourceHandler.php +++ b/src/User/src/Handler/User/GetUserResourceHandler.php @@ -4,9 +4,9 @@ namespace Api\User\Handler\User; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\NotFoundException; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; diff --git a/src/User/src/Handler/User/PatchUserActivateHandler.php b/src/User/src/Handler/User/PatchUserActivateHandler.php index a0e6d3de..8c70db1d 100644 --- a/src/User/src/Handler/User/PatchUserActivateHandler.php +++ b/src/User/src/Handler/User/PatchUserActivateHandler.php @@ -4,10 +4,10 @@ namespace Api\User\Handler\User; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; use Core\App\Message; use Core\App\Service\MailService; use Dot\DependencyInjection\Attribute\Inject; @@ -36,7 +36,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $user = $this->userService->findUser($request->getAttribute('uuid')); if ($user->isActive()) { - throw new ConflictException(Message::USER_ALREADY_ACTIVATED); + throw ConflictException::create(Message::USER_ALREADY_ACTIVATED); } $this->userService->activateUser($user); diff --git a/src/User/src/Handler/User/PatchUserDeactivateHandler.php b/src/User/src/Handler/User/PatchUserDeactivateHandler.php index 4854f628..836c6c50 100644 --- a/src/User/src/Handler/User/PatchUserDeactivateHandler.php +++ b/src/User/src/Handler/User/PatchUserDeactivateHandler.php @@ -4,10 +4,10 @@ namespace Api\User\Handler\User; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; @@ -31,7 +31,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $user = $this->userService->findUser($request->getAttribute('uuid')); if ($user->isPending()) { - throw new ConflictException(Message::USER_ALREADY_DEACTIVATED); + throw ConflictException::create(Message::USER_ALREADY_DEACTIVATED); } $this->userService->deactivateUser($user); diff --git a/src/User/src/Handler/User/PatchUserResourceHandler.php b/src/User/src/Handler/User/PatchUserResourceHandler.php index 856fa599..6961d9fe 100644 --- a/src/User/src/Handler/User/PatchUserResourceHandler.php +++ b/src/User/src/Handler/User/PatchUserResourceHandler.php @@ -4,12 +4,13 @@ namespace Api\User\Handler\User; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\InputFilter\UpdateUserInputFilter; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; +use Core\App\Message; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -35,7 +36,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } return $this->createResponse( diff --git a/src/User/src/Handler/User/PostUserResourceHandler.php b/src/User/src/Handler/User/PostUserResourceHandler.php index 46f1e059..944fc355 100644 --- a/src/User/src/Handler/User/PostUserResourceHandler.php +++ b/src/User/src/Handler/User/PostUserResourceHandler.php @@ -4,12 +4,13 @@ namespace Api\User\Handler\User; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\InputFilter\CreateUserInputFilter; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; +use Core\App\Message; use Core\App\Service\MailService; use Dot\DependencyInjection\Attribute\Inject; use Dot\Mail\Exception\MailException; @@ -40,7 +41,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $this->inputFilter->setData((array) $request->getParsedBody()); if (! $this->inputFilter->isValid()) { - throw (new BadRequestException())->setMessages($this->inputFilter->getMessages()); + throw BadRequestException::create( + detail: Message::VALIDATOR_INVALID_DATA, + additional: ['errors' => $this->inputFilter->getMessages()] + ); } $user = $this->userService->saveUser((array) $this->inputFilter->getValues()); diff --git a/src/User/src/Handler/User/Role/GetUserRoleResourceHandler.php b/src/User/src/Handler/User/Role/GetUserRoleResourceHandler.php index 46297ab5..2b01af24 100644 --- a/src/User/src/Handler/User/Role/GetUserRoleResourceHandler.php +++ b/src/User/src/Handler/User/Role/GetUserRoleResourceHandler.php @@ -4,9 +4,9 @@ namespace Api\User\Handler\User\Role; +use Api\App\Exception\NotFoundException; use Api\App\Handler\AbstractHandler; use Api\User\Service\UserRoleServiceInterface; -use Core\App\Exception\NotFoundException; use Dot\DependencyInjection\Attribute\Inject; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; diff --git a/src/User/src/InputFilter/Input/StatusInput.php b/src/User/src/InputFilter/Input/StatusInput.php index d6586e72..9dd755bf 100644 --- a/src/User/src/InputFilter/Input/StatusInput.php +++ b/src/User/src/InputFilter/Input/StatusInput.php @@ -21,12 +21,11 @@ public function __construct(?string $name = null, bool $isRequired = true) $this->getFilterChain() ->attachByName(StringTrim::class) - ->attachByName(StripTags::class) - ->attach(fn($value) => $value === null ? UserStatusEnum::Active : UserStatusEnum::from($value)); + ->attachByName(StripTags::class); $this->getValidatorChain() ->attachByName(InArray::class, [ - 'haystack' => UserStatusEnum::cases(), + 'haystack' => UserStatusEnum::values(), 'message' => Message::invalidValue('status'), ], true); } diff --git a/src/User/src/Service/UserResetPasswordService.php b/src/User/src/Service/UserResetPasswordService.php index 494cb699..38fd8755 100644 --- a/src/User/src/Service/UserResetPasswordService.php +++ b/src/User/src/Service/UserResetPasswordService.php @@ -4,7 +4,7 @@ namespace Api\User\Service; -use Core\App\Exception\NotFoundException; +use Api\App\Exception\NotFoundException; use Core\App\Message; use Core\User\Entity\UserResetPassword; use Core\User\Repository\UserResetPasswordRepository; @@ -32,7 +32,7 @@ public function findOneBy(array $params): UserResetPassword { $userResetPassword = $this->userResetPasswordRepository->findOneBy($params); if (! $userResetPassword instanceof UserResetPassword) { - throw new NotFoundException(Message::RESET_PASSWORD_NOT_FOUND); + throw NotFoundException::create(Message::RESET_PASSWORD_NOT_FOUND); } return $userResetPassword; diff --git a/src/User/src/Service/UserResetPasswordServiceInterface.php b/src/User/src/Service/UserResetPasswordServiceInterface.php index 5c6d4cb2..0a257aaf 100644 --- a/src/User/src/Service/UserResetPasswordServiceInterface.php +++ b/src/User/src/Service/UserResetPasswordServiceInterface.php @@ -4,7 +4,7 @@ namespace Api\User\Service; -use Core\App\Exception\NotFoundException; +use Api\App\Exception\NotFoundException; use Core\User\Entity\UserResetPassword; use Core\User\Repository\UserResetPasswordRepository; diff --git a/src/User/src/Service/UserRoleService.php b/src/User/src/Service/UserRoleService.php index d6170a34..f395fde2 100644 --- a/src/User/src/Service/UserRoleService.php +++ b/src/User/src/Service/UserRoleService.php @@ -4,7 +4,7 @@ namespace Api\User\Service; -use Core\App\Exception\NotFoundException; +use Api\App\Exception\NotFoundException; use Core\App\Helper\Paginator; use Core\App\Message; use Core\User\Entity\UserRole; @@ -36,7 +36,7 @@ public function findUserRole(string $id): UserRole { $userRole = $this->userRoleRepository->find($id); if (! $userRole instanceof UserRole) { - throw new NotFoundException(Message::ROLE_NOT_FOUND); + throw NotFoundException::create(Message::ROLE_NOT_FOUND); } return $userRole; diff --git a/src/User/src/Service/UserRoleServiceInterface.php b/src/User/src/Service/UserRoleServiceInterface.php index 84fd129e..2a114153 100644 --- a/src/User/src/Service/UserRoleServiceInterface.php +++ b/src/User/src/Service/UserRoleServiceInterface.php @@ -4,7 +4,7 @@ namespace Api\User\Service; -use Core\App\Exception\NotFoundException; +use Api\App\Exception\NotFoundException; use Core\User\Entity\UserRole; use Core\User\Repository\UserRoleRepository; use Doctrine\ORM\QueryBuilder; diff --git a/src/User/src/Service/UserService.php b/src/User/src/Service/UserService.php index 048ba68e..7c08056f 100644 --- a/src/User/src/Service/UserService.php +++ b/src/User/src/Service/UserService.php @@ -4,9 +4,9 @@ namespace Api\User\Service; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Core\App\Helper\Paginator; use Core\App\Message; use Core\Security\Repository\OAuthAccessTokenRepository; @@ -82,12 +82,12 @@ public function findByEmail(string $email): User { $userDetail = $this->userDetailRepository->findOneBy(['email' => $email]); if (! $userDetail instanceof UserDetail) { - throw new NotFoundException(Message::USER_NOT_FOUND); + throw NotFoundException::create(Message::USER_NOT_FOUND); } $user = $userDetail->getUser(); if ($user->isDeleted()) { - throw new NotFoundException(Message::USER_NOT_FOUND); + throw NotFoundException::create(Message::USER_NOT_FOUND); } return $user; @@ -108,7 +108,7 @@ public function findOneBy(array $params): User { $user = $this->userRepository->findOneBy($params); if (! $user instanceof User || $user->isDeleted()) { - throw new NotFoundException(Message::USER_NOT_FOUND); + throw NotFoundException::create(Message::USER_NOT_FOUND); } return $user; @@ -121,7 +121,7 @@ public function findUser(string $id): User { $user = $this->userRepository->find($id); if (! $user instanceof User || $user->isDeleted()) { - throw new NotFoundException(Message::USER_NOT_FOUND); + throw NotFoundException::create(Message::USER_NOT_FOUND); } return $user; @@ -178,7 +178,10 @@ public function saveUser(array $data, ?User $user = null): User $status = UserStatusEnum::tryFrom($status); } if (! $status instanceof UserStatusEnum) { - throw new BadRequestException(Message::invalidValue('status')); + throw BadRequestException::create( + detail: Message::invalidValue('status'), + additional: ['errors' => ['status' => $data['status']]] + ); } $user->setStatus($status); } @@ -204,7 +207,7 @@ public function saveUser(array $data, ?User $user = null): User foreach ($data['roles'] as $roleData) { $userRole = $this->userRoleRepository->find($roleData['uuid']); if (! $userRole instanceof UserRole) { - throw new NotFoundException(Message::ROLE_NOT_FOUND); + throw NotFoundException::create(Message::ROLE_NOT_FOUND); } $user->addRole($userRole); } @@ -260,20 +263,20 @@ private function validateUniqueUser(string $identity, string $email, ?UuidInterf $user = $this->userRepository->findOneBy(['identity' => $identity]); if ($user instanceof User) { if ($uuid === null) { - throw new ConflictException(Message::DUPLICATE_IDENTITY); + throw ConflictException::create(Message::DUPLICATE_IDENTITY); } if ($user->getUuid()->toString() !== $uuid->toString()) { - throw new ConflictException(Message::DUPLICATE_IDENTITY); + throw ConflictException::create(Message::DUPLICATE_IDENTITY); } } $userDetail = $this->userDetailRepository->findOneBy(['email' => $email]); if ($userDetail instanceof UserDetail) { if ($uuid === null) { - throw new ConflictException(Message::DUPLICATE_EMAIL); + throw ConflictException::create(Message::DUPLICATE_EMAIL); } if ($userDetail->getUser()->getUuid()->toString() !== $uuid->toString()) { - throw new ConflictException(Message::DUPLICATE_EMAIL); + throw ConflictException::create(Message::DUPLICATE_EMAIL); } } } diff --git a/src/User/src/Service/UserServiceInterface.php b/src/User/src/Service/UserServiceInterface.php index 4373f8e1..b9ad0891 100644 --- a/src/User/src/Service/UserServiceInterface.php +++ b/src/User/src/Service/UserServiceInterface.php @@ -4,9 +4,9 @@ namespace Api\User\Service; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Core\User\Entity\User; use Core\User\Repository\UserRepository; use Doctrine\ORM\QueryBuilder; diff --git a/test/Functional/AdminTest.php b/test/Functional/AdminTest.php index 5bac840b..e2ea1e02 100644 --- a/test/Functional/AdminTest.php +++ b/test/Functional/AdminTest.php @@ -160,9 +160,8 @@ public function testCannotCreateDuplicateAdminAccount(): void $data = json_decode($response->getBody()->getContents(), true); $this->assertResponseConflict($response); - $this->assertArrayHasKey('error', $data); - $this->assertArrayHasKey('messages', $data['error']); - $this->assertSame(Message::DUPLICATE_IDENTITY, $data['error']['messages'][0]); + $this->assertArrayHasKey('detail', $data); + $this->assertSame(Message::DUPLICATE_IDENTITY, $data['detail']); } /** @@ -357,9 +356,8 @@ public function testAdminCreateUserAccountDuplicateEmail(): void $this->assertResponseConflict($response); $data = json_decode($response->getBody()->getContents(), true); - $this->assertArrayHasKey('error', $data); - $this->assertArrayHasKey('messages', $data['error']); - $this->assertSame(Message::DUPLICATE_EMAIL, $data['error']['messages'][0]); + $this->assertArrayHasKey('detail', $data); + $this->assertSame(Message::DUPLICATE_EMAIL, $data['detail']); } /** @@ -499,9 +497,8 @@ public function testAdminUpdateUserAccountDuplicateEmail(): void $this->assertResponseConflict($response); $data = json_decode($response->getBody()->getContents(), true); - $this->assertArrayHasKey('error', $data); - $this->assertArrayHasKey('messages', $data['error']); - $this->assertSame(Message::DUPLICATE_EMAIL, $data['error']['messages'][0]); + $this->assertArrayHasKey('detail', $data); + $this->assertSame(Message::DUPLICATE_EMAIL, $data['detail']); $userDetailRepository = $this->getEntityManager()->getRepository(UserDetail::class); $userDetail = $userDetailRepository->find($user2->getDetail()->getUuid()); diff --git a/test/Functional/UserTest.php b/test/Functional/UserTest.php index a9bd0dcc..864b5335 100644 --- a/test/Functional/UserTest.php +++ b/test/Functional/UserTest.php @@ -53,10 +53,8 @@ public function testRegisterAccountDuplicateIdentity(): void $data = json_decode($response->getBody()->getContents(), true); - $this->assertArrayHasKey('error', $data); - $this->assertNotEmpty($data['error']); - $this->assertArrayHasKey('messages', $data['error']); - $this->assertContains(Message::DUPLICATE_IDENTITY, $data['error']['messages']); + $this->assertArrayHasKey('detail', $data); + $this->assertSame(Message::DUPLICATE_IDENTITY, $data['detail']); } /** @@ -79,10 +77,8 @@ public function testRegisterAccountDuplicateEmail(): void $data = json_decode($response->getBody()->getContents(), true); - $this->assertArrayHasKey('error', $data); - $this->assertNotEmpty($data['error']); - $this->assertArrayHasKey('messages', $data['error']); - $this->assertContains(Message::DUPLICATE_EMAIL, $data['error']['messages']); + $this->assertArrayHasKey('detail', $data); + $this->assertSame(Message::DUPLICATE_EMAIL, $data['detail']); } /** @@ -345,10 +341,8 @@ public function testRequestResetPasswordExpired(): void $this->assertResponseGone($response); $data = json_decode($response->getBody()->getContents(), true); - $this->assertArrayHasKey('error', $data); - $this->assertArrayHasKey('messages', $data['error']); - $this->assertNotEmpty($data['error']['messages'][0]); - $this->assertSame(Message::RESET_PASSWORD_EXPIRED, $data['error']['messages'][0]); + $this->assertArrayHasKey('detail', $data); + $this->assertSame(Message::RESET_PASSWORD_EXPIRED, $data['detail']); } /** @@ -381,10 +375,8 @@ public function testRequestResetPasswordAlreadyUsed(): void $this->assertResponseConflict($response); $data = json_decode($response->getBody()->getContents(), true); - $this->assertArrayHasKey('error', $data); - $this->assertArrayHasKey('messages', $data['error']); - $this->assertNotEmpty($data['error']['messages'][0]); - $this->assertSame(Message::RESET_PASSWORD_USED, $data['error']['messages'][0]); + $this->assertArrayHasKey('detail', $data); + $this->assertSame(Message::RESET_PASSWORD_USED, $data['detail']); } /** diff --git a/test/Unit/Admin/Service/AdminServiceTest.php b/test/Unit/Admin/Service/AdminServiceTest.php index 43f02f12..2ff770b1 100644 --- a/test/Unit/Admin/Service/AdminServiceTest.php +++ b/test/Unit/Admin/Service/AdminServiceTest.php @@ -6,14 +6,13 @@ use Api\Admin\Service\AdminService; use Api\Admin\Service\AdminServiceInterface; +use Api\App\Exception\ConflictException; use Core\Admin\Entity\Admin; use Core\Admin\Entity\AdminRole; use Core\Admin\Enum\AdminRoleEnum; use Core\Admin\Enum\AdminStatusEnum; use Core\Admin\Repository\AdminRepository; use Core\Admin\Repository\AdminRoleRepository; -use Core\App\Exception\ConflictException; -use Core\App\Message; use Exception; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -43,7 +42,6 @@ public function setUp(): void public function testCreateAdminThrowsDuplicateIdentity(): void { $this->expectException(ConflictException::class); - $this->expectExceptionMessage(Message::DUPLICATE_IDENTITY); $request = $this->getAdmin(); $this->adminRepository diff --git a/test/Unit/App/Attribute/MethodDeprecationTest.php b/test/Unit/App/Attribute/MethodDeprecationTest.php index 7050b3e2..13321bbc 100644 --- a/test/Unit/App/Attribute/MethodDeprecationTest.php +++ b/test/Unit/App/Attribute/MethodDeprecationTest.php @@ -5,8 +5,8 @@ namespace ApiTest\Unit\App\Attribute; use Api\App\Attribute\MethodDeprecation; +use Api\App\Exception\SunsetException; use Api\App\Middleware\DeprecationMiddleware; -use Core\App\Exception\DeprecationSunsetException; use PHPUnit\Framework\TestCase; use ReflectionClass; @@ -28,7 +28,7 @@ public function test(): void $reflectionClass = new ReflectionClass($class); $attributes = $this->getAttributes($reflectionClass); - $this->expectException(DeprecationSunsetException::class); + $this->expectException(SunsetException::class); $attributes[0]->newInstance(); } diff --git a/test/Unit/App/Attribute/ResourceDeprecationTest.php b/test/Unit/App/Attribute/ResourceDeprecationTest.php index aed76d49..f89e7e04 100644 --- a/test/Unit/App/Attribute/ResourceDeprecationTest.php +++ b/test/Unit/App/Attribute/ResourceDeprecationTest.php @@ -5,8 +5,8 @@ namespace ApiTest\Unit\App\Attribute; use Api\App\Attribute\ResourceDeprecation; +use Api\App\Exception\SunsetException; use Api\App\Middleware\DeprecationMiddleware; -use Core\App\Exception\DeprecationSunsetException; use PHPUnit\Framework\TestCase; use ReflectionClass; @@ -24,7 +24,7 @@ public function testInvalidDateThrowsException(): void $reflectionClass = new ReflectionClass($class); $attributes = $reflectionClass->getAttributes(ResourceDeprecation::class); - $this->expectException(DeprecationSunsetException::class); + $this->expectException(SunsetException::class); $attributes[0]->newInstance(); } diff --git a/test/Unit/App/Middleware/AuthenticationMiddlewareTest.php b/test/Unit/App/Middleware/AuthenticationMiddlewareTest.php index dcddcc26..620ccbdd 100644 --- a/test/Unit/App/Middleware/AuthenticationMiddlewareTest.php +++ b/test/Unit/App/Middleware/AuthenticationMiddlewareTest.php @@ -19,10 +19,10 @@ class AuthenticationMiddlewareTest extends TestCase { private AuthenticationMiddleware $authenticationMiddleware; - private AuthenticationInterface|MockObject $auth; + private AuthenticationInterface&MockObject $auth; private ServerRequestInterface $request; - private RequestHandlerInterface|MockObject $handler; - private ResponseInterface|MockObject $response; + private RequestHandlerInterface&MockObject $handler; + private ResponseInterface&MockObject $response; /** * @throws Exception diff --git a/test/Unit/App/Middleware/AuthorizationMiddlewareTest.php b/test/Unit/App/Middleware/AuthorizationMiddlewareTest.php index 4e665f39..40f8896c 100644 --- a/test/Unit/App/Middleware/AuthorizationMiddlewareTest.php +++ b/test/Unit/App/Middleware/AuthorizationMiddlewareTest.php @@ -33,11 +33,11 @@ class AuthorizationMiddlewareTest extends TestCase { private AuthorizationMiddleware $authorizationMiddleware; - private UserRepository|MockObject $userRepository; - private AdminRepository|MockObject $adminRepository; - private AuthorizationInterface|MockObject $authorization; + private UserRepository&MockObject $userRepository; + private AdminRepository&MockObject $adminRepository; + private AuthorizationInterface&MockObject $authorization; private ServerRequestInterface $request; - private RequestHandlerInterface|MockObject $handler; + private RequestHandlerInterface&MockObject $handler; private ResponseInterface $response; /** diff --git a/test/Unit/App/Middleware/DeprecationMiddlewareTest.php b/test/Unit/App/Middleware/DeprecationMiddlewareTest.php index 2ba181d1..7d5e647a 100644 --- a/test/Unit/App/Middleware/DeprecationMiddlewareTest.php +++ b/test/Unit/App/Middleware/DeprecationMiddlewareTest.php @@ -6,9 +6,8 @@ use Api\App\Attribute\MethodDeprecation; use Api\App\Attribute\ResourceDeprecation; +use Api\App\Exception\ConflictException; use Api\App\Middleware\DeprecationMiddleware; -use Core\App\Exception\DeprecationConflictException; -use Core\App\Message; use Fig\Http\Message\RequestMethodInterface; use Laminas\Diactoros\Response\EmptyResponse; use Laminas\Stratigility\MiddlewarePipe; @@ -32,8 +31,8 @@ class DeprecationMiddlewareTest extends TestCase { private DeprecationMiddleware $deprecationMiddleware; - private ServerRequestInterface|MockObject $request; - private RequestHandlerInterface|MockObject $handler; + private ServerRequestInterface&MockObject $request; + private RequestHandlerInterface&MockObject $handler; private ResponseInterface $response; private const VERSIONING_CONFIG = [ @@ -87,18 +86,14 @@ public function handle(ServerRequestInterface $request): ResponseInterface $this->request->method('getAttribute')->with(RouteResult::class)->willReturn($routeResult); $this->handler->method('handle')->with($this->request)->willReturn($this->response); - $this->expectException(DeprecationConflictException::class); - $this->expectExceptionMessage(sprintf( - Message::RESTRICTION_DEPRECATION, - DeprecationMiddleware::RESOURCE_DEPRECATION_ATTRIBUTE, - DeprecationMiddleware::METHOD_DEPRECATION_ATTRIBUTE - )); + $this->expectException(ConflictException::class); $this->deprecationMiddleware->process($this->request, $this->handler); } /** * @throws Exception + * @throws ConflictException * @throws ReflectionException */ public function testLazyLoadingMiddleware(): void diff --git a/test/Unit/User/Service/UserServiceTest.php b/test/Unit/User/Service/UserServiceTest.php index 67da141d..e5d1ed9c 100644 --- a/test/Unit/User/Service/UserServiceTest.php +++ b/test/Unit/User/Service/UserServiceTest.php @@ -4,11 +4,11 @@ namespace ApiTest\Unit\User\Service; +use Api\App\Exception\BadRequestException; +use Api\App\Exception\ConflictException; +use Api\App\Exception\NotFoundException; use Api\User\Service\UserService; use Api\User\Service\UserServiceInterface; -use Core\App\Exception\BadRequestException; -use Core\App\Exception\ConflictException; -use Core\App\Exception\NotFoundException; use Core\Security\Repository\OAuthAccessTokenRepository; use Core\Security\Repository\OAuthRefreshTokenRepository; use Core\User\Entity\User;