From 097834ff11fd7809cd4d6699e1f3d0489afb2834 Mon Sep 17 00:00:00 2001 From: alexmerlin Date: Fri, 18 Aug 2023 17:44:21 +0300 Subject: [PATCH] Code quality related changes. Signed-off-by: alexmerlin --- .github/workflows/codecov.yml | 61 ++++++ .github/workflows/cs-tests.yml | 46 +++++ .github/workflows/run-tests.yml | 2 +- .github/workflows/static-analysis.yml | 4 +- .gitignore | 1 + README.md | 7 +- bin/clear-config-cache.php | 5 +- composer.json | 33 +-- config/autoload/authorization.global.php | 22 +- config/autoload/cli.global.php | 16 +- config/autoload/cors.local.php.dist | 14 +- config/autoload/dependencies.global.php | 34 ++-- config/autoload/development.local.php.dist | 4 +- config/autoload/doctrine.global.php | 40 ++-- config/autoload/error-handling.global.php | 21 +- config/autoload/local.php.dist | 70 +++---- config/autoload/local.test.php.dist | 5 +- config/autoload/mail.local.php.dist | 136 ++++--------- .../mezzio-tooling-factories.global.php | 1 + config/autoload/mezzio.global.php | 5 +- config/autoload/response-header.global.php | 4 +- config/autoload/templates.global.php | 26 +-- config/container.php | 2 +- config/development.config.php.dist | 3 +- config/migrations.php | 14 +- config/pipeline.php | 5 +- config/routes.php | 2 +- phpcs.xml | 23 +++ phpcs.xml.dist | 20 -- phpunit.xml | 5 +- psalm-baseline.xml | 30 +++ psalm.xml | 14 +- public/index.php | 7 +- src/Admin/src/Collection/AdminCollection.php | 3 +- .../src/Collection/AdminRoleCollection.php | 3 +- src/Admin/src/Command/AdminCreateCommand.php | 29 +-- src/Admin/src/ConfigProvider.php | 18 +- src/Admin/src/Entity/Admin.php | 48 ++--- src/Admin/src/Entity/AdminRole.php | 12 +- src/Admin/src/Handler/AdminAccountHandler.php | 5 +- src/Admin/src/Handler/AdminHandler.php | 21 +- src/Admin/src/Handler/AdminRoleHandler.php | 9 +- .../src/InputFilter/AdminRoleInputFilter.php | 3 + .../InputFilter/CreateAdminInputFilter.php | 3 + .../src/InputFilter/Input/FirstNameInput.php | 4 +- .../src/InputFilter/Input/IdentityInput.php | 4 +- .../src/InputFilter/Input/LastNameInput.php | 4 +- .../Input/PasswordConfirmInput.php | 4 +- .../src/InputFilter/Input/PasswordInput.php | 6 +- .../src/InputFilter/Input/StatusInput.php | 6 +- src/Admin/src/InputFilter/Input/UuidInput.php | 2 +- .../InputFilter/UpdateAdminInputFilter.php | 6 +- src/Admin/src/Repository/AdminRepository.php | 5 +- .../src/Repository/AdminRoleRepository.php | 7 +- src/Admin/src/RoutesDelegator.php | 27 ++- src/Admin/src/Service/AdminRoleService.php | 3 +- src/Admin/src/Service/AdminService.php | 17 +- .../src/Collection/CollectionInterface.php | 3 +- src/App/src/Collection/ResourceCollection.php | 6 +- src/App/src/Command/RouteListCommand.php | 26 ++- src/App/src/Command/TokenGenerateCommand.php | 19 +- src/App/src/ConfigProvider.php | 69 ++++--- src/App/src/Entity/AbstractEntity.php | 15 +- src/App/src/Entity/EntityListenerResolver.php | 4 +- src/App/src/Entity/OAuthAccessToken.php | 37 ++-- src/App/src/Entity/OAuthAuthCode.php | 23 +-- src/App/src/Entity/OAuthClient.php | 24 +-- src/App/src/Entity/OAuthRefreshToken.php | 16 +- src/App/src/Entity/OAuthScope.php | 26 +-- src/App/src/Entity/PasswordTrait.php | 5 + .../src/Entity/TimestampAwareInterface.php | 4 - src/App/src/Entity/TimestampAwareTrait.php | 11 +- src/App/src/Entity/UuidAwareTrait.php | 4 +- .../src/Entity/UuidOrderedTimeGenerator.php | 6 +- .../AuthenticationMiddlewareFactory.php | 2 +- .../Factory/ErrorResponseGeneratorFactory.php | 2 +- .../Factory/OAuthScopeRepositoryFactory.php | 2 - src/App/src/Handler/ErrorReportHandler.php | 5 +- src/App/src/Handler/ResponseTrait.php | 12 +- src/App/src/Helper/PaginationHelper.php | 8 +- src/App/src/Message.php | 94 ++++----- .../Middleware/AuthenticationMiddleware.php | 2 +- .../Middleware/AuthorizationMiddleware.php | 29 +-- .../Middleware/ErrorResponseMiddleware.php | 10 +- .../Repository/OAuthAccessTokenRepository.php | 17 +- .../Repository/OAuthAuthCodeRepository.php | 9 +- .../src/Repository/OAuthClientRepository.php | 27 ++- .../OAuthRefreshTokenRepository.php | 9 + .../src/Repository/OAuthScopeRepository.php | 11 + src/App/src/RoutesDelegator.php | 44 ++-- src/App/src/Service/ErrorReportService.php | 44 ++-- src/App/src/UserIdentity.php | 31 +-- src/User/src/Collection/UserCollection.php | 3 +- .../src/Collection/UserRoleCollection.php | 3 +- src/User/src/ConfigProvider.php | 46 +++-- src/User/src/Entity/User.php | 73 +++---- src/User/src/Entity/UserAvatar.php | 16 +- src/User/src/Entity/UserDetail.php | 22 +- .../src/Entity/UserResetPasswordEntity.php | 32 +-- src/User/src/Entity/UserRole.php | 12 +- .../EventListener/UserAvatarEventListener.php | 6 +- .../src/Handler/AccountActivateHandler.php | 13 +- src/User/src/Handler/AccountAvatarHandler.php | 9 +- src/User/src/Handler/AccountHandler.php | 9 +- .../src/Handler/AccountRecoveryHandler.php | 9 +- .../Handler/AccountResetPasswordHandler.php | 27 +-- src/User/src/Handler/UserActivateHandler.php | 11 +- src/User/src/Handler/UserAvatarHandler.php | 13 +- src/User/src/Handler/UserHandler.php | 17 +- src/User/src/Handler/UserRoleHandler.php | 9 +- .../ActivateAccountInputFilter.php | 3 + .../CreateUserDetailInputFilter.php | 3 + .../src/InputFilter/CreateUserInputFilter.php | 3 + .../src/InputFilter/Input/AvatarInput.php | 6 +- src/User/src/InputFilter/Input/EmailInput.php | 4 +- .../src/InputFilter/Input/FirstNameInput.php | 2 +- .../src/InputFilter/Input/IdentityInput.php | 4 +- .../src/InputFilter/Input/LastNameInput.php | 2 +- .../Input/PasswordConfirmInput.php | 4 +- .../src/InputFilter/Input/PasswordInput.php | 6 +- .../src/InputFilter/Input/StatusInput.php | 6 +- src/User/src/InputFilter/Input/UuidInput.php | 2 +- .../RecoverIdentityInputFilter.php | 3 + .../InputFilter/ResetPasswordInputFilter.php | 3 + .../InputFilter/UpdateAvatarInputFilter.php | 3 + .../InputFilter/UpdatePasswordInputFilter.php | 3 + .../UpdateUserDetailInputFilter.php | 3 + .../src/InputFilter/UpdateUserInputFilter.php | 3 + .../src/InputFilter/UserRoleInputFilter.php | 3 + .../src/Repository/UserAvatarRepository.php | 1 + .../src/Repository/UserDetailRepository.php | 4 +- src/User/src/Repository/UserRepository.php | 35 ++-- .../src/Repository/UserRoleRepository.php | 3 +- src/User/src/RoutesDelegator.php | 72 ++++--- src/User/src/Service/UserAvatarService.php | 18 +- src/User/src/Service/UserRoleService.php | 3 +- src/User/src/Service/UserService.php | 50 +++-- src/User/src/Service/UserServiceInterface.php | 4 - .../Functional}/AbstractFunctionalTest.php | 191 ++++++++---------- .../src => test/Functional}/AdminTest.php | 173 ++++++++-------- .../Functional}/AuthenticationTest.php | 34 ++-- .../Exception/AuthenticationException.php | 6 +- .../Traits/AuthenticationTrait.php | 39 ++-- .../Functional}/Traits/DatabaseTrait.php | 11 +- .../src => test/Functional}/UserTest.php | 187 +++++++++-------- .../Unit/AdminServiceTest.php | 28 ++- .../Unit/AuthenticationMiddlewareTest.php | 21 +- .../Unit/AuthorizationMiddlewareTest.php | 93 ++++----- .../AppTest => test}/Unit/UserAvatarTest.php | 46 ++--- .../AppTest => test}/Unit/UserServiceTest.php | 76 ++++--- 150 files changed, 1644 insertions(+), 1434 deletions(-) create mode 100644 .github/workflows/codecov.yml create mode 100644 .github/workflows/cs-tests.yml create mode 100644 phpcs.xml delete mode 100644 phpcs.xml.dist create mode 100644 psalm-baseline.xml rename {tests/AppTest/Functional/src => test/Functional}/AbstractFunctionalTest.php (75%) rename {tests/AppTest/Functional/src => test/Functional}/AdminTest.php (77%) rename {tests/AppTest/Functional/src => test/Functional}/AuthenticationTest.php (90%) rename {tests/AppTest/Functional/src => test/Functional}/Exception/AuthenticationException.php (82%) rename {tests/AppTest/Functional/src => test/Functional}/Traits/AuthenticationTrait.php (81%) rename {tests/AppTest/Functional/src => test/Functional}/Traits/DatabaseTrait.php (84%) rename {tests/AppTest/Functional/src => test/Functional}/UserTest.php (81%) rename {tests/AppTest => test}/Unit/AdminServiceTest.php (82%) rename {tests/AppTest => test}/Unit/AuthenticationMiddlewareTest.php (76%) rename {tests/AppTest => test}/Unit/AuthorizationMiddlewareTest.php (74%) rename {tests/AppTest => test}/Unit/UserAvatarTest.php (69%) rename {tests/AppTest => test}/Unit/UserServiceTest.php (80%) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 0000000..56e0638 --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,61 @@ +on: + - push + +name: Run Codecov checks + +jobs: + code-coverage: + name: Code Coverage + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: + - ubuntu-latest + + php: + - "8.1" + - "8.2" + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: pcov + ini-values: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On + tools: composer:v2, cs2pr + + - name: Determine composer cache directory + run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV + + - name: Cache dependencies installed with composer + uses: actions/cache@v3 + with: + path: ${{ env.COMPOSER_CACHE_DIR }} + key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: | + php${{ matrix.php }}-composer- + + - name: Install dependencies with composer + run: composer install --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + - name: Setup project + run: | + mv config/autoload/local.php.dist config/autoload/local.php + mv config/autoload/mail.local.php.dist config/autoload/mail.local.php + mv config/autoload/local.test.php.dist config/autoload/local.test.php + + - name: Run unit tests + run: vendor/bin/phpunit --testsuite=UnitTests --testdox --colors=always --coverage-clover clover.xml + - name: Run functional tests + run: vendor/bin/phpunit --testsuite=FunctionalTests --testdox --colors=always --coverage-clover clover.xml + + - name: Send code coverage report to Codecov.io + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/cs-tests.yml b/.github/workflows/cs-tests.yml new file mode 100644 index 0000000..3da9965 --- /dev/null +++ b/.github/workflows/cs-tests.yml @@ -0,0 +1,46 @@ +on: + - push + +name: Run phpcs checks + +jobs: + mutation: + name: PHP ${{ matrix.php }}-${{ matrix.os }} + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: + - ubuntu-latest + + php: + - "8.1" + - "8.2" + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + tools: composer:v2, cs2pr + coverage: none + + - name: Determine composer cache directory + run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV + + - name: Cache dependencies installed with composer + uses: actions/cache@v3 + with: + path: ${{ env.COMPOSER_CACHE_DIR }} + key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: | + php${{ matrix.php }}-composer- + - name: Install dependencies with composer + run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + - name: Run phpcs checks + run: vendor/bin/phpcs diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index b79941d..39ee42c 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,5 +1,4 @@ on: -# - pull_request - push name: Run PHPUnit tests @@ -17,6 +16,7 @@ jobs: php: - "8.1" + - "8.2" steps: - name: Checkout diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 3c6b971..b863b6d 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -1,8 +1,7 @@ on: -# - pull_request - push -name: static analysis +name: Run static analysis jobs: mutation: @@ -17,6 +16,7 @@ jobs: php: - "8.1" + - "8.2" steps: - name: Checkout diff --git a/.gitignore b/.gitignore index e6afb97..d4e57a3 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ composer.phar composer.lock .phpunit.result.cache +.phpcs-cache diff --git a/README.md b/README.md index d7a0686..28bbf3d 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,16 @@ Based on Enrico Zimuel's [Zend Expressive API - Skeleton example](https://github.com/ezimuel/zend-expressive-api), DotKernel API runs on [Laminas](https://github.com/laminas) and [Mezzio](https://github.com/mezzio) components and implements standards like PSR-3, PSR-4, PSR-7, PSR-11 and PSR-15. ![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/api) +![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/4.2.0) [![GitHub issues](https://img.shields.io/github/issues/dotkernel/api)](https://github.com/dotkernel/api/issues) [![GitHub forks](https://img.shields.io/github/forks/dotkernel/api)](https://github.com/dotkernel/api/network) [![GitHub stars](https://img.shields.io/github/stars/dotkernel/api)](https://github.com/dotkernel/api/stargazers) [![GitHub license](https://img.shields.io/github/license/dotkernel/api)](https://github.com/dotkernel/api/blob/4.0/LICENSE.md) -![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/4.0.x-dev) - [![Build Static](https://github.com/dotkernel/api/actions/workflows/static-analysis.yml/badge.svg?branch=4.0)](https://github.com/dotkernel/api/actions/workflows/static-analysis.yml) [![Build Static](https://github.com/dotkernel/api/actions/workflows/run-tests.yml/badge.svg?branch=4.0)](https://github.com/dotkernel/api/actions/workflows/run-tests.yml) +[![codecov](https://codecov.io/gh/dotkernel/api/graph/badge.svg?token=53FN78G5CK)](https://codecov.io/gh/dotkernel/api) [![SymfonyInsight](https://insight.symfony.com/projects/7f9143cc-5e3c-4cfc-992c-377a001fde3e/big.svg)](https://insight.symfony.com/projects/7f9143cc-5e3c-4cfc-992c-377a001fde3e) @@ -65,9 +65,6 @@ composer development-status * 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: -* duplicate `phpcs.xml.dist` as `phpcs.xml` - ### Step 5: Setup database diff --git a/bin/clear-config-cache.php b/bin/clear-config-cache.php index 2fcf36b..9e3a7af 100644 --- a/bin/clear-config-cache.php +++ b/bin/clear-config-cache.php @@ -1,12 +1,9 @@ [ - 'roles' => [ + 'roles' => [ AdminRole::ROLE_SUPERUSER => [], - AdminRole::ROLE_ADMIN => [AdminRole::ROLE_SUPERUSER], - UserRole::ROLE_GUEST => [UserRole::ROLE_USER] + AdminRole::ROLE_ADMIN => [ + AdminRole::ROLE_SUPERUSER, + ], + UserRole::ROLE_GUEST => [ + UserRole::ROLE_USER, + ], ], 'permissions' => [ AdminRole::ROLE_SUPERUSER => [], - AdminRole::ROLE_ADMIN => [ + AdminRole::ROLE_ADMIN => [ 'admin.my-account.update', 'admin.my-account.view', 'admin.create', @@ -49,7 +53,7 @@ 'error.report', 'home', ], - UserRole::ROLE_USER => [ + UserRole::ROLE_USER => [ 'user.my-account.delete', 'user.my-account.update', 'user.my-account.view', @@ -57,7 +61,7 @@ 'user.my-avatar.delete', 'user.my-avatar.view', ], - UserRole::ROLE_GUEST => [ + UserRole::ROLE_GUEST => [ 'account.activate.request', 'account.activate', 'account.register', @@ -70,7 +74,7 @@ 'error.report', 'home', 'user.create', - ] + ], ], - ] + ], ]; diff --git a/config/autoload/cli.global.php b/config/autoload/cli.global.php index 8f3cddf..1f88428 100644 --- a/config/autoload/cli.global.php +++ b/config/autoload/cli.global.php @@ -9,18 +9,18 @@ use Dot\Cli\FileLockerInterface; return [ - 'dot_cli' => [ - 'version' => '1.0.0', - 'name' => 'DotKernel CLI', + 'dot_cli' => [ + 'version' => '1.0.0', + 'name' => 'DotKernel CLI', 'commands' => [ - DemoCommand::getDefaultName() => DemoCommand::class, - RouteListCommand::getDefaultName() => RouteListCommand::class, - AdminCreateCommand::getDefaultName() => AdminCreateCommand::class, + DemoCommand::getDefaultName() => DemoCommand::class, + RouteListCommand::getDefaultName() => RouteListCommand::class, + AdminCreateCommand::getDefaultName() => AdminCreateCommand::class, TokenGenerateCommand::getDefaultName() => TokenGenerateCommand::class, - ] + ], ], FileLockerInterface::class => [ 'enabled' => true, 'dirPath' => getcwd() . '/data/lock', - ] + ], ]; diff --git a/config/autoload/cors.local.php.dist b/config/autoload/cors.local.php.dist index a252337..e789151 100644 --- a/config/autoload/cors.local.php.dist +++ b/config/autoload/cors.local.php.dist @@ -6,18 +6,22 @@ use Mezzio\Cors\Configuration\ConfigurationInterface; return [ ConfigurationInterface::CONFIGURATION_IDENTIFIER => [ - 'allowed_origins' => [ + 'allowed_origins' => [ /** * Leaving this line here makes your application accessible by any origin. * * To restrict, replace this line with a list of origins that should have access to your application. * Example: "domain1.com", "domain2.com" */ - ConfigurationInterface::ANY_ORIGIN + ConfigurationInterface::ANY_ORIGIN, ], - 'allowed_headers' => ['Accept', 'Content-Type', 'Authorization'], // Custom headers - 'allowed_max_age' => '600', // 10 minutes + 'allowed_headers' => [ + 'Accept', + 'Content-Type', + 'Authorization', + ], // Custom headers + 'allowed_max_age' => '600', // 10 minutes 'credentials_allowed' => true, // Allow cookies - 'exposed_headers' => [], // Tell client that the API will always return this header + 'exposed_headers' => [], // Tell client that the API will always return this header ], ]; diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index 27f200b..e856f63 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -36,15 +36,15 @@ // Use 'aliases' to alias a service name to another service. The // key is the alias name, the value is the service to which it points. 'aliases' => [ - AccessTokenRepositoryInterface::class => OAuthAccessTokenRepository::class, - AuthCodeRepositoryInterface::class => OAuthAuthCodeRepository::class, - ClientRepositoryInterface::class => OAuthClientRepository::class, - RefreshTokenRepositoryInterface::class => OAuthRefreshTokenRepository::class, - ScopeRepositoryInterface::class => OAuthScopeRepository::class, - ErrorHandlerInterface::class => LogErrorHandler::class, - Mezzio\Authentication\UserInterface::class => UserIdentity::class, + AccessTokenRepositoryInterface::class => OAuthAccessTokenRepository::class, + AuthCodeRepositoryInterface::class => OAuthAuthCodeRepository::class, + ClientRepositoryInterface::class => OAuthClientRepository::class, + RefreshTokenRepositoryInterface::class => OAuthRefreshTokenRepository::class, + ScopeRepositoryInterface::class => OAuthScopeRepository::class, + ErrorHandlerInterface::class => LogErrorHandler::class, + Mezzio\Authentication\UserInterface::class => UserIdentity::class, Mezzio\Authorization\AuthorizationInterface::class => Mezzio\Authorization\Rbac\LaminasRbac::class, - UserRepositoryInterface::class => UserRepository::class, + UserRepositoryInterface::class => UserRepository::class, ], // Use 'invokables' for constructor-less services, or services that do // not require arguments to the constructor. Map a service name to the @@ -53,16 +53,16 @@ // Fully\Qualified\InterfaceName::class => Fully\Qualified\ClassName::class, ], // Use 'factories' for services provided by callbacks/factory classes. - 'factories' => [ - ExecuteCommand::class => CommandFactory::class, + 'factories' => [ + ExecuteCommand::class => CommandFactory::class, Mezzio\Middleware\ErrorResponseGenerator::class => ErrorResponseGeneratorFactory::class, - OAuthAccessTokenRepository::class => OAuthAccessTokenRepositoryFactory::class, - OAuthAuthCodeRepository::class => OAuthAuthCodeRepositoryFactory::class, - OAuthClientRepository::class => OAuthClientRepositoryFactory::class, - OAuthRefreshTokenRepository::class => OAuthRefreshTokenRepositoryFactory::class, - OAuthScopeRepository::class => OAuthScopeRepositoryFactory::class, - UserRepository::class => UserRepositoryFactory::class, - UserIdentity::class => UserIdentityFactory::class, + OAuthAccessTokenRepository::class => OAuthAccessTokenRepositoryFactory::class, + OAuthAuthCodeRepository::class => OAuthAuthCodeRepositoryFactory::class, + OAuthClientRepository::class => OAuthClientRepositoryFactory::class, + OAuthRefreshTokenRepository::class => OAuthRefreshTokenRepositoryFactory::class, + OAuthScopeRepository::class => OAuthScopeRepositoryFactory::class, + UserRepository::class => UserRepositoryFactory::class, + UserIdentity::class => UserIdentityFactory::class, ], ], ]; diff --git a/config/autoload/development.local.php.dist b/config/autoload/development.local.php.dist index 2110c4a..edd3efb 100644 --- a/config/autoload/development.local.php.dist +++ b/config/autoload/development.local.php.dist @@ -1,4 +1,5 @@ [ + 'doctrine' => [ 'connection' => [ 'orm_default' => [ 'doctrine_mapping_types' => [ - UuidBinaryType::NAME => 'binary', + UuidBinaryType::NAME => 'binary', UuidBinaryOrderedTimeType::NAME => 'binary', - ] - ] + ], + ], ], - 'driver' => [ - 'orm_default' => [ - 'class' => MappingDriverChain::class, + 'driver' => [ + 'orm_default' => [ + 'class' => MappingDriverChain::class, 'drivers' => [ - 'Api\\User\\Entity' => 'UserEntities', + 'Api\\User\\Entity' => 'UserEntities', 'Api\\Admin\\Entity' => 'AdminEntities', - 'Api\\App\Entity' => 'AppEntities', - ] + 'Api\\App\Entity' => 'AppEntities', + ], ], 'AdminEntities' => [ 'class' => AnnotationDriver::class, 'cache' => 'array', 'paths' => __DIR__ . '/../../src/Admin/src/Entity', ], - 'UserEntities' => [ + 'UserEntities' => [ 'class' => AnnotationDriver::class, 'cache' => 'array', 'paths' => __DIR__ . '/../../src/User/src/Entity', ], - 'AppEntities' => [ + 'AppEntities' => [ 'class' => AnnotationDriver::class, 'cache' => 'array', 'paths' => __DIR__ . '/../../src/App/src/Entity', - ] + ], ], - 'types' => [ - UuidType::NAME => UuidType::class, - UuidBinaryType::NAME => UuidBinaryType::class, + 'types' => [ + UuidType::NAME => UuidType::class, + UuidBinaryType::NAME => UuidBinaryType::class, UuidBinaryOrderedTimeType::NAME => UuidBinaryOrderedTimeType::class, ], - 'cache' => [ + 'cache' => [ PhpFileCache::class => [ - 'class' => PhpFileCache::class, + 'class' => PhpFileCache::class, 'directory' => getcwd() . '/data/cache/doctrine', - ] + ], ], - 'fixtures' => getcwd() . '/data/doctrine/fixtures', + 'fixtures' => getcwd() . '/data/doctrine/fixtures', ], 'resultCacheLifetime' => 3600, ]; diff --git a/config/autoload/error-handling.global.php b/config/autoload/error-handling.global.php index 48dfe8c..77425f7 100644 --- a/config/autoload/error-handling.global.php +++ b/config/autoload/error-handling.global.php @@ -3,27 +3,26 @@ declare(strict_types=1); use Api\App\Service\ErrorReportServiceInterface; -use Laminas\Log\Logger; use Laminas\Log\Formatter\Json; +use Laminas\Log\Logger; return [ 'dot-errorhandler' => [ 'loggerEnabled' => true, - 'logger' => 'dot-log.default_logger' + 'logger' => 'dot-log.default_logger', ], - 'dot_log' => [ + 'dot_log' => [ 'loggers' => [ 'default_logger' => [ 'writers' => [ 'FileWriter' => [ - 'name' => 'stream', + 'name' => 'stream', 'priority' => Logger::ALERT, - 'options' => [ - 'stream' => __DIR__ . '/../../log/error-log-{Y}-{m}-{d}.log', - // explicitly log all messages - 'filters' => [ + 'options' => [ + 'stream' => __DIR__ . '/../../log/error-log-{Y}-{m}-{d}.log', + 'filters' => [ 'allMessages' => [ - 'name' => 'priority', + 'name' => 'priority', 'options' => [ 'operator' => '>=', 'priority' => Logger::EMERG, @@ -61,7 +60,7 @@ 'path' => __DIR__ . '/../../log/error-report-endpoint-log.log', /** - * In order to be eligible for storing messages, Requests sent to the error reporting endpoint, must contain a header having: + * In order to store messages, requests sent to the error reporting endpoint, must contain a header having: * - name: the value of \Api\App\Service\ErrorReportService::HEADER_NAME * - value: one of the items in this array */ @@ -82,5 +81,5 @@ * 3. If you want to whitelist only specific IP addresses, add them to ip_whitelist. */ 'ip_whitelist' => [], - ] + ], ]; diff --git a/config/autoload/local.php.dist b/config/autoload/local.php.dist index ea97e06..854ff23 100644 --- a/config/autoload/local.php.dist +++ b/config/autoload/local.php.dist @@ -1,10 +1,4 @@ [ - 'host' => 'localhost', - 'dbname' => '', - 'user' => '', + 'host' => 'localhost', + 'dbname' => '', + 'user' => '', 'password' => '', - 'port' => 3306, - 'driver' => 'pdo_mysql', - 'charset' => 'utf8mb4', - 'collate' => 'utf8mb4_general_ci' - ] + 'port' => 3306, + 'driver' => 'pdo_mysql', + 'charset' => 'utf8mb4', + 'collate' => 'utf8mb4_general_ci', + ], // you can add more database connections into this array ]; return [ 'annotations_cache_dir' => getcwd() . '/data/cache/annotations', - 'application' => [ + 'application' => [ 'name' => 'DotKernel API', - 'url' => $baseUrl + 'url' => $baseUrl, ], - 'authentication' => [ - 'private_key' => [ - 'key_or_path' => getcwd() . '/data/oauth/private.key', - 'key_permissions_check' => false + 'authentication' => [ + 'private_key' => [ + 'key_or_path' => getcwd() . '/data/oauth/private.key', + 'key_permissions_check' => false, ], - 'public_key' => [ - 'key_or_path' => getcwd() . '/data/oauth/public.key', - 'key_permissions_check' => false + 'public_key' => [ + 'key_or_path' => getcwd() . '/data/oauth/public.key', + 'key_permissions_check' => false, ], - 'encryption_key' => require getcwd() . '/data/oauth/encryption.key', + 'encryption_key' => require getcwd() . '/data/oauth/encryption.key', 'access_token_expire' => 'P1D', 'refresh_token_expire' => 'P1M', - 'auth_code_expire' => 'PT10M', - 'invalid_credentials' => [ - 'error' => 'Invalid credentials.', + 'auth_code_expire' => 'PT10M', + 'invalid_credentials' => [ + 'error' => 'Invalid credentials.', 'error_description' => 'Invalid credentials.', - 'message' => 'Invalid credentials.', - ] + 'message' => 'Invalid credentials.', + ], ], - 'databases' => $databases, - 'doctrine' => [ - 'connection' => [ + 'databases' => $databases, + 'doctrine' => [ + 'connection' => [ 'orm_default' => [ 'params' => $databases['default'], - ] + ], ], 'configuration' => [ 'orm_default' => [ // it is recommended to disable doctrine cache on development // just comment any type of cache you don't want to be applied on development - 'query_cache' => PhpFileCache::class, - 'metadata_cache' => PhpFileCache::class, - 'result_cache' => PhpFileCache::class, + 'query_cache' => PhpFileCache::class, + 'metadata_cache' => PhpFileCache::class, + 'result_cache' => PhpFileCache::class, 'entity_listener_resolver' => EntityListenerResolver::class, ], ], ], - 'uploads' => [ + 'uploads' => [ 'user' => [ - 'url' => $baseUrl . '/uploads/user', + 'url' => $baseUrl . '/uploads/user', 'path' => realpath(__DIR__ . '/../../public/uploads/user'), ], ], diff --git a/config/autoload/local.test.php.dist b/config/autoload/local.test.php.dist index f18c364..6d0081e 100644 --- a/config/autoload/local.test.php.dist +++ b/config/autoload/local.test.php.dist @@ -1,4 +1,5 @@ [ - //the key is the mail service name, this is the default one, which does not extend any configuration +// the key is the mail service name, this is the default one, which does not extend any configuration 'default' => [ - //tells which other mail service configuration to extend +// tells which other mail service configuration to extend 'extends' => null, - /** * the mail transport to use * can be any class implementing Laminas\Mail\Transport\TransportInterface @@ -30,111 +28,65 @@ return [ * * defaults to sendmail **/ - 'transport' => Smtp::class, - - // Uncomment the below line if you want to save a copy of all sent emails to a certain IMAP folder - // Valid only if the Transport is SMTP - // 'save_sent_message_folder' => ['INBOX.Sent'], - - //message configuration + /** + * Uncomment the below line if you want to save a copy of all sent emails to a certain IMAP folder. + * Valid only if the Transport is SMTP + */ +// 'save_sent_message_folder' => ['INBOX.Sent'], +// message configuration 'message_options' => [ - - //from email address of the email - 'from' => '', - - //from name to be displayed instead of from address - 'from_name' => 'DotKernel', - - //reply-to email address of the email - 'reply_to' => '', - - //replyTo name to be displayed instead of the address + 'from' => '', + 'from_name' => 'DotKernel', + 'reply_to' => '', 'reply_to_name' => '', - - //destination email address as string or a list of email addresses - 'to' => [], - - //copy destination addresses - 'cc' => [], - - //hidden copy destination addresses - 'bcc' => [], - - //email subject - 'subject' => '', - - //body options - content can be plain text, HTML - 'body' => [ + 'to' => [], + 'cc' => [], + 'bcc' => [], + 'subject' => '', + 'body' => [ 'content' => '', - 'charset' => 'utf-8', ], - - //attachments config - 'attachments' => [ + 'attachments' => [ 'files' => [], - - 'dir' => [ - 'iterate' => false, - 'path' => 'data/mail/attachments', + 'dir' => [ + 'iterate' => false, + 'path' => 'data/mail/attachments', 'recursive' => false, - ] + ], ], ], - - //options that will be used only if Laminas\Mail\Transport\Smtp adapter is used +// options that will be used only if Laminas\Mail\Transport\Smtp adapter is used 'smtp_options' => [ - - //hostname or IP address of the mail server - 'host' => 'smtp.gmail.com', - - //port of the mail server - 587 or 465 for secure connections - 'port' => 587, - - //connection class used for authentication - //the value can be one of smtp, plain, login or crammd5 - 'connection_class' => 'login', - + 'host' => 'smtp.gmail.com', + 'port' => 587, + 'connection_class' => 'login', 'connection_config' => [ - - //the smtp authentication identity 'username' => '', - - //the smtp authentication credential 'password' => '', - - //the encryption type to be used, ssl or tls - //null should be used to disable SSL - 'ssl' => 'tls', - ] + 'ssl' => 'tls', + ], ], - - //file options that will be used only if the adapter is Laminas\Mail\Transport\File - /*'file_options' => [ - - //this is the folder where the file is going to be saved - //default value is 'data/mail/output' - 'path' => 'data/mail/output', - - //a callable that will get the Laminas\Mail\Transport\File object as an argument and should return the filename - //if null is used, and empty callable will be used - //'callback' => null, - ],*/ - - //listeners to register with the mail service, for mail events +// file options that will be used only if the adapter is Laminas\Mail\Transport\File +// 'file_options' => [ +// 'path' => 'data/mail/output', +// //a callable that will get the Laminas\Mail\Transport\File object as an argument +// //and should return the filename if null is used, and empty callable will be used +// 'callback' => null, +// ], +// listeners to register with the mail service, for mail events 'event_listeners' => [ - //[ - //'type' => 'service or class name', - //'priority' => 1 - //], +// [ +// 'type' => 'service or class name', +// 'priority' => 1, +// ], ], ], - // option to log the SENT emails +// option to log the SENT emails 'log' => [ - 'sent' => getcwd() . '/log/mail/sent.log' + 'sent' => getcwd() . '/log/mail/sent.log', ], - /** * You can define other mail services here, with the same structure as the default block * you can even extend from the default block, and overwrite only the differences diff --git a/config/autoload/mezzio-tooling-factories.global.php b/config/autoload/mezzio-tooling-factories.global.php index 8953122..a81187f 100644 --- a/config/autoload/mezzio-tooling-factories.global.php +++ b/config/autoload/mezzio-tooling-factories.global.php @@ -1,4 +1,5 @@ true, // Enable debugging; typically used to provide debugging information within templates. - 'debug' => false, - + 'debug' => false, 'mezzio' => [ // Provide templates for the error handling middleware to use when // generating responses. 'error_handler' => [ - 'template_404' => 'error::404', + 'template_404' => 'error::404', 'template_error' => 'error::error', ], ], diff --git a/config/autoload/response-header.global.php b/config/autoload/response-header.global.php index 5573d2d..2381758 100644 --- a/config/autoload/response-header.global.php +++ b/config/autoload/response-header.global.php @@ -9,7 +9,7 @@ */ '*' => [ 'permissions-policy' => [ - 'value' => 'interest-cohort=()', + 'value' => 'interest-cohort=()', 'overwrite' => true, ], ], @@ -23,5 +23,5 @@ // 'overwrite' => true, // ] // ], - ] + ], ]; diff --git a/config/autoload/templates.global.php b/config/autoload/templates.global.php index 273789a..411d20f 100644 --- a/config/autoload/templates.global.php +++ b/config/autoload/templates.global.php @@ -3,20 +3,20 @@ declare(strict_types=1); return [ - 'debug' => false, + 'debug' => false, 'templates' => [ 'extension' => 'html.twig', ], - 'twig' => [ - 'assets_url' => '/', - 'assets_version' => null, - 'autoescape' => 'html', - 'auto_reload' => true, - 'cache_dir' => 'data/cache/twig', - 'extensions' => [], - 'globals' => [], - 'optimizations' => -1, - 'runtime_loaders' => [], -// 'timezone' => '', - ] + 'twig' => [ + 'assets_url' => '/', + 'assets_version' => null, + 'autoescape' => 'html', + 'auto_reload' => true, + 'cache_dir' => 'data/cache/twig', + 'extensions' => [], + 'globals' => [], + 'optimizations' => -1, + 'runtime_loaders' => [], +// 'timezone' => '', + ], ]; diff --git a/config/container.php b/config/container.php index 570c6c4..16a9e05 100644 --- a/config/container.php +++ b/config/container.php @@ -7,7 +7,7 @@ // Load configuration $config = require 'config/config.php'; -$dependencies = $config['dependencies']; +$dependencies = $config['dependencies']; $dependencies['services']['config'] = $config; // Build container diff --git a/config/development.config.php.dist b/config/development.config.php.dist index 348da3b..94b06d2 100644 --- a/config/development.config.php.dist +++ b/config/development.config.php.dist @@ -1,4 +1,5 @@ true, + 'debug' => true, ConfigAggregator::ENABLE_CACHE => false, ]; diff --git a/config/migrations.php b/config/migrations.php index c264a63..2588890 100644 --- a/config/migrations.php +++ b/config/migrations.php @@ -3,16 +3,16 @@ declare(strict_types=1); return [ - 'table_storage' => [ - 'table_name' => 'doctrine_migration_versions', - 'version_column_name' => 'version', - 'version_column_length' => 1024, - 'executed_at_column_name' => 'executed_at', + 'table_storage' => [ + 'table_name' => 'doctrine_migration_versions', + 'version_column_name' => 'version', + 'version_column_length' => 1024, + 'executed_at_column_name' => 'executed_at', 'execution_time_column_name' => 'execution_time', ], - 'migrations_paths' => [ + 'migrations_paths' => [ 'Api\Migrations' => 'data/doctrine/migrations', ], - 'all_or_nothing' => true, + 'all_or_nothing' => true, 'check_database_platform' => true, ]; diff --git a/config/pipeline.php b/config/pipeline.php index 27cc60a..358e320 100644 --- a/config/pipeline.php +++ b/config/pipeline.php @@ -7,8 +7,8 @@ use Api\App\Middleware\AuthorizationMiddleware; use Dot\ErrorHandler\ErrorHandlerInterface; use Dot\ResponseHeader\Middleware\ResponseHeaderMiddleware; -use Mezzio\Cors\Middleware\CorsMiddleware; use Mezzio\Application; +use Mezzio\Cors\Middleware\CorsMiddleware; use Mezzio\Helper\BodyParams\BodyParamsMiddleware; use Mezzio\Helper\ServerUrlMiddleware; use Mezzio\Helper\UrlHelperMiddleware; @@ -18,9 +18,6 @@ use Mezzio\Router\Middleware\MethodNotAllowedMiddleware; use Mezzio\Router\Middleware\RouteMiddleware; -/** - * Setup middleware pipeline: - */ return function (Application $app): void { // The error handler should be the first (most outer) middleware to catch // all Exceptions. diff --git a/config/routes.php b/config/routes.php index da4dd38..406bcdc 100644 --- a/config/routes.php +++ b/config/routes.php @@ -2,9 +2,9 @@ declare(strict_types=1); -use Psr\Container\ContainerInterface; use Mezzio\Application; use Mezzio\MiddlewareFactory; +use Psr\Container\ContainerInterface; return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container): void { }; diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..fa6e19d --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + bin + config + public + src + test + + + + diff --git a/phpcs.xml.dist b/phpcs.xml.dist deleted file mode 100644 index e66f5f5..0000000 --- a/phpcs.xml.dist +++ /dev/null @@ -1,20 +0,0 @@ - - - Mezzio Skeleton coding standard - - - - - - - - - - - - - - - - src - diff --git a/phpunit.xml b/phpunit.xml index 888f62e..6ac1fbc 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -5,10 +5,11 @@ colors="true"> - ./tests/AppTest/Unit + ./test/Unit - ./tests/AppTest/Functional + ./test/Functional + ./test/Functional/AbstractFunctionalTest.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml new file mode 100644 index 0000000..9f636fd --- /dev/null +++ b/psalm-baseline.xml @@ -0,0 +1,30 @@ + + + + + getSecret + getSecret + + + + + matching + matching + + + + + orX + + + + + matching + + + + + matching + + + diff --git a/psalm.xml b/psalm.xml index 3e4e3d0..ed4c186 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,11 +1,13 @@ + errorLevel="4" + resolveFromConfigFile="true" + findUnusedCode="false" + findUnusedBaselineEntry="true" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="https://getpsalm.org/schema/config" + xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" + errorBaseline="psalm-baseline.xml"> diff --git a/public/index.php b/public/index.php index b604ce3..d8ace78 100644 --- a/public/index.php +++ b/public/index.php @@ -2,8 +2,9 @@ declare(strict_types=1); -use Psr\Container\ContainerInterface; use Mezzio\Application; +use Mezzio\MiddlewareFactory; +use Psr\Container\ContainerInterface; // Delegate static file requests back to the PHP built-in webserver if (PHP_SAPI === 'cli-server' && $_SERVER['SCRIPT_FILENAME'] !== __FILE__) { @@ -21,8 +22,8 @@ $container = require 'config/container.php'; /** @var Application $app */ - $app = $container->get(Application::class); - $factory = $container->get(\Mezzio\MiddlewareFactory::class); + $app = $container->get(Application::class); + $factory = $container->get(MiddlewareFactory::class); // Execute programmatic/declarative middleware pipeline and routing // configuration statements diff --git a/src/Admin/src/Collection/AdminCollection.php b/src/Admin/src/Collection/AdminCollection.php index 2ab5dcf..f23e56e 100644 --- a/src/Admin/src/Collection/AdminCollection.php +++ b/src/Admin/src/Collection/AdminCollection.php @@ -7,4 +7,5 @@ use Api\App\Collection\ResourceCollection; class AdminCollection extends ResourceCollection -{} +{ +} diff --git a/src/Admin/src/Collection/AdminRoleCollection.php b/src/Admin/src/Collection/AdminRoleCollection.php index 6430031..4a42eab 100644 --- a/src/Admin/src/Collection/AdminRoleCollection.php +++ b/src/Admin/src/Collection/AdminRoleCollection.php @@ -7,4 +7,5 @@ use Api\App\Collection\ResourceCollection; class AdminRoleCollection extends ResourceCollection -{} +{ +} diff --git a/src/Admin/src/Command/AdminCreateCommand.php b/src/Admin/src/Command/AdminCreateCommand.php index afa1544..0d277ac 100644 --- a/src/Admin/src/Command/AdminCreateCommand.php +++ b/src/Admin/src/Command/AdminCreateCommand.php @@ -15,8 +15,14 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use function implode; +use function sprintf; + +use const PHP_EOL; + class AdminCreateCommand extends Command { + /** @var string $defaultName */ protected static $defaultName = 'admin:create'; public function __construct( @@ -37,8 +43,7 @@ protected function configure(): void ->addOption('identity', 'i', InputOption::VALUE_REQUIRED, 'Admin identity') ->addOption('password', 'p', InputOption::VALUE_REQUIRED, 'Admin password') ->addOption('firstName', 'f', InputOption::VALUE_REQUIRED, 'Admin first name') - ->addOption('lastName', 'l', InputOption::VALUE_REQUIRED, 'Admin last name') - ; + ->addOption('lastName', 'l', InputOption::VALUE_REQUIRED, 'Admin last name'); } /** @@ -47,7 +52,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { $inputFilter = (new CreateAdminInputFilter())->setData($this->getData($input)); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { $messages = []; foreach ($inputFilter->getMessages() as $field => $errors) { foreach ($errors as $error) { @@ -71,21 +76,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int private function getData(InputInterface $input): array { $role = $this->adminRoleService->findOneBy(['name' => AdminRole::ROLE_ADMIN]); - if (!($role instanceof AdminRole)) { + if (! $role instanceof AdminRole) { throw new Exception( sprintf(Message::ADMIN_ROLE_MISSING, AdminRole::ROLE_ADMIN) ); } - return [ - 'identity' => $input->getOption('identity'), - 'password' => $input->getOption('password'), + return [ + 'identity' => $input->getOption('identity'), + 'password' => $input->getOption('password'), 'passwordConfirm' => $input->getOption('password'), - 'firstName' => $input->getOption('firstName'), - 'lastName' => $input->getOption('lastName'), - 'roles' => [ - ['uuid' => $role->getUuid()->toString()] - ] + 'firstName' => $input->getOption('firstName'), + 'lastName' => $input->getOption('lastName'), + 'roles' => [ + ['uuid' => $role->getUuid()->toString()], + ], ]; } } diff --git a/src/Admin/src/ConfigProvider.php b/src/Admin/src/ConfigProvider.php index 08e29d8..7ed44e6 100644 --- a/src/Admin/src/ConfigProvider.php +++ b/src/Admin/src/ConfigProvider.php @@ -29,7 +29,7 @@ class ConfigProvider public function __invoke(): array { return [ - 'dependencies' => $this->getDependencies(), + 'dependencies' => $this->getDependencies(), MetadataMap::class => $this->getHalConfig(), ]; } @@ -38,17 +38,17 @@ public function getDependencies(): array { return [ 'factories' => [ - AdminHandler::class => AnnotatedServiceFactory::class, + AdminHandler::class => AnnotatedServiceFactory::class, AdminAccountHandler::class => AnnotatedServiceFactory::class, - AdminRoleHandler::class => AnnotatedServiceFactory::class, - AdminService::class => AnnotatedServiceFactory::class, - AdminRoleService::class => AnnotatedServiceFactory::class, - AdminCreateCommand::class => AdminCreateCommandFactory::class, - AdminRepository::class => AnnotatedRepositoryFactory::class, + AdminRoleHandler::class => AnnotatedServiceFactory::class, + AdminService::class => AnnotatedServiceFactory::class, + AdminRoleService::class => AnnotatedServiceFactory::class, + AdminCreateCommand::class => AdminCreateCommandFactory::class, + AdminRepository::class => AnnotatedRepositoryFactory::class, AdminRoleRepository::class => AnnotatedRepositoryFactory::class, ], - 'aliases' => [ - AdminServiceInterface::class => AdminService::class, + 'aliases' => [ + AdminServiceInterface::class => AdminService::class, AdminRoleServiceInterface::class => AdminRoleService::class, ], ]; diff --git a/src/Admin/src/Entity/Admin.php b/src/Admin/src/Entity/Admin.php index 0575d8c..52499f1 100644 --- a/src/Admin/src/Entity/Admin.php +++ b/src/Admin/src/Entity/Admin.php @@ -14,46 +14,34 @@ use League\OAuth2\Server\Entities\UserEntityInterface; /** - * Class Admin * @ORM\Entity(repositoryClass="Api\Admin\Repository\AdminRepository") * @ORM\Table(name="admin") * @ORM\HasLifecycleCallbacks() - * @package Api\Admin\Entity */ class Admin extends AbstractEntity implements UserEntityInterface { use PasswordTrait; - public const STATUS_ACTIVE = 'active'; + public const STATUS_ACTIVE = 'active'; public const STATUS_INACTIVE = 'inactive'; - public const STATUSES = [ + public const STATUSES = [ self::STATUS_ACTIVE, - self::STATUS_INACTIVE + self::STATUS_INACTIVE, ]; - /** - * @ORM\Column(name="identity", type="string", length=100, unique=true) - */ + /** @ORM\Column(name="identity", type="string", length=100, unique=true) */ protected string $identity; - /** - * @ORM\Column(name="firstName", type="string", length=255) - */ + /** @ORM\Column(name="firstName", type="string", length=255) */ protected string $firstName; - /** - * @ORM\Column(name="lastName", type="string", length=255) - */ + /** @ORM\Column(name="lastName", type="string", length=255) */ protected string $lastName; - /** - * @ORM\Column(name="password", type="string", length=100) - */ + /** @ORM\Column(name="password", type="string", length=100) */ protected string $password; - /** - * @ORM\Column(name="status", type="string", length=20) - */ + /** @ORM\Column(name="status", type="string", length=20) */ protected string $status = self::STATUS_ACTIVE; /** @@ -79,16 +67,16 @@ public function __construct() public function getArrayCopy(): array { return [ - 'uuid' => $this->getUuid()->toString(), - 'identity' => $this->getIdentity(), + 'uuid' => $this->getUuid()->toString(), + 'identity' => $this->getIdentity(), 'firstName' => $this->getFirstName(), - 'lastName' => $this->getLastName(), - 'status' => $this->getStatus(), - 'roles' => $this->getRoles()->map(function (AdminRole $role) { - return $role->getArrayCopy(); + 'lastName' => $this->getLastName(), + 'status' => $this->getStatus(), + 'roles' => $this->getRoles()->map(function (AdminRole $role) { + return $role->getArrayCopy(); })->toArray(), - 'created' => $this->getCreated(), - 'updated' => $this->getUpdated() + 'created' => $this->getCreated(), + 'updated' => $this->getUpdated(), ]; } @@ -166,7 +154,7 @@ public function setRoles(ArrayCollection $roles): self public function addRole(RoleInterface $role): self { - if (!$this->roles->contains($role)) { + if (! $this->roles->contains($role)) { $this->roles->add($role); } @@ -175,7 +163,7 @@ public function addRole(RoleInterface $role): self public function removeRole(RoleInterface $role): self { - if (!$this->roles->contains($role)) { + if (! $this->roles->contains($role)) { $this->roles->removeElement($role); } diff --git a/src/Admin/src/Entity/AdminRole.php b/src/Admin/src/Entity/AdminRole.php index 8422024..52a2965 100644 --- a/src/Admin/src/Entity/AdminRole.php +++ b/src/Admin/src/Entity/AdminRole.php @@ -9,24 +9,20 @@ use Doctrine\ORM\Mapping as ORM; /** - * Class AdminRole * @ORM\Entity(repositoryClass="Api\Admin\Repository\AdminRoleRepository") * @ORM\Table(name="admin_role") * @ORM\HasLifecycleCallbacks() - * @package Api\Admin\Entity */ class AdminRole extends AbstractEntity implements RoleInterface { - public const ROLE_ADMIN = 'admin'; + public const ROLE_ADMIN = 'admin'; public const ROLE_SUPERUSER = 'superuser'; - public const ROLES = [ + public const ROLES = [ self::ROLE_ADMIN, - self::ROLE_SUPERUSER + self::ROLE_SUPERUSER, ]; - /** - * @ORM\Column(name="name", type="string", length=30, unique=true) - */ + /** @ORM\Column(name="name", type="string", length=30, unique=true) */ protected string $name; public function getName(): string diff --git a/src/Admin/src/Handler/AdminAccountHandler.php b/src/Admin/src/Handler/AdminAccountHandler.php index da95b58..a69d1d0 100644 --- a/src/Admin/src/Handler/AdminAccountHandler.php +++ b/src/Admin/src/Handler/AdminAccountHandler.php @@ -31,7 +31,8 @@ public function __construct( protected HalResponseFactory $responseFactory, protected ResourceGenerator $resourceGenerator, protected AdminServiceInterface $adminService - ) {} + ) { + } public function get(ServerRequestInterface $request): ResponseInterface { @@ -44,7 +45,7 @@ public function get(ServerRequestInterface $request): ResponseInterface public function patch(ServerRequestInterface $request): ResponseInterface { $inputFilter = (new UpdateAdminInputFilter())->setData($request->getParsedBody()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } diff --git a/src/Admin/src/Handler/AdminHandler.php b/src/Admin/src/Handler/AdminHandler.php index dfa337e..75f9c36 100644 --- a/src/Admin/src/Handler/AdminHandler.php +++ b/src/Admin/src/Handler/AdminHandler.php @@ -18,6 +18,8 @@ use Psr\Http\Server\RequestHandlerInterface; use Throwable; +use function sprintf; + class AdminHandler implements RequestHandlerInterface { use ResponseTrait; @@ -33,14 +35,15 @@ public function __construct( protected HalResponseFactory $responseFactory, protected ResourceGenerator $resourceGenerator, protected AdminServiceInterface $adminService - ) {} + ) { + } public function delete(ServerRequestInterface $request): ResponseInterface { try { - $uuid = $request->getAttribute('uuid'); + $uuid = $request->getAttribute('uuid'); $admin = $this->adminService->findOneBy(['uuid' => $uuid]); - if (!$admin instanceof Admin) { + if (! $admin instanceof Admin) { return $this->notFoundResponse(sprintf(Message::NOT_FOUND_BY_UUID, 'admin', $uuid)); } @@ -56,7 +59,7 @@ public function get(ServerRequestInterface $request): ResponseInterface { try { $uuid = $request->getAttribute('uuid'); - if (!empty($uuid)) { + if (! empty($uuid)) { return $this->view($request, $uuid); } @@ -69,14 +72,14 @@ public function get(ServerRequestInterface $request): ResponseInterface public function patch(ServerRequestInterface $request): ResponseInterface { $inputFilter = (new UpdateAdminInputFilter())->setData($request->getParsedBody()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } try { - $uuid = $request->getAttribute('uuid'); + $uuid = $request->getAttribute('uuid'); $admin = $this->adminService->findOneBy(['uuid' => $uuid]); - if (!$admin instanceof Admin) { + if (! $admin instanceof Admin) { return $this->notFoundResponse(sprintf(Message::NOT_FOUND_BY_UUID, 'admin', $uuid)); } @@ -91,7 +94,7 @@ public function patch(ServerRequestInterface $request): ResponseInterface public function post(ServerRequestInterface $request): ResponseInterface { $inputFilter = (new CreateAdminInputFilter())->setData($request->getParsedBody()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } @@ -112,7 +115,7 @@ private function list(ServerRequestInterface $request): ResponseInterface private function view(ServerRequestInterface $request, string $uuid): ResponseInterface { $admin = $this->adminService->findOneBy(['uuid' => $uuid]); - if (!$admin instanceof Admin) { + if (! $admin instanceof Admin) { return $this->notFoundResponse(sprintf(Message::NOT_FOUND_BY_UUID, 'admin', $uuid)); } diff --git a/src/Admin/src/Handler/AdminRoleHandler.php b/src/Admin/src/Handler/AdminRoleHandler.php index 0a54c56..9773761 100644 --- a/src/Admin/src/Handler/AdminRoleHandler.php +++ b/src/Admin/src/Handler/AdminRoleHandler.php @@ -16,6 +16,8 @@ use Psr\Http\Server\RequestHandlerInterface; use Throwable; +use function sprintf; + class AdminRoleHandler implements RequestHandlerInterface { use ResponseTrait; @@ -31,13 +33,14 @@ public function __construct( protected HalResponseFactory $responseFactory, protected ResourceGenerator $resourceGenerator, protected AdminRoleServiceInterface $roleService - ) {} + ) { + } public function get(ServerRequestInterface $request): ResponseInterface { try { $uuid = $request->getAttribute('uuid'); - if (!empty($uuid)) { + if (! empty($uuid)) { return $this->view($request, $uuid); } @@ -58,7 +61,7 @@ private function list(ServerRequestInterface $request): ResponseInterface private function view(ServerRequestInterface $request, string $uuid): ResponseInterface { $role = $this->roleService->findOneBy(['uuid' => $uuid]); - if (!$role instanceof AdminRole) { + if (! $role instanceof AdminRole) { return $this->notFoundResponse(sprintf(Message::NOT_FOUND_BY_UUID, 'role', $uuid)); } diff --git a/src/Admin/src/InputFilter/AdminRoleInputFilter.php b/src/Admin/src/InputFilter/AdminRoleInputFilter.php index fe08171..dfa10e4 100644 --- a/src/Admin/src/InputFilter/AdminRoleInputFilter.php +++ b/src/Admin/src/InputFilter/AdminRoleInputFilter.php @@ -7,6 +7,9 @@ use Api\Admin\InputFilter\Input\UuidInput; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class AdminRoleInputFilter extends InputFilter { public function __construct() diff --git a/src/Admin/src/InputFilter/CreateAdminInputFilter.php b/src/Admin/src/InputFilter/CreateAdminInputFilter.php index 44ec4c0..bf69ff1 100644 --- a/src/Admin/src/InputFilter/CreateAdminInputFilter.php +++ b/src/Admin/src/InputFilter/CreateAdminInputFilter.php @@ -13,6 +13,9 @@ use Laminas\InputFilter\CollectionInputFilter; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class CreateAdminInputFilter extends InputFilter { public function __construct() diff --git a/src/Admin/src/InputFilter/Input/FirstNameInput.php b/src/Admin/src/InputFilter/Input/FirstNameInput.php index f5000ab..a865f4e 100644 --- a/src/Admin/src/InputFilter/Input/FirstNameInput.php +++ b/src/Admin/src/InputFilter/Input/FirstNameInput.php @@ -10,9 +10,11 @@ use Laminas\InputFilter\Input; use Laminas\Validator\NotEmpty; +use function sprintf; + class FirstNameInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); diff --git a/src/Admin/src/InputFilter/Input/IdentityInput.php b/src/Admin/src/InputFilter/Input/IdentityInput.php index 51a83e2..82abf6f 100644 --- a/src/Admin/src/InputFilter/Input/IdentityInput.php +++ b/src/Admin/src/InputFilter/Input/IdentityInput.php @@ -10,9 +10,11 @@ use Laminas\InputFilter\Input; use Laminas\Validator\NotEmpty; +use function sprintf; + class IdentityInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); diff --git a/src/Admin/src/InputFilter/Input/LastNameInput.php b/src/Admin/src/InputFilter/Input/LastNameInput.php index 58d7948..4d000fb 100644 --- a/src/Admin/src/InputFilter/Input/LastNameInput.php +++ b/src/Admin/src/InputFilter/Input/LastNameInput.php @@ -10,9 +10,11 @@ use Laminas\InputFilter\Input; use Laminas\Validator\NotEmpty; +use function sprintf; + class LastNameInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); diff --git a/src/Admin/src/InputFilter/Input/PasswordConfirmInput.php b/src/Admin/src/InputFilter/Input/PasswordConfirmInput.php index e3a5b77..1ae9bd1 100644 --- a/src/Admin/src/InputFilter/Input/PasswordConfirmInput.php +++ b/src/Admin/src/InputFilter/Input/PasswordConfirmInput.php @@ -12,7 +12,7 @@ class PasswordConfirmInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); @@ -24,7 +24,7 @@ public function __construct(string $name = null, bool $isRequired = true) $this->getValidatorChain() ->attachByName(Identical::class, [ - 'token' => 'password', + 'token' => 'password', 'message' => Message::VALIDATOR_PASSWORD_MISMATCH, ], true); } diff --git a/src/Admin/src/InputFilter/Input/PasswordInput.php b/src/Admin/src/InputFilter/Input/PasswordInput.php index bb703ba..9bf2501 100644 --- a/src/Admin/src/InputFilter/Input/PasswordInput.php +++ b/src/Admin/src/InputFilter/Input/PasswordInput.php @@ -11,11 +11,13 @@ use Laminas\Validator\NotEmpty; use Laminas\Validator\StringLength; +use function sprintf; + class PasswordInput extends Input { public const PASSWORD_MIN_LENGTH = 6; - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); @@ -27,7 +29,7 @@ public function __construct(string $name = null, bool $isRequired = true) $this->getValidatorChain() ->attachByName(StringLength::class, [ - 'min' => self::PASSWORD_MIN_LENGTH, + 'min' => self::PASSWORD_MIN_LENGTH, 'message' => sprintf(Message::VALIDATOR_MIN_LENGTH, 'Password', self::PASSWORD_MIN_LENGTH), ], true) ->attachByName(NotEmpty::class, [ diff --git a/src/Admin/src/InputFilter/Input/StatusInput.php b/src/Admin/src/InputFilter/Input/StatusInput.php index 848b69a..1393f25 100644 --- a/src/Admin/src/InputFilter/Input/StatusInput.php +++ b/src/Admin/src/InputFilter/Input/StatusInput.php @@ -11,9 +11,11 @@ use Laminas\InputFilter\Input; use Laminas\Validator\InArray; +use function sprintf; + class StatusInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); @@ -26,7 +28,7 @@ public function __construct(string $name = null, bool $isRequired = true) $this->getValidatorChain() ->attachByName(InArray::class, [ 'haystack' => Admin::STATUSES, - 'message' => sprintf(Message::INVALID_VALUE, 'status'), + 'message' => sprintf(Message::INVALID_VALUE, 'status'), ], true); } } diff --git a/src/Admin/src/InputFilter/Input/UuidInput.php b/src/Admin/src/InputFilter/Input/UuidInput.php index 1e80f23..71a5a9d 100644 --- a/src/Admin/src/InputFilter/Input/UuidInput.php +++ b/src/Admin/src/InputFilter/Input/UuidInput.php @@ -10,7 +10,7 @@ class UuidInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); diff --git a/src/Admin/src/InputFilter/UpdateAdminInputFilter.php b/src/Admin/src/InputFilter/UpdateAdminInputFilter.php index 2ca0652..3cb4629 100644 --- a/src/Admin/src/InputFilter/UpdateAdminInputFilter.php +++ b/src/Admin/src/InputFilter/UpdateAdminInputFilter.php @@ -12,6 +12,9 @@ use Laminas\InputFilter\CollectionInputFilter; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class UpdateAdminInputFilter extends InputFilter { public function __construct() @@ -26,7 +29,6 @@ public function __construct() ->add(new FirstNameInput('firstName', false)) ->add(new LastNameInput('lastName', false)) ->add(new StatusInput('status', false)) - ->add($roles, 'roles') - ; + ->add($roles, 'roles'); } } diff --git a/src/Admin/src/Repository/AdminRepository.php b/src/Admin/src/Repository/AdminRepository.php index 3153379..617b832 100644 --- a/src/Admin/src/Repository/AdminRepository.php +++ b/src/Admin/src/Repository/AdminRepository.php @@ -14,6 +14,7 @@ /** * @Entity(name="Api\Admin\Entity\Admin") + * @extends EntityRepository */ class AdminRepository extends EntityRepository { @@ -31,7 +32,7 @@ public function deleteAdmin(Admin $admin): void */ public function saveAdmin(Admin $admin): Admin { - if (!$admin->hasRoles()) { + if (! $admin->hasRoles()) { throw new Exception(Message::RESTRICTION_ROLES); } @@ -50,7 +51,7 @@ public function getAdmins(array $filters = []): AdminCollection ->createQueryBuilder() ->select(['admin']) ->from(Admin::class, 'admin') - ->orderBy(($filters['order'] ?? 'admin.created'), $filters['dir'] ?? 'desc') + ->orderBy($filters['order'] ?? 'admin.created', $filters['dir'] ?? 'desc') ->setFirstResult($page['offset']) ->setMaxResults($page['limit']) ->getQuery() diff --git a/src/Admin/src/Repository/AdminRoleRepository.php b/src/Admin/src/Repository/AdminRoleRepository.php index e3569e4..41b4cfc 100644 --- a/src/Admin/src/Repository/AdminRoleRepository.php +++ b/src/Admin/src/Repository/AdminRoleRepository.php @@ -12,13 +12,10 @@ /** * @Entity(name="Api\Admin\Entity\AdminRole") + * @extends EntityRepository */ class AdminRoleRepository extends EntityRepository { - /** - * @param array $filters - * @return AdminRoleCollection - */ public function getAdminRoles(array $filters = []): AdminRoleCollection { $page = PaginationHelper::getOffsetAndLimit($filters); @@ -28,7 +25,7 @@ public function getAdminRoles(array $filters = []): AdminRoleCollection ->createQueryBuilder() ->select(['role']) ->from(AdminRole::class, 'role') - ->orderBy(($filters['order'] ?? 'role.created'), $filters['dir'] ?? 'desc') + ->orderBy($filters['order'] ?? 'role.created', $filters['dir'] ?? 'desc') ->setFirstResult($page['offset']) ->setMaxResults($page['limit']) ->getQuery() diff --git a/src/Admin/src/RoutesDelegator.php b/src/Admin/src/RoutesDelegator.php index 2d5d819..9250f33 100644 --- a/src/Admin/src/RoutesDelegator.php +++ b/src/Admin/src/RoutesDelegator.php @@ -19,41 +19,50 @@ public function __invoke(ContainerInterface $container, string $serviceName, cal $uuid = \Api\App\RoutesDelegator::REGEXP_UUID; - $app->get('/admin/my-account', + $app->get( + '/admin/my-account', AdminAccountHandler::class, 'admin.my-account.view' ); - $app->patch('/admin/my-account', + $app->patch( + '/admin/my-account', AdminAccountHandler::class, 'admin.my-account.update' ); - $app->post('/admin', + $app->post( + '/admin', AdminHandler::class, 'admin.create' ); - $app->delete('/admin/' . $uuid, + $app->delete( + '/admin/' . $uuid, AdminHandler::class, 'admin.delete' ); - $app->get('/admin', + $app->get( + '/admin', AdminHandler::class, 'admin.list' ); - $app->patch('/admin/' . $uuid, + $app->patch( + '/admin/' . $uuid, AdminHandler::class, 'admin.update' ); - $app->get('/admin/' . $uuid, + $app->get( + '/admin/' . $uuid, AdminHandler::class, 'admin.view' ); - $app->get('/admin/role', + $app->get( + '/admin/role', AdminRoleHandler::class, 'admin.role.list' ); - $app->get('/admin/role/' . $uuid, + $app->get( + '/admin/role/' . $uuid, AdminRoleHandler::class, 'admin.role.view' ); diff --git a/src/Admin/src/Service/AdminRoleService.php b/src/Admin/src/Service/AdminRoleService.php index 8f46b62..97f95a2 100644 --- a/src/Admin/src/Service/AdminRoleService.php +++ b/src/Admin/src/Service/AdminRoleService.php @@ -18,7 +18,8 @@ class AdminRoleService implements AdminRoleServiceInterface */ public function __construct( protected AdminRoleRepository $adminRoleRepository - ) {} + ) { + } public function findOneBy(array $params = []): ?AdminRole { diff --git a/src/Admin/src/Service/AdminService.php b/src/Admin/src/Service/AdminService.php index e6c6e67..d9c4d0e 100644 --- a/src/Admin/src/Service/AdminService.php +++ b/src/Admin/src/Service/AdminService.php @@ -12,6 +12,8 @@ use Dot\AnnotatedServices\Annotation\Inject; use Exception; +use function sprintf; + class AdminService implements AdminServiceInterface { /** @@ -23,7 +25,8 @@ class AdminService implements AdminServiceInterface public function __construct( protected AdminRoleService $adminRoleService, protected AdminRepository $adminRepository - ) {} + ) { + } /** * @throws Exception @@ -41,10 +44,10 @@ public function createAdmin(array $data = []): Admin ->setLastName($data['lastName']) ->setStatus($data['status'] ?? Admin::STATUS_ACTIVE); - if (!empty($data['roles'])) { + if (! empty($data['roles'])) { foreach ($data['roles'] as $roleData) { $role = $this->adminRoleService->findOneBy(['uuid' => $roleData['uuid']]); - if (!$role instanceof AdminRole) { + if (! $role instanceof AdminRole) { throw new Exception( sprintf(Message::NOT_FOUND_BY_UUID, 'role', $roleData['uuid']) ); @@ -53,7 +56,7 @@ public function createAdmin(array $data = []): Admin } } else { $role = $this->adminRoleService->findOneBy(['name' => AdminRole::ROLE_ADMIN]); - if (!$role instanceof AdminRole) { + if (! $role instanceof AdminRole) { throw new Exception( sprintf(Message::NOT_FOUND_BY_NAME, 'role', AdminRole::ROLE_ADMIN) ); @@ -94,7 +97,7 @@ public function getAdmins(array $params = []): AdminCollection */ public function updateAdmin(Admin $admin, array $data = []): Admin { - if (!empty($data['password'])) { + if (! empty($data['password'])) { $admin->usePassword($data['password']); } @@ -110,11 +113,11 @@ public function updateAdmin(Admin $admin, array $data = []): Admin $admin->setStatus($data['status']); } - if (!empty($data['roles'])) { + if (! empty($data['roles'])) { $admin->resetRoles(); foreach ($data['roles'] as $roleData) { $role = $this->adminRoleService->findOneBy(['uuid' => $roleData['uuid']]); - if (!$role instanceof AdminRole) { + if (! $role instanceof AdminRole) { throw new Exception( sprintf(Message::NOT_FOUND_BY_UUID, 'role', $roleData['uuid']) ); diff --git a/src/App/src/Collection/CollectionInterface.php b/src/App/src/Collection/CollectionInterface.php index d7bedc3..517fa9a 100644 --- a/src/App/src/Collection/CollectionInterface.php +++ b/src/App/src/Collection/CollectionInterface.php @@ -5,4 +5,5 @@ namespace Api\App\Collection; interface CollectionInterface -{} +{ +} diff --git a/src/App/src/Collection/ResourceCollection.php b/src/App/src/Collection/ResourceCollection.php index d39a2fc..d8eb3b4 100644 --- a/src/App/src/Collection/ResourceCollection.php +++ b/src/App/src/Collection/ResourceCollection.php @@ -6,5 +6,9 @@ use Doctrine\ORM\Tools\Pagination\Paginator; +/** + * @extends Paginator + */ class ResourceCollection extends Paginator implements CollectionInterface -{} +{ +} diff --git a/src/App/src/Command/RouteListCommand.php b/src/App/src/Command/RouteListCommand.php index 5481edf..499d876 100644 --- a/src/App/src/Command/RouteListCommand.php +++ b/src/App/src/Command/RouteListCommand.php @@ -12,8 +12,15 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use function array_map; +use function ksort; +use function sprintf; +use function str_contains; +use function str_replace; + class RouteListCommand extends Command { + /** @var string $defaultName */ protected static $defaultName = 'route:list'; public function __construct( @@ -30,32 +37,31 @@ protected function configure(): void ->addUsage('[-i|--name[=NAME]] [-p|--path[=PATH]] [-m|--method[=METHOD]]') ->addOption('name', 'i', InputOption::VALUE_OPTIONAL, 'Filter routes by name') ->addOption('path', 'p', InputOption::VALUE_OPTIONAL, 'Filter routes by path') - ->addOption('method', 'm', InputOption::VALUE_OPTIONAL, 'Filter routes by method') - ; + ->addOption('method', 'm', InputOption::VALUE_OPTIONAL, 'Filter routes by method'); } protected function execute(InputInterface $input, OutputInterface $output): int { - $nameFilter = (string)$input->getOption('name'); - $pathFilter = (string)$input->getOption('path'); - $methodFilter = (string)$input->getOption('method'); + $nameFilter = (string) $input->getOption('name'); + $pathFilter = (string) $input->getOption('path'); + $methodFilter = (string) $input->getOption('method'); $routes = []; foreach ($this->application->getRoutes() as $route) { foreach ($route->getAllowedMethods() as $method) { - if (stripos($route->getName(), $nameFilter) === false) { + if (! str_contains($route->getName(), $nameFilter)) { continue; } - if (stripos($route->getPath(), $pathFilter) === false) { + if (! str_contains($route->getPath(), $pathFilter)) { continue; } - if (stripos($method, $methodFilter) === false) { + if (! str_contains($method, $methodFilter)) { continue; } $routes[sprintf('%s:%s', $method, $route->getPath())] = [ - 'name' => $route->getName(), - 'path' => $route->getPath(), + 'name' => $route->getName(), + 'path' => $route->getPath(), 'method' => $method, ]; } diff --git a/src/App/src/Command/TokenGenerateCommand.php b/src/App/src/Command/TokenGenerateCommand.php index b6eeb22..2e19a9e 100644 --- a/src/App/src/Command/TokenGenerateCommand.php +++ b/src/App/src/Command/TokenGenerateCommand.php @@ -11,9 +11,12 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use function sprintf; + class TokenGenerateCommand extends Command { - protected static $defaultName = 'token:generate'; + /** @var string $defaultName */ + protected static $defaultName = 'token:generate'; private string $typeErrorReporting = 'error-reporting'; public function __construct( @@ -29,7 +32,9 @@ protected function configure(): void ->setDescription('Generic token generator.') ->addArgument('type', InputArgument::REQUIRED, 'The type of token to be generated.') ->addUsage($this->typeErrorReporting) - ->setHelp(<<setHelp( + // @codingStandardsIgnoreStart + <<%command.name% is a multipurpose command that allows creating tokens required by different parts of the API. Usage: @@ -39,6 +44,7 @@ protected function configure(): void * open config/autoload/error-handling.global.php * paste the copied string inside the tokens array found under the ErrorReportServiceInterface::class key. MSG + // @codingStandardsIgnoreEnd ); } @@ -48,7 +54,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { $type = $input->getArgument('type'); - match($type) { + match ($type) { $this->typeErrorReporting => $this->generateErrorReportingToken($output), default => throw new Exception( sprintf('Unknown token type: %s', $type) @@ -62,7 +68,9 @@ private function generateErrorReportingToken(OutputInterface $output): void { $token = $this->errorReportService->generateToken(); - $output->writeln(<<writeln( + // @codingStandardsIgnoreStart + <<$token @@ -71,6 +79,7 @@ private function generateErrorReportingToken(OutputInterface $output): void * open config/autoload/error-handling.global.php * paste the copied string inside the tokens array found under the ErrorReportServiceInterface::class key. MSG -); + // @codingStandardsIgnoreEnd + ); } } diff --git a/src/App/src/ConfigProvider.php b/src/App/src/ConfigProvider.php index 54fb590..7bcb414 100644 --- a/src/App/src/ConfigProvider.php +++ b/src/App/src/ConfigProvider.php @@ -27,9 +27,9 @@ use Laminas\Hydrator\ArraySerializableHydrator; use Mezzio\Application; use Mezzio\Authentication; +use Mezzio\Hal\Metadata\MetadataMap; use Mezzio\Hal\Metadata\RouteBasedCollectionMetadata; use Mezzio\Hal\Metadata\RouteBasedResourceMetadata; -use Mezzio\Hal\Metadata\MetadataMap; use Mezzio\Template\TemplateRendererInterface; use Mezzio\Twig\TwigEnvironmentFactory; use Mezzio\Twig\TwigExtension; @@ -44,8 +44,8 @@ class ConfigProvider public function __invoke(): array { return [ - 'dependencies' => $this->getDependencies(), - MetadataMap::class => $this->getHalConfig() + 'dependencies' => $this->getDependencies(), + MetadataMap::class => $this->getHalConfig(), ]; } @@ -57,32 +57,32 @@ public function getDependencies(): array RoutesDelegator::class, \Api\Admin\RoutesDelegator::class, \Api\User\RoutesDelegator::class, - ] + ], ], 'factories' => [ - 'doctrine.entity_manager.orm_default' => EntityManagerFactory::class, - 'dot-mail.options.default' => MailOptionsAbstractFactory::class, - 'dot-mail.service.default' => MailServiceAbstractFactory::class, + 'doctrine.entity_manager.orm_default' => EntityManagerFactory::class, + 'dot-mail.options.default' => MailOptionsAbstractFactory::class, + 'dot-mail.service.default' => MailServiceAbstractFactory::class, AbstractAnnotatedFactory::CACHE_SERVICE => AnnotationsCacheFactory::class, - AuthenticationMiddleware::class => AuthenticationMiddlewareFactory::class, - AuthorizationMiddleware::class => AnnotatedServiceFactory::class, - Environment::class => TwigEnvironmentFactory::class, - TwigExtension::class => TwigExtensionFactory::class, - TwigRenderer::class => TwigRendererFactory::class, - ErrorResponseMiddleware::class => AnnotatedServiceFactory::class, - RouteListCommand::class => RouteListCommandFactory::class, - TokenGenerateCommand::class => TokenGenerateCommandFactory::class, - ErrorReportService::class => AnnotatedServiceFactory::class, - EntityListenerResolver::class => EntityListenerResolverFactory::class, + AuthenticationMiddleware::class => AuthenticationMiddlewareFactory::class, + AuthorizationMiddleware::class => AnnotatedServiceFactory::class, + Environment::class => TwigEnvironmentFactory::class, + TwigExtension::class => TwigExtensionFactory::class, + TwigRenderer::class => TwigRendererFactory::class, + ErrorResponseMiddleware::class => AnnotatedServiceFactory::class, + RouteListCommand::class => RouteListCommandFactory::class, + TokenGenerateCommand::class => TokenGenerateCommandFactory::class, + ErrorReportService::class => AnnotatedServiceFactory::class, + EntityListenerResolver::class => EntityListenerResolverFactory::class, ], - 'aliases' => [ + 'aliases' => [ Authentication\AuthenticationInterface::class => Authentication\OAuth2\OAuth2Adapter::class, - MailService::class => 'dot-mail.service.default', - EntityManager::class => 'doctrine.entity_manager.orm_default', - EntityManagerInterface::class => 'doctrine.entity_manager.orm_default', - TemplateRendererInterface::class => TwigRenderer::class, - ErrorReportServiceInterface::class => ErrorReportService::class, - ] + MailService::class => 'dot-mail.service.default', + EntityManager::class => 'doctrine.entity_manager.orm_default', + EntityManagerInterface::class => 'doctrine.entity_manager.orm_default', + TemplateRendererInterface::class => TwigRenderer::class, + ErrorReportServiceInterface::class => ErrorReportService::class, + ], ]; } @@ -94,10 +94,10 @@ public function getHalConfig(): array public static function getCollection(string $collectionClass, string $route, string $collectionRelation): array { return [ - '__class__' => RouteBasedCollectionMetadata::class, - 'collection_class' => $collectionClass, + '__class__' => RouteBasedCollectionMetadata::class, + 'collection_class' => $collectionClass, 'collection_relation' => $collectionRelation, - 'route' => $route + 'route' => $route, ]; } @@ -106,15 +106,14 @@ public static function getResource( string $route, string $resourceIdentifier = 'uuid', string $resourceIdentifierPlaceholder = 'uuid' - ): array - { + ): array { return [ - '__class__' => RouteBasedResourceMetadata::class, - 'resource_class' => $resourceClass, - 'route' => $route, - 'extractor' => ArraySerializableHydrator::class, - 'resource_identifier' => $resourceIdentifier, - 'route_identifier_placeholder' => $resourceIdentifierPlaceholder + '__class__' => RouteBasedResourceMetadata::class, + 'resource_class' => $resourceClass, + 'route' => $route, + 'extractor' => ArraySerializableHydrator::class, + 'resource_identifier' => $resourceIdentifier, + 'route_identifier_placeholder' => $resourceIdentifierPlaceholder, ]; } } diff --git a/src/App/src/Entity/AbstractEntity.php b/src/App/src/Entity/AbstractEntity.php index a766810..4bb8f07 100644 --- a/src/App/src/Entity/AbstractEntity.php +++ b/src/App/src/Entity/AbstractEntity.php @@ -7,17 +7,18 @@ use DateTimeImmutable; use Laminas\Stdlib\ArraySerializableInterface; +use function is_array; +use function method_exists; +use function ucfirst; + abstract class AbstractEntity implements UuidAwareInterface, TimestampAwareInterface, ArraySerializableInterface { - use UuidAwareTrait; use TimestampAwareTrait; + use UuidAwareTrait; - /** - * AbstractEntity constructor. - */ public function __construct() { - $this->uuid = UuidOrderedTimeGenerator::generateUuid(); + $this->uuid = UuidOrderedTimeGenerator::generateUuid(); $this->created = new DateTimeImmutable(); $this->updated = new DateTimeImmutable(); } @@ -27,7 +28,7 @@ public function exchangeArray(array $array): void foreach ($array as $property => $values) { if (is_array($values)) { $method = 'add' . ucfirst($property); - if (!method_exists($this, $method)) { + if (! method_exists($this, $method)) { continue; } foreach ($values as $value) { @@ -35,7 +36,7 @@ public function exchangeArray(array $array): void } } else { $method = 'set' . ucfirst($property); - if (!method_exists($this, $method)) { + if (! method_exists($this, $method)) { continue; } $this->$method($values); diff --git a/src/App/src/Entity/EntityListenerResolver.php b/src/App/src/Entity/EntityListenerResolver.php index 5e046e1..329b701 100644 --- a/src/App/src/Entity/EntityListenerResolver.php +++ b/src/App/src/Entity/EntityListenerResolver.php @@ -13,9 +13,11 @@ class EntityListenerResolver extends DefaultEntityListenerResolver { public function __construct( protected ContainerInterface $container - ) {} + ) { + } /** + * @param string $className * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/src/App/src/Entity/OAuthAccessToken.php b/src/App/src/Entity/OAuthAccessToken.php index 15742c3..01c2d90 100644 --- a/src/App/src/Entity/OAuthAccessToken.php +++ b/src/App/src/Entity/OAuthAccessToken.php @@ -9,21 +9,19 @@ use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\Mapping as ORM; +use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Signer\Key\InMemory; +use Lcobucci\JWT\Signer\Rsa\Sha256; +use Lcobucci\JWT\Token; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; -use Lcobucci\JWT\Signer\Rsa\Sha256; -use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\Token; use RuntimeException; /** - * Class OAuthAccessToken * @ORM\Entity(repositoryClass="Api\App\Repository\OAuthAccessTokenRepository") * @ORM\Table(name="oauth_access_tokens") - * @package Api\App\Entity */ class OAuthAccessToken implements AccessTokenEntityInterface { @@ -40,19 +38,13 @@ class OAuthAccessToken implements AccessTokenEntityInterface */ private ClientEntityInterface $client; - /** - * @ORM\Column(name="user_id", type="string", nullable=true) - */ + /** @ORM\Column(name="user_id", type="string", nullable=true) */ private ?string $userId; - /** - * @ORM\Column(name="token", type="string", length=100) - */ + /** @ORM\Column(name="token", type="string", length=100) */ private string $token; - /** - * @ORM\Column(name="revoked", type="boolean", options={"default":0}) - */ + /** @ORM\Column(name="revoked", type="boolean", options={"default":0}) */ private bool $isRevoked = false; /** @@ -64,9 +56,7 @@ class OAuthAccessToken implements AccessTokenEntityInterface */ protected Collection $scopes; - /** - * @ORM\Column(name="expires_at", type="datetime_immutable") - */ + /** @ORM\Column(name="expires_at", type="datetime_immutable") */ private DateTimeImmutable $expiresAt; private ?CryptKey $privateKey = null; @@ -138,11 +128,17 @@ public function getIdentifier(): string return $this->getToken(); } + /** + * @param mixed $identifier + */ public function setIdentifier($identifier): self { return $this->setToken($identifier); } + /** + * @param string|int|null $identifier + */ public function setUserIdentifier($identifier): self { $this->userId = $identifier; @@ -157,7 +153,7 @@ public function getUserIdentifier(): ?string public function addScope(ScopeEntityInterface $scope): self { - if (!$this->scopes->contains($scope)) { + if (! $this->scopes->contains($scope)) { $this->scopes->add($scope); } @@ -175,11 +171,10 @@ public function removeScope(OAuthScope $scope): self public function getScopes(?Criteria $criteria = null): array { - if (is_null($criteria)) { + if ($criteria === null) { return $this->scopes->toArray(); } - /** @psalm-suppress UndefinedInterfaceMethod */ return $this->scopes->matching($criteria)->toArray(); } @@ -224,7 +219,7 @@ private function convertToJWT(): Token { $this->initJwtConfiguration(); - if (is_null($this->jwtConfiguration)) { + if ($this->jwtConfiguration === null) { throw new RuntimeException('Unable to convert to JWT without config'); } diff --git a/src/App/src/Entity/OAuthAuthCode.php b/src/App/src/Entity/OAuthAuthCode.php index 6e9b866..5f8b758 100644 --- a/src/App/src/Entity/OAuthAuthCode.php +++ b/src/App/src/Entity/OAuthAuthCode.php @@ -15,10 +15,8 @@ use League\OAuth2\Server\Entities\Traits\AuthCodeTrait; /** - * Class OauthClient * @ORM\Entity(repositoryClass="Api\App\Repository\OAuthAuthCodeRepository") * @ORM\Table(name="oauth_auth_codes") - * @package Api\App\Entity */ class OAuthAuthCode implements AuthCodeEntityInterface { @@ -37,9 +35,7 @@ class OAuthAuthCode implements AuthCodeEntityInterface */ private ClientEntityInterface $client; - /** - * @ORM\Column(name="revoked", type="boolean", options={"default":0}) - */ + /** @ORM\Column(name="revoked", type="boolean", options={"default":0}) */ private bool $isRevoked = false; /** @@ -51,15 +47,13 @@ class OAuthAuthCode implements AuthCodeEntityInterface */ protected Collection $scopes; - /** - * @ORM\Column(type="datetime_immutable", nullable=true) - */ + /** @ORM\Column(type="datetime_immutable", nullable=true) */ private DateTimeImmutable $expiresDatetime; public function __construct() { $this->expiresDatetime = new DateTimeImmutable(); - $this->scopes = new ArrayCollection(); + $this->scopes = new ArrayCollection(); } public function setId(int $id): self @@ -91,6 +85,9 @@ public function getIdentifier(): ?int return $this->getId(); } + /** + * @param mixed $identifier + */ public function setIdentifier($identifier): self { $this->setId($identifier); @@ -98,6 +95,9 @@ public function setIdentifier($identifier): self return $this; } + /** + * @param string|int|null $identifier + */ public function setUserIdentifier($identifier): self { return $this; @@ -136,7 +136,7 @@ public function revoke(): self public function addScope(ScopeEntityInterface $scope): self { - if (!$this->scopes->contains($scope)) { + if (! $this->scopes->contains($scope)) { $this->scopes->add($scope); } @@ -152,11 +152,10 @@ public function removeScope(OAuthScope $scope): self public function getScopes(?Criteria $criteria = null): array { - if (is_null($criteria)) { + if ($criteria === null) { return $this->scopes->toArray(); } - /** @psalm-suppress UndefinedInterfaceMethod */ return $this->scopes->matching($criteria)->toArray(); } diff --git a/src/App/src/Entity/OAuthClient.php b/src/App/src/Entity/OAuthClient.php index e1e6e85..0d1af8d 100644 --- a/src/App/src/Entity/OAuthClient.php +++ b/src/App/src/Entity/OAuthClient.php @@ -9,14 +9,12 @@ use League\OAuth2\Server\Entities\ClientEntityInterface; /** - * Class OauthClient * @ORM\Entity(repositoryClass="Api\App\Repository\OAuthClientRepository") * @ORM\Table(name="oauth_clients") - * @package Api\App\Entity */ class OAuthClient implements ClientEntityInterface { - public const NAME_ADMIN = 'admin'; + public const NAME_ADMIN = 'admin'; public const NAME_FRONTEND = 'frontend'; /** @@ -32,29 +30,19 @@ class OAuthClient implements ClientEntityInterface */ private ?User $user = null; - /** - * @ORM\Column(name="name", type="string", length=40) - */ + /** @ORM\Column(name="name", type="string", length=40) */ private string $name = ''; - /** - * @ORM\Column(name="secret", type="string", length=100, nullable=true) - */ + /** @ORM\Column(name="secret", type="string", length=100, nullable=true) */ private ?string $secret = null; - /** - * @ORM\Column(name="redirect", type="string", length=191) - */ + /** @ORM\Column(name="redirect", type="string", length=191) */ private string $redirect = ''; - /** - * @ORM\Column(name="revoked", type="boolean", options={"default":0}) - */ + /** @ORM\Column(name="revoked", type="boolean", options={"default":0}) */ private bool $isRevoked = false; - /** - * @ORM\Column(name="isConfidential", type="boolean", options={"default":0}) - */ + /** @ORM\Column(name="isConfidential", type="boolean", options={"default":0}) */ private bool $isConfidential = false; public function setId(int $id): self diff --git a/src/App/src/Entity/OAuthRefreshToken.php b/src/App/src/Entity/OAuthRefreshToken.php index 42d3762..1649bfe 100644 --- a/src/App/src/Entity/OAuthRefreshToken.php +++ b/src/App/src/Entity/OAuthRefreshToken.php @@ -10,10 +10,8 @@ use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; /** - * Class OAuthRefreshToken * @ORM\Entity(repositoryClass="Api\App\Repository\OAuthRefreshTokenRepository") * @ORM\Table(name="oauth_refresh_tokens") - * @package Api\App\Entity */ class OAuthRefreshToken implements RefreshTokenEntityInterface { @@ -30,14 +28,10 @@ class OAuthRefreshToken implements RefreshTokenEntityInterface */ private AccessTokenEntityInterface $accessToken; - /** - * @ORM\Column(name="revoked", type="boolean", options={"default":0}) - */ + /** @ORM\Column(name="revoked", type="boolean", options={"default":0}) */ private bool $isRevoked = false; - /** - * @ORM\Column(name="expires_at", type="datetime_immutable") - */ + /** @ORM\Column(name="expires_at", type="datetime_immutable") */ private DateTimeImmutable $expiresAt; public function setId(int $id): self @@ -54,12 +48,12 @@ public function getId(): ?int public function getIdentifier(): string { - return (string)$this->getId(); + return (string) $this->getId(); } - public function setIdentifier($identifier): self + public function setIdentifier(mixed $identifier): self { - $this->setId((int)$identifier); + $this->setId((int) $identifier); return $this; } diff --git a/src/App/src/Entity/OAuthScope.php b/src/App/src/Entity/OAuthScope.php index 26e152a..5a8192a 100644 --- a/src/App/src/Entity/OAuthScope.php +++ b/src/App/src/Entity/OAuthScope.php @@ -12,12 +12,8 @@ use League\OAuth2\Server\Entities\Traits\ScopeTrait; /** - * Class OAuthScope * @ORM\Entity(repositoryClass="Api\App\Repository\OAuthScopeRepository") * @ORM\Table(name="oauth_scopes") - * @package Api\App\Entity - * - * @psalm-suppress UndefinedInterfaceMethod */ class OAuthScope implements ScopeEntityInterface { @@ -30,25 +26,19 @@ class OAuthScope implements ScopeEntityInterface */ private int $id; - /** - * @ORM\Column(name="scope", type="string", length=191) - */ + /** @ORM\Column(name="scope", type="string", length=191) */ private string $scope = ''; - /** - * @ORM\ManyToMany(targetEntity="Api\App\Entity\OAuthAccessToken", mappedBy="scopes") - */ + /** @ORM\ManyToMany(targetEntity="Api\App\Entity\OAuthAccessToken", mappedBy="scopes") */ protected Collection $accessTokens; - /** - * @ORM\ManyToMany(targetEntity="Api\App\Entity\OAuthAuthCode", mappedBy="scopes") - */ + /** @ORM\ManyToMany(targetEntity="Api\App\Entity\OAuthAuthCode", mappedBy="scopes") */ protected Collection $authCodes; public function __construct() { $this->accessTokens = new ArrayCollection(); - $this->authCodes = new ArrayCollection(); + $this->authCodes = new ArrayCollection(); } public function setId(int $id): self @@ -82,7 +72,7 @@ public function getScope(): string public function addAccessToken(OAuthAccessToken $accessToken): self { - if (!$this->accessTokens->contains($accessToken)) { + if (! $this->accessTokens->contains($accessToken)) { $this->accessTokens->add($accessToken); } @@ -100,7 +90,7 @@ public function removeAccessToken(OAuthAccessToken $accessToken): self public function getAccessTokens(?Criteria $criteria = null): Collection { - if (is_null($criteria)) { + if ($criteria === null) { return $this->accessTokens; } @@ -109,7 +99,7 @@ public function getAccessTokens(?Criteria $criteria = null): Collection public function addAuthCode(OAuthAuthCode $authCode): self { - if (!$this->authCodes->contains($authCode)) { + if (! $this->authCodes->contains($authCode)) { $this->authCodes->add($authCode); } @@ -127,7 +117,7 @@ public function removeAuthCode(OAuthAuthCode $authCode): self public function getAuthCodes(?Criteria $criteria = null): Collection { - if (is_null($criteria)) { + if ($criteria === null) { return $this->authCodes; } diff --git a/src/App/src/Entity/PasswordTrait.php b/src/App/src/Entity/PasswordTrait.php index de6edc3..5287e0b 100644 --- a/src/App/src/Entity/PasswordTrait.php +++ b/src/App/src/Entity/PasswordTrait.php @@ -4,6 +4,11 @@ namespace Api\App\Entity; +use function password_hash; +use function password_verify; + +use const PASSWORD_DEFAULT; + trait PasswordTrait { public function usePassword(string $password): self diff --git a/src/App/src/Entity/TimestampAwareInterface.php b/src/App/src/Entity/TimestampAwareInterface.php index cbac39a..ab3e68f 100644 --- a/src/App/src/Entity/TimestampAwareInterface.php +++ b/src/App/src/Entity/TimestampAwareInterface.php @@ -6,10 +6,6 @@ use DateTimeImmutable; -/** - * Interface TimestampAwareInterface - * @package Api\App\Entity - */ interface TimestampAwareInterface { public function getCreated(): ?DateTimeImmutable; diff --git a/src/App/src/Entity/TimestampAwareTrait.php b/src/App/src/Entity/TimestampAwareTrait.php index b3f70ad..d1874db 100644 --- a/src/App/src/Entity/TimestampAwareTrait.php +++ b/src/App/src/Entity/TimestampAwareTrait.php @@ -11,20 +11,15 @@ trait TimestampAwareTrait { private string $dateFormat = 'Y-m-d H:i:s'; - /** - * @ORM\Column(name="created", type="datetime_immutable") - */ + /** @ORM\Column(name="created", type="datetime_immutable") */ protected ?DateTimeImmutable $created; - /** - * @ORM\Column(name="updated", type="datetime_immutable", nullable=true) - */ + /** @ORM\Column(name="updated", type="datetime_immutable", nullable=true) */ protected ?DateTimeImmutable $updated; /** * @ORM\PrePersist() * @ORM\PreUpdate() - * @return void */ public function updateTimestamps(): void { @@ -61,7 +56,7 @@ public function getUpdatedFormatted(): ?string public function touch(): void { - if (!($this->created instanceof DateTimeImmutable)) { + if (! $this->created instanceof DateTimeImmutable) { $this->created = new DateTimeImmutable(); } diff --git a/src/App/src/Entity/UuidAwareTrait.php b/src/App/src/Entity/UuidAwareTrait.php index 1918ed6..72d7398 100644 --- a/src/App/src/Entity/UuidAwareTrait.php +++ b/src/App/src/Entity/UuidAwareTrait.php @@ -14,12 +14,12 @@ trait UuidAwareTrait * @ORM\Column(name="uuid", type="uuid_binary_ordered_time", unique=true) * @ORM\GeneratedValue(strategy="CUSTOM") * @ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidOrderedTimeGenerator") - */ + */ protected ?UuidInterface $uuid; public function getUuid(): ?UuidInterface { - if (!$this->uuid) { + if (! $this->uuid) { $this->uuid = UuidOrderedTimeGenerator::generateUuid(); } diff --git a/src/App/src/Entity/UuidOrderedTimeGenerator.php b/src/App/src/Entity/UuidOrderedTimeGenerator.php index 39e41eb..40d4846 100644 --- a/src/App/src/Entity/UuidOrderedTimeGenerator.php +++ b/src/App/src/Entity/UuidOrderedTimeGenerator.php @@ -10,6 +10,8 @@ use Ramsey\Uuid\UuidInterface; use Throwable; +use function error_log; + final class UuidOrderedTimeGenerator { private static ?UuidFactory $factory = null; @@ -27,9 +29,9 @@ public static function generateUuid(): ?UuidInterface private static function getFactory(): ?UuidFactory { - if (!(self::$factory instanceof UuidFactory)) { + if (! self::$factory instanceof UuidFactory) { /** @var UuidFactory $factory */ - $factory = clone Uuid::getFactory(); + $factory = clone Uuid::getFactory(); self::$factory = $factory; $codec = new OrderedTimeCodec( diff --git a/src/App/src/Factory/AuthenticationMiddlewareFactory.php b/src/App/src/Factory/AuthenticationMiddlewareFactory.php index b17afe9..fcb0f94 100644 --- a/src/App/src/Factory/AuthenticationMiddlewareFactory.php +++ b/src/App/src/Factory/AuthenticationMiddlewareFactory.php @@ -19,7 +19,7 @@ class AuthenticationMiddlewareFactory */ public function __invoke(ContainerInterface $container): AuthenticationMiddleware { - if (!$container->has(AuthenticationInterface::class)) { + if (! $container->has(AuthenticationInterface::class)) { throw new InvalidConfigException('AuthenticationInterface service is missing'); } diff --git a/src/App/src/Factory/ErrorResponseGeneratorFactory.php b/src/App/src/Factory/ErrorResponseGeneratorFactory.php index d5a4fad..ebeda28 100644 --- a/src/App/src/Factory/ErrorResponseGeneratorFactory.php +++ b/src/App/src/Factory/ErrorResponseGeneratorFactory.php @@ -19,6 +19,6 @@ public function __invoke(ContainerInterface $container): ErrorResponseGenerator { $config = $container->has('config') ? $container->get('config') : []; - return new ErrorResponseGenerator(($config['debug'] ?? false)); + return new ErrorResponseGenerator($config['debug'] ?? false); } } diff --git a/src/App/src/Factory/OAuthScopeRepositoryFactory.php b/src/App/src/Factory/OAuthScopeRepositoryFactory.php index fddf57f..48f0e11 100644 --- a/src/App/src/Factory/OAuthScopeRepositoryFactory.php +++ b/src/App/src/Factory/OAuthScopeRepositoryFactory.php @@ -14,8 +14,6 @@ class OAuthScopeRepositoryFactory { /** - * @param ContainerInterface $container - * @return ObjectRepository * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/src/App/src/Handler/ErrorReportHandler.php b/src/App/src/Handler/ErrorReportHandler.php index 72f12df..255b97c 100644 --- a/src/App/src/Handler/ErrorReportHandler.php +++ b/src/App/src/Handler/ErrorReportHandler.php @@ -35,11 +35,10 @@ public function __construct( protected HalResponseFactory $responseFactory, protected ResourceGenerator $resourceGenerator, protected ErrorReportServiceInterface $errorReportService - ) {} + ) { + } /** - * @param ServerRequestInterface $request - * @return ResponseInterface * @throws Throwable */ public function post(ServerRequestInterface $request): ResponseInterface diff --git a/src/App/src/Handler/ResponseTrait.php b/src/App/src/Handler/ResponseTrait.php index db1f4e1..12c84b7 100644 --- a/src/App/src/Handler/ResponseTrait.php +++ b/src/App/src/Handler/ResponseTrait.php @@ -12,6 +12,12 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use function is_array; +use function method_exists; +use function sprintf; +use function strtolower; +use function strtoupper; + trait ResponseTrait { /** @@ -49,7 +55,7 @@ public function errorResponse( return $this->restResponse([ 'error' => [ 'messages' => is_array($messages) ? $messages : [$messages], - ] + ], ], $status); } @@ -60,7 +66,7 @@ public function infoResponse( return $this->restResponse([ 'info' => [ 'messages' => is_array($messages) ? $messages : [$messages], - ] + ], ], $status); } @@ -77,7 +83,7 @@ public function redirectResponse(string $location): ResponseInterface public function restResponse( array|string $messages = [], int $status = Response::STATUS_CODE_200 - ): ResponseInterface{ + ): ResponseInterface { return new JsonResponse($messages, $status); } } diff --git a/src/App/src/Helper/PaginationHelper.php b/src/App/src/Helper/PaginationHelper.php index 1cd80a4..cceb75d 100644 --- a/src/App/src/Helper/PaginationHelper.php +++ b/src/App/src/Helper/PaginationHelper.php @@ -10,8 +10,8 @@ class PaginationHelper public static function getOffsetAndLimit(array $filters = []): array { - $page = (int)($filters['page'] ?? 1); - $limit = (int)($filters['limit'] ?? self::LIMIT); + $page = (int) ($filters['page'] ?? 1); + $limit = (int) ($filters['limit'] ?? self::LIMIT); $offset = 0; if ($page > 0) { @@ -19,8 +19,8 @@ public static function getOffsetAndLimit(array $filters = []): array } return [ - 'offset' => (int)$offset, - 'limit' => $limit + 'offset' => (int) $offset, + 'limit' => $limit, ]; } } diff --git a/src/App/src/Message.php b/src/App/src/Message.php index b6139a5..777a68e 100644 --- a/src/App/src/Message.php +++ b/src/App/src/Message.php @@ -6,52 +6,52 @@ class Message { - public const ADMIN_CREATED = 'Admin account has been created.'; - public const ADMIN_DELETED = 'Admin account has been deleted.'; - public const ADMIN_NOT_ACTIVATED = 'This account is deactivated.'; - public const ADMIN_ROLE_MISSING = 'Admin role \'%s\' is missing.'; - public const AVATAR_CREATED = 'Avatar has been successfully created.'; - public const AVATAR_DELETED = 'Avatar has been successfully deleted.'; - public const AVATAR_MISSING = 'This user account has no avatar associated with it.'; - public const DUPLICATE_EMAIL = 'An account with this email address already exists.'; - public const DUPLICATE_IDENTITY = 'An account with this identity already exists.'; - public const ERROR_REPORT_OK = 'Error report successfully saved.'; - public const ERROR_REPORT_NOT_ALLOWED = 'You are not allowed to report errors.'; - public const ERROR_REPORT_NOT_ENABLED = 'Remote error reporting is not enabled.'; - public const INVALID_ACTIVATION_CODE = 'Invalid activation code.'; - public const INVALID_CLIENT_ID = 'Invalid client_id.'; - public const INVALID_CONFIG = 'Invalid configuration value: \'%s\''; - public const INVALID_EMAIL = 'Invalid email.'; - public const INVALID_VALUE = 'The value specified for \'%s\' is invalid.'; - public const INVALID_IDENTIFIER = 'User cannot be found by supplied identifier.'; - public const MAIL_SENT_RECOVER_IDENTITY = 'If the provided email identifies an account in our system, ' . - 'you will receive an email with your account\'s identity.'; - public const MAIL_SENT_RESET_PASSWORD = 'If the provided email identifies an account in our system, ' . - 'you will receive an email with further instructions on resetting your account\'s password.'; - public const MAIL_SENT_USER_ACTIVATION = 'User activation mail has been successfully sent to \'%s\''; - public const MISSING_CONFIG = 'Missing configuration value: \'%s\''; - public const MISSING_PARAMETER = 'Missing parameter: \'%s\''; - public const NOT_FOUND_BY_NAME = 'Unable to find %s identified by name: %s'; - public const NOT_FOUND_BY_UUID = 'Unable to find %s identified by UUID: %s'; - public const RESET_PASSWORD_EXPIRED = 'Password reset request for hash: \'%s\' is invalid (expired).'; - public const RESET_PASSWORD_NOT_FOUND = 'Could not find password reset request identified by hash: \'%s\''; - public const RESET_PASSWORD_OK = 'Password successfully modified.'; - public const RESET_PASSWORD_USED = 'Password reset request for hash: \'%s\' is invalid (completed).'; - public const RESET_PASSWORD_VALID = 'Password reset request for hash: \'%s\' is valid.'; - public const RESOURCE_NOT_ALLOWED = 'You are not allowed to access this resource.'; - public const RESTRICTION_IMAGE = 'File must be an image (jpg, png).'; - public const RESTRICTION_ROLES = 'User accounts must have at least one role.'; - public const USER_ACTIVATED = 'This account has been activated.'; - public const USER_ALREADY_ACTIVATED = 'This account is already active.'; - public const USER_NOT_ACTIVATED = 'User account must be activated first.'; - public const USER_NOT_FOUND_BY_EMAIL = 'Could not find account identified by email \'%s\''; - public const USER_NOT_FOUND_BY_IDENTITY = 'Could not find account by identity \'%s\''; - public const VALIDATOR_MIN_LENGTH = '%s must be at least %d characters long.'; - public const VALIDATOR_MAX_LENGTH = '%s cannot be longer than %d characters.'; - public const VALIDATOR_MIN_MAX_LENGTH = '%s length must be between %d and %d characters.'; - public const VALIDATOR_PASSWORD_MISMATCH = 'Password confirmation does not match the provided password.'; - public const VALIDATOR_REQUIRED_FIELD = 'This field is required and cannot be empty.'; + public const ADMIN_CREATED = 'Admin account has been created.'; + public const ADMIN_DELETED = 'Admin account has been deleted.'; + public const ADMIN_NOT_ACTIVATED = 'This account is deactivated.'; + public const ADMIN_ROLE_MISSING = 'Admin role \'%s\' is missing.'; + public const AVATAR_CREATED = 'Avatar has been successfully created.'; + public const AVATAR_DELETED = 'Avatar has been successfully deleted.'; + public const AVATAR_MISSING = 'This user account has no avatar associated with it.'; + public const DUPLICATE_EMAIL = 'An account with this email address already exists.'; + public const DUPLICATE_IDENTITY = 'An account with this identity already exists.'; + public const ERROR_REPORT_OK = 'Error report successfully saved.'; + public const ERROR_REPORT_NOT_ALLOWED = 'You are not allowed to report errors.'; + public const ERROR_REPORT_NOT_ENABLED = 'Remote error reporting is not enabled.'; + public const INVALID_ACTIVATION_CODE = 'Invalid activation code.'; + public const INVALID_CLIENT_ID = 'Invalid client_id.'; + public const INVALID_CONFIG = 'Invalid configuration value: \'%s\''; + public const INVALID_EMAIL = 'Invalid email.'; + public const INVALID_VALUE = 'The value specified for \'%s\' is invalid.'; + public const INVALID_IDENTIFIER = 'User cannot be found by supplied identifier.'; + public const MAIL_SENT_RECOVER_IDENTITY = 'If the provided email identifies an account in our system, ' + . 'you will receive an email with your account\'s identity.'; + public const MAIL_SENT_RESET_PASSWORD = 'If the provided email identifies an account in our system, ' + . 'you will receive an email with further instructions on resetting your account\'s password.'; + public const MAIL_SENT_USER_ACTIVATION = 'User activation mail has been successfully sent to \'%s\''; + public const MISSING_CONFIG = 'Missing configuration value: \'%s\''; + public const MISSING_PARAMETER = 'Missing parameter: \'%s\''; + public const NOT_FOUND_BY_NAME = 'Unable to find %s identified by name: %s'; + public const NOT_FOUND_BY_UUID = 'Unable to find %s identified by UUID: %s'; + public const RESET_PASSWORD_EXPIRED = 'Password reset request for hash: \'%s\' is invalid (expired).'; + public const RESET_PASSWORD_NOT_FOUND = 'Could not find password reset request identified by hash: \'%s\''; + public const RESET_PASSWORD_OK = 'Password successfully modified.'; + public const RESET_PASSWORD_USED = 'Password reset request for hash: \'%s\' is invalid (completed).'; + public const RESET_PASSWORD_VALID = 'Password reset request for hash: \'%s\' is valid.'; + public const RESOURCE_NOT_ALLOWED = 'You are not allowed to access this resource.'; + public const RESTRICTION_IMAGE = 'File must be an image (jpg, png).'; + public const RESTRICTION_ROLES = 'User accounts must have at least one role.'; + public const USER_ACTIVATED = 'This account has been activated.'; + public const USER_ALREADY_ACTIVATED = 'This account is already active.'; + public const USER_NOT_ACTIVATED = 'User account must be activated first.'; + public const USER_NOT_FOUND_BY_EMAIL = 'Could not find account identified by email \'%s\''; + public const USER_NOT_FOUND_BY_IDENTITY = 'Could not find account by identity \'%s\''; + public const VALIDATOR_MIN_LENGTH = '%s must be at least %d characters long.'; + public const VALIDATOR_MAX_LENGTH = '%s cannot be longer than %d characters.'; + public const VALIDATOR_MIN_MAX_LENGTH = '%s length must be between %d and %d characters.'; + public const VALIDATOR_PASSWORD_MISMATCH = 'Password confirmation does not match the provided password.'; + public const VALIDATOR_REQUIRED_FIELD = 'This field is required and cannot be empty.'; public const VALIDATOR_REQUIRED_FIELD_BY_NAME = '%s is required and cannot be empty.'; - public const VALIDATOR_SKIP_OR_FILL = 'If this field is specified, then it must be filled in.'; - public const VALIDATOR_REQUIRED_UPLOAD = 'A file must be uploaded first.'; + public const VALIDATOR_SKIP_OR_FILL = 'If this field is specified, then it must be filled in.'; + public const VALIDATOR_REQUIRED_UPLOAD = 'A file must be uploaded first.'; } diff --git a/src/App/src/Middleware/AuthenticationMiddleware.php b/src/App/src/Middleware/AuthenticationMiddleware.php index b890b8c..25ac3c8 100644 --- a/src/App/src/Middleware/AuthenticationMiddleware.php +++ b/src/App/src/Middleware/AuthenticationMiddleware.php @@ -16,7 +16,7 @@ class AuthenticationMiddleware extends \Mezzio\Authentication\AuthenticationMidd public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $user = $this->auth->authenticate($request); - if (!$user instanceof UserIdentity) { + if (! $user instanceof UserIdentity) { $user = new UserIdentity('guest', [ UserRole::ROLE_GUEST, ], [ diff --git a/src/App/src/Middleware/AuthorizationMiddleware.php b/src/App/src/Middleware/AuthorizationMiddleware.php index be92d6a..70400c0 100644 --- a/src/App/src/Middleware/AuthorizationMiddleware.php +++ b/src/App/src/Middleware/AuthorizationMiddleware.php @@ -6,21 +6,23 @@ use Api\Admin\Entity\Admin; use Api\Admin\Repository\AdminRepository; -use Api\User\Repository\UserRepository; -use Dot\AnnotatedServices\Annotation\Inject; use Api\App\Entity\Guest; use Api\App\Entity\RoleInterface; use Api\App\Message; use Api\App\UserIdentity; use Api\User\Entity\User; +use Api\User\Repository\UserRepository; +use Dot\AnnotatedServices\Annotation\Inject; use Laminas\Diactoros\Response\JsonResponse; use Laminas\Http\Response; +use Mezzio\Authentication\UserInterface; +use Mezzio\Authorization\AuthorizationInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; -use Mezzio\Authentication\UserInterface; -use Mezzio\Authorization\AuthorizationInterface; + +use function sprintf; class AuthorizationMiddleware implements MiddlewareInterface { @@ -35,7 +37,8 @@ public function __construct( protected AuthorizationInterface $authorization, protected UserRepository $userRepository, protected AdminRepository $adminRepository - ) {} + ) { + } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { @@ -44,20 +47,20 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface switch ($defaultUser->getDetail('oauth_client_id')) { case 'admin': $user = $this->adminRepository->findOneBy(['identity' => $defaultUser->getIdentity()]); - if (!$user instanceof Admin) { + if (! $user instanceof Admin) { return $this->unauthorizedResponse(sprintf( Message::USER_NOT_FOUND_BY_IDENTITY, $defaultUser->getIdentity() )); } - if (!$user->isActive()) { + if (! $user->isActive()) { return $this->unauthorizedResponse(Message::ADMIN_NOT_ACTIVATED); } $request = $request->withAttribute(Admin::class, $user); break; case 'frontend': $user = $this->userRepository->findOneBy(['identity' => $defaultUser->getIdentity()]); - if (!$user instanceof User || $user->isDeleted()) { + if (! $user instanceof User || $user->isDeleted()) { return $this->unauthorizedResponse(sprintf( Message::USER_NOT_FOUND_BY_IDENTITY, $defaultUser->getIdentity() @@ -69,7 +72,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $request = $request->withAttribute(User::class, $user); break; case 'guest': - $user = new Guest(); + $user = new Guest(); $request = $request->withAttribute(Guest::class, $user); break; default: @@ -90,7 +93,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } } - if (!$isGranted) { + if (! $isGranted) { return $this->unauthorizedResponse(Message::RESOURCE_NOT_ALLOWED); } @@ -102,9 +105,9 @@ protected function unauthorizedResponse(string $message): ResponseInterface return new JsonResponse([ 'error' => [ 'messages' => [ - $message - ] - ] + $message, + ], + ], ], Response::STATUS_CODE_403); } } diff --git a/src/App/src/Middleware/ErrorResponseMiddleware.php b/src/App/src/Middleware/ErrorResponseMiddleware.php index 70e9f2a..f343670 100644 --- a/src/App/src/Middleware/ErrorResponseMiddleware.php +++ b/src/App/src/Middleware/ErrorResponseMiddleware.php @@ -12,6 +12,9 @@ use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use function json_decode; +use function json_encode; + class ErrorResponseMiddleware implements MiddlewareInterface { /** @@ -21,7 +24,8 @@ class ErrorResponseMiddleware implements MiddlewareInterface */ public function __construct( protected array $config - ) {} + ) { + } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { @@ -29,9 +33,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface if ($response->getStatusCode() === Response::STATUS_CODE_400) { $body = json_decode((string) $response->getBody()); if ($body->error === 'invalid_grant' && empty($body->hint)) { - $body->error = $this->config['invalid_credentials']['error']; + $body->error = $this->config['invalid_credentials']['error']; $body->error_description = $this->config['invalid_credentials']['error_description']; - $body->message = $this->config['invalid_credentials']['message']; + $body->message = $this->config['invalid_credentials']['message']; $stream = new Stream('php://temp', 'wb+'); $stream->write(json_encode($body)); diff --git a/src/App/src/Repository/OAuthAccessTokenRepository.php b/src/App/src/Repository/OAuthAccessTokenRepository.php index bd6c04a..5f3fd13 100644 --- a/src/App/src/Repository/OAuthAccessTokenRepository.php +++ b/src/App/src/Repository/OAuthAccessTokenRepository.php @@ -14,8 +14,14 @@ use League\OAuth2\Server\Entities\UserEntityInterface; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; +/** + * @extends EntityRepository + */ class OAuthAccessTokenRepository extends EntityRepository implements AccessTokenRepositoryInterface { + /** + * @return OAuthAccessToken[] + */ public function findAccessTokens(string $identifier): array { return $this @@ -30,6 +36,9 @@ public function findAccessTokens(string $identifier): array ->getResult(); } + /** + * @param mixed $userIdentifier + */ public function getNewToken( ClientEntityInterface $clientEntity, array $scopes, @@ -40,7 +49,7 @@ public function getNewToken( $accessToken->addScope($scope); } - if (is_null($userIdentifier)) { + if ($userIdentifier === null) { return $accessToken; } @@ -64,6 +73,9 @@ public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEnt $this->getEntityManager()->flush(); } + /** + * @param string $tokenId + */ public function revokeAccessToken($tokenId): void { $accessTokenEntity = $this->findOneBy(['token' => $tokenId]); @@ -73,6 +85,9 @@ public function revokeAccessToken($tokenId): void } } + /** + * @param string $tokenId + */ public function isAccessTokenRevoked($tokenId): bool { $accessTokenEntity = $this->findOneBy(['token' => $tokenId]); diff --git a/src/App/src/Repository/OAuthAuthCodeRepository.php b/src/App/src/Repository/OAuthAuthCodeRepository.php index 0da25ea..6fc8ee7 100644 --- a/src/App/src/Repository/OAuthAuthCodeRepository.php +++ b/src/App/src/Repository/OAuthAuthCodeRepository.php @@ -10,8 +10,7 @@ use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface; /** - * Class OAuthAuthCodeRepository - * @package Api\App\Repository + * @extends EntityRepository */ class OAuthAuthCodeRepository extends EntityRepository implements AuthCodeRepositoryInterface { @@ -26,6 +25,9 @@ public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity): voi $this->getEntityManager()->flush(); } + /** + * @param string $codeId + */ public function revokeAuthCode($codeId): void { $authCodeEntity = $this->find($codeId); @@ -35,6 +37,9 @@ public function revokeAuthCode($codeId): void } } + /** + * @param string $codeId + */ public function isAuthCodeRevoked($codeId): bool { $authCodeEntity = $this->find($codeId); diff --git a/src/App/src/Repository/OAuthClientRepository.php b/src/App/src/Repository/OAuthClientRepository.php index 803205a..fe2233d 100644 --- a/src/App/src/Repository/OAuthClientRepository.php +++ b/src/App/src/Repository/OAuthClientRepository.php @@ -8,36 +8,47 @@ use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Repositories\ClientRepositoryInterface; +use function in_array; +use function password_verify; + /** - * @psalm-suppress UndefinedInterfaceMethod + * @extends EntityRepository */ class OAuthClientRepository extends EntityRepository implements ClientRepositoryInterface { private const GRANT_TYPE_CLIENT_CREDENTIALS = 'client_credentials'; private const GRANT_TYPE_AUTHORIZATION_CODE = 'authorization_code'; - private const GRANT_TYPE_REFRESH_TOKEN = 'refresh_token'; - private const GRANT_TYPE_PASSWORD = 'password'; + private const GRANT_TYPE_REFRESH_TOKEN = 'refresh_token'; + private const GRANT_TYPE_PASSWORD = 'password'; private const GRANT_TYPES = [ self::GRANT_TYPE_CLIENT_CREDENTIALS, self::GRANT_TYPE_AUTHORIZATION_CODE, self::GRANT_TYPE_REFRESH_TOKEN, - self::GRANT_TYPE_PASSWORD + self::GRANT_TYPE_PASSWORD, ]; + /** + * @param string $clientIdentifier + */ public function getClientEntity($clientIdentifier): ?ClientEntityInterface { return $this->findOneBy(['name' => $clientIdentifier]); } + /** + * @param string $clientIdentifier + * @param null|string $clientSecret + * @param null|string $grantType + */ public function validateClient($clientIdentifier, $clientSecret, $grantType): bool { $client = $this->getClientEntity($clientIdentifier); - if (!$client instanceof ClientEntityInterface) { + if (! $client instanceof ClientEntityInterface) { return false; } - if (!$this->isGranted($grantType)) { + if (! $this->isGranted($grantType)) { return false; } @@ -45,10 +56,10 @@ public function validateClient($clientIdentifier, $clientSecret, $grantType): bo return false; } - return password_verify((string)$clientSecret, $client->getSecret()); + return password_verify((string) $clientSecret, $client->getSecret()); } - private function isGranted(string $grantType = null): bool + private function isGranted(?string $grantType = null): bool { return in_array($grantType, self::GRANT_TYPES); } diff --git a/src/App/src/Repository/OAuthRefreshTokenRepository.php b/src/App/src/Repository/OAuthRefreshTokenRepository.php index 227bc3e..089cebf 100644 --- a/src/App/src/Repository/OAuthRefreshTokenRepository.php +++ b/src/App/src/Repository/OAuthRefreshTokenRepository.php @@ -9,6 +9,9 @@ use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; +/** + * @extends EntityRepository + */ class OAuthRefreshTokenRepository extends EntityRepository implements RefreshTokenRepositoryInterface { public function getNewRefreshToken(): OAuthRefreshToken @@ -22,6 +25,9 @@ public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshToken $this->getEntityManager()->flush(); } + /** + * @param string $tokenId + */ public function revokeRefreshToken($tokenId): void { $refreshTokenEntity = $this->find($tokenId); @@ -31,6 +37,9 @@ public function revokeRefreshToken($tokenId): void } } + /** + * @param string $tokenId + */ public function isRefreshTokenRevoked($tokenId): bool { $refreshTokenEntity = $this->find($tokenId); diff --git a/src/App/src/Repository/OAuthScopeRepository.php b/src/App/src/Repository/OAuthScopeRepository.php index d719195..3c2f721 100644 --- a/src/App/src/Repository/OAuthScopeRepository.php +++ b/src/App/src/Repository/OAuthScopeRepository.php @@ -9,13 +9,24 @@ use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; +/** + * @extends EntityRepository + */ class OAuthScopeRepository extends EntityRepository implements ScopeRepositoryInterface { + /** + * @param string $identifier + */ public function getScopeEntityByIdentifier($identifier): ?ScopeEntityInterface { return $this->findOneBy(['scope' => $identifier]); } + /** + * @param string $grantType + * @param null|string $userIdentifier + * @return ScopeEntityInterface[] + */ public function finalizeScopes( array $scopes, $grantType, diff --git a/src/App/src/RoutesDelegator.php b/src/App/src/RoutesDelegator.php index d2f0ef3..68b6596 100644 --- a/src/App/src/RoutesDelegator.php +++ b/src/App/src/RoutesDelegator.php @@ -23,28 +23,44 @@ public function __invoke(ContainerInterface $container, string $serviceName, cal /** * Home page */ - $app->get('/', function () { - return new JsonResponse(['message' => 'Welcome to DotKernel API!']); - }, 'home'); + $app->get( + '/', + function () { + return new JsonResponse(['message' => 'Welcome to DotKernel API!']); + }, + 'home' + ); /** * OAuth authentication */ - $app->post('/security/generate-token', [ - ErrorResponseMiddleware::class, - TokenEndpointHandler::class - ], 'security.generate-token'); - $app->post('/security/refresh-token', [ - ErrorResponseMiddleware::class, - TokenEndpointHandler::class - ], 'security.refresh-token'); + $app->post( + '/security/generate-token', + [ + ErrorResponseMiddleware::class, + TokenEndpointHandler::class, + ], + 'security.generate-token' + ); + $app->post( + '/security/refresh-token', + [ + ErrorResponseMiddleware::class, + TokenEndpointHandler::class, + ], + 'security.refresh-token' + ); /** * Other application reports an error */ - $app->post('/error-report', [ - ErrorReportHandler::class - ], 'error.report'); + $app->post( + '/error-report', + [ + ErrorReportHandler::class, + ], + 'error.report' + ); return $app; } diff --git a/src/App/src/Service/ErrorReportService.php b/src/App/src/Service/ErrorReportService.php index 4968959..0a90466 100644 --- a/src/App/src/Service/ErrorReportService.php +++ b/src/App/src/Service/ErrorReportService.php @@ -11,10 +11,22 @@ use Psr\Http\Message\ServerRequestInterface; use Symfony\Component\Filesystem\Filesystem; +use function array_intersect; +use function array_key_exists; +use function date; +use function in_array; +use function parse_url; +use function sha1; +use function sprintf; +use function uniqid; + +use const PHP_EOL; +use const PHP_URL_HOST; + class ErrorReportService implements ErrorReportServiceInterface { - private FileSystem $fileSystem; private const HEADER_NAME = 'Error-Reporting-Token'; + private Filesystem $fileSystem; private ?string $token = null; /** @@ -26,7 +38,7 @@ public function __construct( protected array $config ) { $this->fileSystem = new Filesystem(); - $this->config = $config[ErrorReportServiceInterface::class] ?? []; + $this->config = $config[ErrorReportServiceInterface::class] ?? []; } /** @@ -47,11 +59,11 @@ public function checkRequest(ServerRequestInterface $request): self { $this->validateConfigs(); - if (!$this->hasValidToken($request)) { + if (! $this->hasValidToken($request)) { throw new ForbiddenException(Message::ERROR_REPORT_NOT_ALLOWED); } - if (!$this->isMatchingDomain($request) && !$this->isMatchingIpAddress($request)) { + if (! $this->isMatchingDomain($request) && ! $this->isMatchingIpAddress($request)) { throw new ForbiddenException(Message::ERROR_REPORT_NOT_ALLOWED); } @@ -74,7 +86,7 @@ private function hasValidToken(ServerRequestInterface $request): bool } $this->token = $tokens[0]; - if (!in_array($this->token, $this->config['tokens'])) { + if (! in_array($this->token, $this->config['tokens'])) { return false; } @@ -87,12 +99,10 @@ private function hasValidToken(ServerRequestInterface $request): bool private function isMatchingDomain(ServerRequestInterface $request): bool { $domain = parse_url($request->getServerParams()['HTTP_ORIGIN'] ?? '', PHP_URL_HOST); + $intersection = array_intersect($this->config['domain_whitelist'], ['*', $domain]); - if (empty($intersection)) { - return false; - } - return true; + return ! empty($intersection); } /** @@ -101,12 +111,10 @@ private function isMatchingDomain(ServerRequestInterface $request): bool private function isMatchingIpAddress(ServerRequestInterface $request): bool { $ipAddress = $request->getServerParams()['REMOTE_ADDR'] ?? null; + $intersection = array_intersect($this->config['ip_whitelist'], ['*', $ipAddress]); - if (empty($intersection)) { - return false; - } - return true; + return ! empty($intersection); } /** @@ -114,7 +122,7 @@ private function isMatchingIpAddress(ServerRequestInterface $request): bool */ private function validateConfigs(): void { - if (!array_key_exists('enabled', $this->config)) { + if (! array_key_exists('enabled', $this->config)) { throw new Exception( sprintf(Message::MISSING_CONFIG, 'config.ErrorReportServiceInterface::class.enabled') ); @@ -124,7 +132,7 @@ private function validateConfigs(): void throw new Exception(Message::ERROR_REPORT_NOT_ENABLED); } - if (!array_key_exists('path', $this->config)) { + if (! array_key_exists('path', $this->config)) { throw new Exception( sprintf(Message::MISSING_CONFIG, 'config.ErrorReportServiceInterface::class.path') ); @@ -136,7 +144,7 @@ private function validateConfigs(): void ); } - if (!array_key_exists('tokens', $this->config)) { + if (! array_key_exists('tokens', $this->config)) { throw new Exception( sprintf(Message::MISSING_CONFIG, 'config.ErrorReportServiceInterface::class.tokens') ); @@ -148,7 +156,7 @@ private function validateConfigs(): void ); } - if (!array_key_exists('domain_whitelist', $this->config)) { + if (! array_key_exists('domain_whitelist', $this->config)) { throw new Exception( sprintf( Message::MISSING_CONFIG, @@ -157,7 +165,7 @@ private function validateConfigs(): void ); } - if (!array_key_exists('ip_whitelist', $this->config)) { + if (! array_key_exists('ip_whitelist', $this->config)) { throw new Exception( sprintf( Message::MISSING_CONFIG, diff --git a/src/App/src/UserIdentity.php b/src/App/src/UserIdentity.php index 2d8d455..1760e1c 100644 --- a/src/App/src/UserIdentity.php +++ b/src/App/src/UserIdentity.php @@ -8,66 +8,41 @@ class UserIdentity implements UserInterface { - /** @var string $identity */ protected string $identity; - /** @var iterable $roles */ protected array $roles; - - /** @var array $details */ protected array $details; - /** - * UserIdentity constructor. - * @param string $identity - * @param array $roles - * @param array $details - */ public function __construct(string $identity, array $roles = [], array $details = []) { $this->identity = $identity; - $this->roles = $roles; - $this->details = $details; + $this->roles = $roles; + $this->details = $details; } - /** - * Get the unique user identity (id, username, email address or ...) - */ public function getIdentity(): string { return $this->identity; } - /** - * @psalm-return iterable - * @return iterable - */ public function getRoles(): iterable { return $this->roles; } /** - * @param string $name - * @param mixed|null $default - * @return mixed + * @param null|mixed $default */ public function getDetail(string $name, $default = null): mixed { return $this->details[$name] ?? $default; } - /** - * @psalm-return array - */ public function getDetails(): array { return $this->details; } - /** - * @param array $roles - */ public function setRoles(array $roles): void { $this->roles = $roles; diff --git a/src/User/src/Collection/UserCollection.php b/src/User/src/Collection/UserCollection.php index 140f547..d002444 100644 --- a/src/User/src/Collection/UserCollection.php +++ b/src/User/src/Collection/UserCollection.php @@ -7,4 +7,5 @@ use Api\App\Collection\ResourceCollection; class UserCollection extends ResourceCollection -{} +{ +} diff --git a/src/User/src/Collection/UserRoleCollection.php b/src/User/src/Collection/UserRoleCollection.php index 8a42490..ccc7fbf 100644 --- a/src/User/src/Collection/UserRoleCollection.php +++ b/src/User/src/Collection/UserRoleCollection.php @@ -7,4 +7,5 @@ use Api\App\Collection\ResourceCollection; class UserRoleCollection extends ResourceCollection -{} +{ +} diff --git a/src/User/src/ConfigProvider.php b/src/User/src/ConfigProvider.php index 565574c..4f7eede 100644 --- a/src/User/src/ConfigProvider.php +++ b/src/User/src/ConfigProvider.php @@ -38,9 +38,9 @@ class ConfigProvider public function __invoke(): array { return [ - 'dependencies' => $this->getDependencies(), + 'dependencies' => $this->getDependencies(), MetadataMap::class => $this->getHalConfig(), - 'templates' => $this->getTemplates() + 'templates' => $this->getTemplates(), ]; } @@ -48,27 +48,27 @@ public function getDependencies(): array { return [ 'factories' => [ - AccountActivateHandler::class => AnnotatedServiceFactory::class, - AccountAvatarHandler::class => AnnotatedServiceFactory::class, - AccountHandler::class => AnnotatedServiceFactory::class, + AccountActivateHandler::class => AnnotatedServiceFactory::class, + AccountAvatarHandler::class => AnnotatedServiceFactory::class, + AccountHandler::class => AnnotatedServiceFactory::class, AccountResetPasswordHandler::class => AnnotatedServiceFactory::class, - AccountRecoveryHandler::class => AnnotatedServiceFactory::class, - UserActivateHandler::class => AnnotatedServiceFactory::class, - UserAvatarHandler::class => AnnotatedServiceFactory::class, - UserHandler::class => AnnotatedServiceFactory::class, - UserRoleHandler::class => AnnotatedServiceFactory::class, - UserService::class => AnnotatedServiceFactory::class, - UserRoleService::class => AnnotatedServiceFactory::class, - UserAvatarService::class => AnnotatedServiceFactory::class, - UserRepository::class => AnnotatedRepositoryFactory::class, - UserDetailRepository::class => AnnotatedRepositoryFactory::class, - UserRoleRepository::class => AnnotatedRepositoryFactory::class, - UserAvatarRepository::class => AnnotatedRepositoryFactory::class, + AccountRecoveryHandler::class => AnnotatedServiceFactory::class, + UserActivateHandler::class => AnnotatedServiceFactory::class, + UserAvatarHandler::class => AnnotatedServiceFactory::class, + UserHandler::class => AnnotatedServiceFactory::class, + UserRoleHandler::class => AnnotatedServiceFactory::class, + UserService::class => AnnotatedServiceFactory::class, + UserRoleService::class => AnnotatedServiceFactory::class, + UserAvatarService::class => AnnotatedServiceFactory::class, + UserRepository::class => AnnotatedRepositoryFactory::class, + UserDetailRepository::class => AnnotatedRepositoryFactory::class, + UserRoleRepository::class => AnnotatedRepositoryFactory::class, + UserAvatarRepository::class => AnnotatedRepositoryFactory::class, ], - 'aliases' => [ + 'aliases' => [ UserAvatarServiceInterface::class => UserAvatarService::class, - UserRoleServiceInterface::class => UserRoleService::class, - UserServiceInterface::class => UserService::class, + UserRoleServiceInterface::class => UserRoleService::class, + UserServiceInterface::class => UserService::class, ], ]; } @@ -88,8 +88,10 @@ public function getTemplates(): array { return [ 'paths' => [ - 'user' => [__DIR__ . '/../templates/user'] - ] + 'user' => [ + __DIR__ . '/../templates/user', + ], + ], ]; } } diff --git a/src/User/src/Entity/User.php b/src/User/src/Entity/User.php index c593125..67aa64f 100644 --- a/src/User/src/Entity/User.php +++ b/src/User/src/Entity/User.php @@ -10,41 +10,36 @@ use Api\App\Entity\UuidOrderedTimeGenerator; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; -use League\OAuth2\Server\Entities\UserEntityInterface; use Doctrine\ORM\Mapping as ORM; +use League\OAuth2\Server\Entities\UserEntityInterface; use Throwable; +use function bin2hex; +use function random_bytes; + /** - * Class User * @ORM\Entity(repositoryClass="Api\User\Repository\UserRepository") * @ORM\Table(name="user") * @ORM\HasLifecycleCallbacks() - * @package Api\User\Entity */ class User extends AbstractEntity implements UserEntityInterface { use PasswordTrait; public const STATUS_PENDING = 'pending'; - public const STATUS_ACTIVE = 'active'; - public const STATUSES = [ + public const STATUS_ACTIVE = 'active'; + public const STATUSES = [ self::STATUS_PENDING, - self::STATUS_ACTIVE + self::STATUS_ACTIVE, ]; - /** - * @ORM\OneToOne(targetEntity="UserAvatar", cascade={"persist", "remove"}, mappedBy="user") - */ + /** @ORM\OneToOne(targetEntity="UserAvatar", cascade={"persist", "remove"}, mappedBy="user") */ protected ?UserAvatar $avatar = null; - /** - * @ORM\OneToOne(targetEntity="UserDetail", cascade={"persist", "remove"}, mappedBy="user") - */ + /** @ORM\OneToOne(targetEntity="UserDetail", cascade={"persist", "remove"}, mappedBy="user") */ protected UserDetail $detail; - /** - * @ORM\OneToMany(targetEntity="UserResetPasswordEntity", cascade={"persist", "remove"}, mappedBy="user") - */ + /** @ORM\OneToMany(targetEntity="UserResetPasswordEntity", cascade={"persist", "remove"}, mappedBy="user") */ protected Collection $resetPasswords; /** @@ -57,36 +52,26 @@ class User extends AbstractEntity implements UserEntityInterface */ protected Collection $roles; - /** - * @ORM\Column(name="identity", type="string", length=191, unique=true) - */ + /** @ORM\Column(name="identity", type="string", length=191, unique=true) */ protected string $identity; - /** - * @ORM\Column(name="password", type="string", length=191) - */ + /** @ORM\Column(name="password", type="string", length=191) */ protected string $password; - /** - * @ORM\Column(name="status", type="string", length=20) - */ + /** @ORM\Column(name="status", type="string", length=20) */ protected string $status = self::STATUS_PENDING; - /** - * @ORM\Column(name="isDeleted", type="boolean") - */ + /** @ORM\Column(name="isDeleted", type="boolean") */ protected bool $isDeleted = false; - /** - * @ORM\Column(name="hash", type="string", length=64, unique=true) - */ + /** @ORM\Column(name="hash", type="string", length=64, unique=true) */ protected string $hash; public function __construct() { parent::__construct(); - $this->roles = new ArrayCollection(); + $this->roles = new ArrayCollection(); $this->resetPasswords = new ArrayCollection(); $this->renewHash(); @@ -270,10 +255,6 @@ public function setResetPasswords(array $resetPasswords): self return $this; } - /** - * Helper methods - */ - public function activate(): self { return $this->setStatus(self::STATUS_ACTIVE); @@ -338,27 +319,27 @@ public function hasRoles(): bool public function hasEmail(): bool { - return !empty($this->getDetail()->getEmail()); + return ! empty($this->getDetail()->getEmail()); } public function getArrayCopy(): array { return [ - 'uuid' => $this->getUuid()->toString(), - 'hash' => $this->getHash(), - 'identity' => $this->getIdentity(), - 'status' => $this->getStatus(), - 'isDeleted' => $this->isDeleted(), - 'avatar' => $this->getAvatar()?->getArrayCopy(), - 'detail' => $this->getDetail()->getArrayCopy(), - 'roles' => $this->getRoles()->map(function (UserRole $userRole) { + 'uuid' => $this->getUuid()->toString(), + 'hash' => $this->getHash(), + 'identity' => $this->getIdentity(), + 'status' => $this->getStatus(), + 'isDeleted' => $this->isDeleted(), + 'avatar' => $this->getAvatar()?->getArrayCopy(), + 'detail' => $this->getDetail()->getArrayCopy(), + 'roles' => $this->getRoles()->map(function (UserRole $userRole) { return $userRole->getArrayCopy(); })->toArray(), 'resetPasswords' => $this->getResetPasswords()->map(function (UserResetPasswordEntity $resetPassword) { return $resetPassword->getArrayCopy(); })->toArray(), - 'created' => $this->getCreated(), - 'updated' => $this->getUpdated(), + 'created' => $this->getCreated(), + 'updated' => $this->getUpdated(), ]; } } diff --git a/src/User/src/Entity/UserAvatar.php b/src/User/src/Entity/UserAvatar.php index 736ee2b..11d8b25 100644 --- a/src/User/src/Entity/UserAvatar.php +++ b/src/User/src/Entity/UserAvatar.php @@ -9,12 +9,10 @@ use Doctrine\ORM\Mapping as ORM; /** - * Class UserAvatar * @ORM\Entity(repositoryClass="Api\User\Repository\UserAvatarRepository") * @ORM\Table(name="user_avatar") * @ORM\HasLifecycleCallbacks() * @ORM\EntityListeners({UserAvatarEventListener::class}) - * @package Api\User\Entity */ class UserAvatar extends AbstractEntity { @@ -24,9 +22,7 @@ class UserAvatar extends AbstractEntity */ protected User $user; - /** - * @ORM\Column(name="name", type="string", length=191) - */ + /** @ORM\Column(name="name", type="string", length=191) */ protected string $name; protected ?string $url = null; @@ -55,10 +51,6 @@ public function setName(string $name): UserAvatar return $this; } - /** - * Helper methods - */ - public function getUrl(): ?string { return $this->url; @@ -74,10 +66,10 @@ public function setUrl(string $url): UserAvatar public function getArrayCopy(): array { return [ - 'uuid' => $this->getUuid()->toString(), - 'url' => $this->getUrl(), + 'uuid' => $this->getUuid()->toString(), + 'url' => $this->getUrl(), 'created' => $this->getCreated(), - 'updated' => $this->getUpdated() + 'updated' => $this->getUpdated(), ]; } } diff --git a/src/User/src/Entity/UserDetail.php b/src/User/src/Entity/UserDetail.php index 06da694..e6a02c0 100644 --- a/src/User/src/Entity/UserDetail.php +++ b/src/User/src/Entity/UserDetail.php @@ -8,11 +8,9 @@ use Doctrine\ORM\Mapping as ORM; /** - * Class UserDetail * @ORM\Entity(repositoryClass="Api\User\Repository\UserDetailRepository") * @ORM\Table(name="user_detail") * @ORM\HasLifecycleCallbacks() - * @package Api\User\Entity */ class UserDetail extends AbstractEntity { @@ -22,19 +20,13 @@ class UserDetail extends AbstractEntity */ protected User $user; - /** - * @ORM\Column(name="firstName", type="string", length=191, nullable=true) - */ + /** @ORM\Column(name="firstName", type="string", length=191, nullable=true) */ protected ?string $firstName = null; - /** - * @ORM\Column(name="lastName", type="string", length=191, nullable=true) - */ + /** @ORM\Column(name="lastName", type="string", length=191, nullable=true) */ protected ?string $lastName = null; - /** - * @ORM\Column(name="email", type="string", length=191) - */ + /** @ORM\Column(name="email", type="string", length=191) */ protected string $email; public function getUser(): User @@ -89,10 +81,10 @@ public function getArrayCopy(): array { return [ 'firstName' => $this->getFirstName(), - 'lastName' => $this->getLastName(), - 'email' => $this->getEmail(), - 'created' => $this->getCreated(), - 'updated' => $this->getUpdated() + 'lastName' => $this->getLastName(), + 'email' => $this->getEmail(), + 'created' => $this->getCreated(), + 'updated' => $this->getUpdated(), ]; } } diff --git a/src/User/src/Entity/UserResetPasswordEntity.php b/src/User/src/Entity/UserResetPasswordEntity.php index dc3fc8d..4b10da6 100644 --- a/src/User/src/Entity/UserResetPasswordEntity.php +++ b/src/User/src/Entity/UserResetPasswordEntity.php @@ -12,19 +12,17 @@ use Throwable; /** - * Class UserResetPasswordEntity * @ORM\Entity() * @ORM\Table(name="user_reset_password") * @ORM\HasLifecycleCallbacks() - * @package Api\User\Entity */ class UserResetPasswordEntity extends AbstractEntity { public const STATUS_COMPLETED = 'completed'; public const STATUS_REQUESTED = 'requested'; - public const STATUSES = [ + public const STATUSES = [ self::STATUS_COMPLETED, - self::STATUS_REQUESTED + self::STATUS_REQUESTED, ]; /** @@ -33,19 +31,13 @@ class UserResetPasswordEntity extends AbstractEntity */ protected User $user; - /** - * @ORM\Column(name="expires", type="datetime_immutable") - */ + /** @ORM\Column(name="expires", type="datetime_immutable") */ protected DateTimeImmutable $expires; - /** - * @ORM\Column(name="hash", type="string", length=64, unique=true) - */ + /** @ORM\Column(name="hash", type="string", length=64, unique=true) */ protected string $hash; - /** - * @ORM\Column(name="status", type="string", length=20) - */ + /** @ORM\Column(name="status", type="string", length=20) */ protected string $status = self::STATUS_REQUESTED; public function __construct() @@ -105,10 +97,6 @@ public function setStatus(string $status): self return $this; } - /** - * Helper methods - */ - public function isCompleted(): bool { return $this->getStatus() === self::STATUS_COMPLETED; @@ -117,7 +105,7 @@ public function isCompleted(): bool public function isValid(): bool { try { - return $this->getExpires() > (new DateTimeImmutable()); + return $this->getExpires() > new DateTimeImmutable(); } catch (Throwable) { } @@ -134,12 +122,12 @@ public function markAsCompleted(): self public function getArrayCopy(): array { return [ - 'uuid' => $this->getUuid()->toString(), + 'uuid' => $this->getUuid()->toString(), 'expires' => $this->getExpires(), - 'hash' => $this->getHash(), - 'status' => $this->getStatus(), + 'hash' => $this->getHash(), + 'status' => $this->getStatus(), 'created' => $this->getCreated(), - 'updated' => $this->getUpdated() + 'updated' => $this->getUpdated(), ]; } } diff --git a/src/User/src/Entity/UserRole.php b/src/User/src/Entity/UserRole.php index 3e5d074..7d6a3c9 100644 --- a/src/User/src/Entity/UserRole.php +++ b/src/User/src/Entity/UserRole.php @@ -9,24 +9,20 @@ use Doctrine\ORM\Mapping as ORM; /** - * Class UserRole * @ORM\Entity(repositoryClass="Api\User\Repository\UserRoleRepository") * @ORM\Table(name="user_role") * @ORM\HasLifecycleCallbacks() - * @package Api\User\Entity */ class UserRole extends AbstractEntity implements RoleInterface { public const ROLE_GUEST = 'guest'; - public const ROLE_USER = 'user'; - public const ROLES = [ + public const ROLE_USER = 'user'; + public const ROLES = [ self::ROLE_GUEST, - self::ROLE_USER + self::ROLE_USER, ]; - /** - * @ORM\Column(name="name", type="string", length=20, unique=true) - */ + /** @ORM\Column(name="name", type="string", length=20, unique=true) */ protected string $name; public function getName(): string diff --git a/src/User/src/EventListener/UserAvatarEventListener.php b/src/User/src/EventListener/UserAvatarEventListener.php index 17a4f59..7a65083 100644 --- a/src/User/src/EventListener/UserAvatarEventListener.php +++ b/src/User/src/EventListener/UserAvatarEventListener.php @@ -8,6 +8,9 @@ use Dot\AnnotatedServices\Annotation\Inject; use Dot\AnnotatedServices\Annotation\Service; +use function rtrim; +use function sprintf; + /** * @Service */ @@ -20,7 +23,8 @@ class UserAvatarEventListener */ public function __construct( protected array $config = [] - ) {} + ) { + } public function postLoad(UserAvatar $avatar): void { diff --git a/src/User/src/Handler/AccountActivateHandler.php b/src/User/src/Handler/AccountActivateHandler.php index f456c84..9b398e8 100644 --- a/src/User/src/Handler/AccountActivateHandler.php +++ b/src/User/src/Handler/AccountActivateHandler.php @@ -18,6 +18,8 @@ use Psr\Http\Server\RequestHandlerInterface; use Throwable; +use function sprintf; + class AccountActivateHandler implements RequestHandlerInterface { use ResponseTrait; @@ -35,13 +37,14 @@ public function __construct( protected ResourceGenerator $resourceGenerator, protected UserServiceInterface $userService, protected UrlHelper $urlHelper - ) {} + ) { + } public function patch(ServerRequestInterface $request): ResponseInterface { $hash = $request->getAttribute('hash'); $user = $this->userService->findOneBy(['hash' => $hash]); - if (!$user instanceof User) { + if (! $user instanceof User) { return $this->errorResponse(Message::INVALID_ACTIVATION_CODE); } @@ -61,13 +64,13 @@ public function patch(ServerRequestInterface $request): ResponseInterface public function post(ServerRequestInterface $request): ResponseInterface { $inputFilter = (new ActivateAccountInputFilter())->setData($request->getParsedBody()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } $email = $inputFilter->getValue('email'); - $user = $this->userService->findByEmail($email); - if (!$user instanceof User) { + $user = $this->userService->findByEmail($email); + if (! $user instanceof User) { return $this->notFoundResponse( sprintf(Message::USER_NOT_FOUND_BY_EMAIL, $email) ); diff --git a/src/User/src/Handler/AccountAvatarHandler.php b/src/User/src/Handler/AccountAvatarHandler.php index 47ea3dd..0883a17 100644 --- a/src/User/src/Handler/AccountAvatarHandler.php +++ b/src/User/src/Handler/AccountAvatarHandler.php @@ -32,14 +32,15 @@ public function __construct( protected HalResponseFactory $responseFactory, protected ResourceGenerator $resourceGenerator, protected UserAvatarServiceInterface $userAvatarService - ) {} + ) { + } public function delete(ServerRequestInterface $request): ResponseInterface { try { /** @var User $user */ $user = $request->getAttribute(User::class); - if (!$user->hasAvatar()) { + if (! $user->hasAvatar()) { return $this->notFoundResponse(Message::AVATAR_MISSING); } @@ -55,7 +56,7 @@ public function get(ServerRequestInterface $request): ResponseInterface { /** @var User $user */ $user = $request->getAttribute(User::class); - if (!$user->hasAvatar()) { + if (! $user->hasAvatar()) { return $this->notFoundResponse(Message::AVATAR_MISSING); } @@ -65,7 +66,7 @@ public function get(ServerRequestInterface $request): ResponseInterface public function post(ServerRequestInterface $request): ResponseInterface { $inputFilter = (new UpdateAvatarInputFilter())->setData($request->getUploadedFiles()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } diff --git a/src/User/src/Handler/AccountHandler.php b/src/User/src/Handler/AccountHandler.php index 7d617e7..d09497f 100644 --- a/src/User/src/Handler/AccountHandler.php +++ b/src/User/src/Handler/AccountHandler.php @@ -5,11 +5,11 @@ namespace Api\User\Handler; use Api\App\Handler\ResponseTrait; +use Api\User\Entity\User; use Api\User\InputFilter\CreateUserInputFilter; use Api\User\InputFilter\UpdateUserInputFilter; use Api\User\Service\UserServiceInterface; use Dot\AnnotatedServices\Annotation\Inject; -use Api\User\Entity\User; use Mezzio\Hal\HalResponseFactory; use Mezzio\Hal\ResourceGenerator; use Psr\Http\Message\ResponseInterface; @@ -32,7 +32,8 @@ public function __construct( protected HalResponseFactory $responseFactory, protected ResourceGenerator $resourceGenerator, protected UserServiceInterface $userService - ) {} + ) { + } public function delete(ServerRequestInterface $request): ResponseInterface { @@ -55,7 +56,7 @@ public function patch(ServerRequestInterface $request): ResponseInterface $inputFilter = (new UpdateUserInputFilter()) ->setValidationGroup(['password', 'passwordConfirm', 'detail']) ->setData($request->getParsedBody()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } @@ -73,7 +74,7 @@ public function post(ServerRequestInterface $request): ResponseInterface $inputFilter = (new CreateUserInputFilter()) ->setValidationGroup(['identity', 'password', 'passwordConfirm', 'detail']) ->setData($request->getParsedBody()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } diff --git a/src/User/src/Handler/AccountRecoveryHandler.php b/src/User/src/Handler/AccountRecoveryHandler.php index 01a3b3d..0071fed 100644 --- a/src/User/src/Handler/AccountRecoveryHandler.php +++ b/src/User/src/Handler/AccountRecoveryHandler.php @@ -18,6 +18,8 @@ use Psr\Http\Server\RequestHandlerInterface; use Throwable; +use function sprintf; + class AccountRecoveryHandler implements RequestHandlerInterface { use ResponseTrait; @@ -33,12 +35,13 @@ public function __construct( protected HalResponseFactory $responseFactory, protected ResourceGenerator $resourceGenerator, protected UserServiceInterface $userService - ) {} + ) { + } public function post(ServerRequestInterface $request): ResponseInterface { $inputFilter = (new RecoverIdentityInputFilter())->setData($request->getParsedBody()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } @@ -46,7 +49,7 @@ public function post(ServerRequestInterface $request): ResponseInterface $email = $inputFilter->getValue('email'); $user = $this->userService->findByEmail($email); - if (!$user instanceof User) { + if (! $user instanceof User) { throw new Exception( sprintf(Message::USER_NOT_FOUND_BY_EMAIL, $email) ); diff --git a/src/User/src/Handler/AccountResetPasswordHandler.php b/src/User/src/Handler/AccountResetPasswordHandler.php index 17536a3..79c2477 100644 --- a/src/User/src/Handler/AccountResetPasswordHandler.php +++ b/src/User/src/Handler/AccountResetPasswordHandler.php @@ -19,6 +19,8 @@ use Psr\Http\Server\RequestHandlerInterface; use Throwable; +use function sprintf; + class AccountResetPasswordHandler implements RequestHandlerInterface { use ResponseTrait; @@ -34,13 +36,14 @@ public function __construct( protected HalResponseFactory $responseFactory, protected ResourceGenerator $resourceGenerator, protected UserServiceInterface $userService - ) {} + ) { + } public function get(ServerRequestInterface $request): ResponseInterface { $hash = $request->getAttribute('hash'); $user = $this->userService->findByResetPasswordHash($hash); - if (!$user instanceof User) { + if (! $user instanceof User) { return $this->notFoundResponse( sprintf(Message::RESET_PASSWORD_NOT_FOUND, $hash) ); @@ -49,10 +52,10 @@ public function get(ServerRequestInterface $request): ResponseInterface /** @var UserResetPasswordEntity $resetPassword */ $resetPassword = $user->getResetPasswords()->filter( function (UserResetPasswordEntity $resetPassword) use ($hash) { - return $resetPassword->getHash() == $hash; + return $resetPassword->getHash() === $hash; } )->current(); - if (!$resetPassword->isValid()) { + if (! $resetPassword->isValid()) { return $this->errorResponse(sprintf(Message::RESET_PASSWORD_EXPIRED, $hash)); } if ($resetPassword->isCompleted()) { @@ -66,7 +69,7 @@ public function patch(ServerRequestInterface $request): ResponseInterface { $hash = $request->getAttribute('hash'); $user = $this->userService->findByResetPasswordHash($hash); - if (!$user instanceof User) { + if (! $user instanceof User) { return $this->notFoundResponse( sprintf(Message::RESET_PASSWORD_NOT_FOUND, $hash) ); @@ -75,10 +78,10 @@ public function patch(ServerRequestInterface $request): ResponseInterface /** @var UserResetPasswordEntity $resetPassword */ $resetPassword = $user->getResetPasswords()->filter( function (UserResetPasswordEntity $resetPassword) use ($hash) { - return $resetPassword->getHash() == $hash; + return $resetPassword->getHash() === $hash; } )->current(); - if (!$resetPassword->isValid()) { + if (! $resetPassword->isValid()) { return $this->errorResponse(sprintf(Message::RESET_PASSWORD_EXPIRED, $hash)); } if ($resetPassword->isCompleted()) { @@ -86,7 +89,7 @@ function (UserResetPasswordEntity $resetPassword) use ($hash) { } $inputFilter = (new UpdatePasswordInputFilter())->setData($request->getParsedBody()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } @@ -107,20 +110,20 @@ function (UserResetPasswordEntity $resetPassword) use ($hash) { public function post(ServerRequestInterface $request): ResponseInterface { $inputFilter = (new ResetPasswordInputFilter())->setData($request->getParsedBody()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } try { - if (!empty($inputFilter->getValue('email'))) { + if (! empty($inputFilter->getValue('email'))) { $user = $this->userService->findByEmail($inputFilter->getValue('email')); - } elseif (!empty($inputFilter->getValue('identity'))) { + } elseif (! empty($inputFilter->getValue('identity'))) { $user = $this->userService->findByIdentity($inputFilter->getValue('identity')); } else { $user = null; } - if (!$user instanceof User) { + if (! $user instanceof User) { return $this->infoResponse(Message::MAIL_SENT_RESET_PASSWORD); } diff --git a/src/User/src/Handler/UserActivateHandler.php b/src/User/src/Handler/UserActivateHandler.php index 85e03d6..281f685 100644 --- a/src/User/src/Handler/UserActivateHandler.php +++ b/src/User/src/Handler/UserActivateHandler.php @@ -16,6 +16,8 @@ use Psr\Http\Server\RequestHandlerInterface; use Throwable; +use function sprintf; + class UserActivateHandler implements RequestHandlerInterface { use ResponseTrait; @@ -31,18 +33,15 @@ public function __construct( protected HalResponseFactory $responseFactory, protected ResourceGenerator $resourceGenerator, protected UserServiceInterface $userService - ) {} + ) { + } - /** - * @param ServerRequestInterface $request - * @return ResponseInterface - */ public function post(ServerRequestInterface $request): ResponseInterface { try { $uuid = $request->getAttribute('uuid'); $user = $this->userService->findOneBy(['uuid' => $uuid]); - if (!$user instanceof User) { + if (! $user instanceof User) { return $this->notFoundResponse( sprintf(Message::NOT_FOUND_BY_UUID, 'user', $uuid) ); diff --git a/src/User/src/Handler/UserAvatarHandler.php b/src/User/src/Handler/UserAvatarHandler.php index a8238aa..86b8e9a 100644 --- a/src/User/src/Handler/UserAvatarHandler.php +++ b/src/User/src/Handler/UserAvatarHandler.php @@ -18,6 +18,8 @@ use Psr\Http\Server\RequestHandlerInterface; use Throwable; +use function sprintf; + class UserAvatarHandler implements RequestHandlerInterface { use ResponseTrait; @@ -35,14 +37,15 @@ public function __construct( protected ResourceGenerator $resourceGenerator, protected UserServiceInterface $userService, protected UserAvatarServiceInterface $userAvatarService - ) {} + ) { + } public function delete(ServerRequestInterface $request): ResponseInterface { try { $uuid = $request->getAttribute('uuid'); $user = $this->userService->findOneBy(['uuid' => $uuid]); - if (!$user->hasAvatar()) { + if (! $user->hasAvatar()) { return $this->notFoundResponse(Message::AVATAR_MISSING); } @@ -58,7 +61,7 @@ public function get(ServerRequestInterface $request): ResponseInterface { $uuid = $request->getAttribute('uuid'); $user = $this->userService->findOneBy(['uuid' => $uuid]); - if (!$user->hasAvatar()) { + if (! $user->hasAvatar()) { return $this->notFoundResponse(Message::AVATAR_MISSING); } @@ -68,14 +71,14 @@ public function get(ServerRequestInterface $request): ResponseInterface public function post(ServerRequestInterface $request): ResponseInterface { $inputFilter = (new UpdateAvatarInputFilter())->setData($request->getUploadedFiles()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } try { $uuid = $request->getAttribute('uuid'); $user = $this->userService->findOneBy(['uuid' => $uuid]); - if (!$user instanceof User) { + if (! $user instanceof User) { return $this->notFoundResponse( sprintf(Message::NOT_FOUND_BY_UUID, 'user', $uuid) ); diff --git a/src/User/src/Handler/UserHandler.php b/src/User/src/Handler/UserHandler.php index e033275..8410ce9 100644 --- a/src/User/src/Handler/UserHandler.php +++ b/src/User/src/Handler/UserHandler.php @@ -18,6 +18,8 @@ use Psr\Http\Server\RequestHandlerInterface; use Throwable; +use function sprintf; + class UserHandler implements RequestHandlerInterface { use ResponseTrait; @@ -33,14 +35,15 @@ public function __construct( protected HalResponseFactory $responseFactory, protected ResourceGenerator $resourceGenerator, protected UserServiceInterface $userService - ) {} + ) { + } public function delete(ServerRequestInterface $request): ResponseInterface { try { $uuid = $request->getAttribute('uuid'); $user = $this->userService->findOneBy(['uuid' => $uuid]); - if (!$user instanceof User) { + if (! $user instanceof User) { return $this->notFoundResponse(sprintf(Message::NOT_FOUND_BY_UUID, 'user', $uuid)); } @@ -56,7 +59,7 @@ public function get(ServerRequestInterface $request): ResponseInterface { try { $uuid = $request->getAttribute('uuid'); - if (!empty($uuid)) { + if (! empty($uuid)) { return $this->view($request, $uuid); } @@ -69,14 +72,14 @@ public function get(ServerRequestInterface $request): ResponseInterface public function patch(ServerRequestInterface $request): ResponseInterface { $inputFilter = (new UpdateUserInputFilter())->setData($request->getParsedBody()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } try { $uuid = $request->getAttribute('uuid'); $user = $this->userService->findOneBy(['uuid' => $uuid]); - if (!$user instanceof User) { + if (! $user instanceof User) { return $this->notFoundResponse(sprintf(Message::NOT_FOUND_BY_UUID, 'user', $uuid)); } @@ -91,7 +94,7 @@ public function patch(ServerRequestInterface $request): ResponseInterface public function post(ServerRequestInterface $request): ResponseInterface { $inputFilter = (new CreateUserInputFilter())->setData($request->getParsedBody()); - if (!$inputFilter->isValid()) { + if (! $inputFilter->isValid()) { return $this->errorResponse($inputFilter->getMessages()); } @@ -117,7 +120,7 @@ private function list(ServerRequestInterface $request): ResponseInterface private function view(ServerRequestInterface $request, string $uuid): ResponseInterface { $user = $this->userService->findOneBy(['uuid' => $uuid]); - if (!$user instanceof User) { + if (! $user instanceof User) { return $this->notFoundResponse(sprintf(Message::NOT_FOUND_BY_UUID, 'user', $uuid)); } return $this->createResponse($request, $user); diff --git a/src/User/src/Handler/UserRoleHandler.php b/src/User/src/Handler/UserRoleHandler.php index e0080fa..76dc9d6 100644 --- a/src/User/src/Handler/UserRoleHandler.php +++ b/src/User/src/Handler/UserRoleHandler.php @@ -16,6 +16,8 @@ use Psr\Http\Server\RequestHandlerInterface; use Throwable; +use function sprintf; + class UserRoleHandler implements RequestHandlerInterface { use ResponseTrait; @@ -31,13 +33,14 @@ public function __construct( protected HalResponseFactory $responseFactory, protected ResourceGenerator $resourceGenerator, protected UserRoleServiceInterface $roleService - ) {} + ) { + } public function get(ServerRequestInterface $request): ResponseInterface { try { $uuid = $request->getAttribute('uuid'); - if (!empty($uuid)) { + if (! empty($uuid)) { return $this->view($request, $uuid); } @@ -55,7 +58,7 @@ private function list(ServerRequestInterface $request): ResponseInterface private function view(ServerRequestInterface $request, string $uuid): ResponseInterface { $role = $this->roleService->findOneBy(['uuid' => $uuid]); - if (!($role instanceof UserRole)) { + if (! $role instanceof UserRole) { return $this->notFoundResponse(sprintf(Message::NOT_FOUND_BY_UUID, 'role', $uuid)); } diff --git a/src/User/src/InputFilter/ActivateAccountInputFilter.php b/src/User/src/InputFilter/ActivateAccountInputFilter.php index 28b50ff..31ff9cc 100644 --- a/src/User/src/InputFilter/ActivateAccountInputFilter.php +++ b/src/User/src/InputFilter/ActivateAccountInputFilter.php @@ -7,6 +7,9 @@ use Api\User\InputFilter\Input\EmailInput; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class ActivateAccountInputFilter extends InputFilter { public function __construct() diff --git a/src/User/src/InputFilter/CreateUserDetailInputFilter.php b/src/User/src/InputFilter/CreateUserDetailInputFilter.php index 7db2b77..9441cc8 100644 --- a/src/User/src/InputFilter/CreateUserDetailInputFilter.php +++ b/src/User/src/InputFilter/CreateUserDetailInputFilter.php @@ -9,6 +9,9 @@ use Api\User\InputFilter\Input\LastNameInput; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class CreateUserDetailInputFilter extends InputFilter { public function __construct() diff --git a/src/User/src/InputFilter/CreateUserInputFilter.php b/src/User/src/InputFilter/CreateUserInputFilter.php index 1765fd7..2a5d656 100644 --- a/src/User/src/InputFilter/CreateUserInputFilter.php +++ b/src/User/src/InputFilter/CreateUserInputFilter.php @@ -11,6 +11,9 @@ use Laminas\InputFilter\CollectionInputFilter; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class CreateUserInputFilter extends InputFilter { public function __construct() diff --git a/src/User/src/InputFilter/Input/AvatarInput.php b/src/User/src/InputFilter/Input/AvatarInput.php index e7b8d85..fabe088 100644 --- a/src/User/src/InputFilter/Input/AvatarInput.php +++ b/src/User/src/InputFilter/Input/AvatarInput.php @@ -14,7 +14,7 @@ class AvatarInput extends FileInput { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); @@ -29,9 +29,9 @@ public function __construct(string $name = null, bool $isRequired = true) 'message' => Message::VALIDATOR_REQUIRED_FIELD, ], true) ->attachByName(UploadFile::class, [ - 'message' => Message::VALIDATOR_REQUIRED_UPLOAD + 'message' => Message::VALIDATOR_REQUIRED_UPLOAD, ], true)->attachByName(IsImage::class, [ - 'message' => Message::RESTRICTION_IMAGE + 'message' => Message::RESTRICTION_IMAGE, ], true); } } diff --git a/src/User/src/InputFilter/Input/EmailInput.php b/src/User/src/InputFilter/Input/EmailInput.php index 94847e1..bc6c2eb 100644 --- a/src/User/src/InputFilter/Input/EmailInput.php +++ b/src/User/src/InputFilter/Input/EmailInput.php @@ -10,9 +10,11 @@ use Laminas\InputFilter\Input; use Laminas\Validator\NotEmpty; +use function sprintf; + class EmailInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); diff --git a/src/User/src/InputFilter/Input/FirstNameInput.php b/src/User/src/InputFilter/Input/FirstNameInput.php index 60bf9fc..0670b78 100644 --- a/src/User/src/InputFilter/Input/FirstNameInput.php +++ b/src/User/src/InputFilter/Input/FirstNameInput.php @@ -10,7 +10,7 @@ class FirstNameInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); diff --git a/src/User/src/InputFilter/Input/IdentityInput.php b/src/User/src/InputFilter/Input/IdentityInput.php index 0d8d83b..bf69cd6 100644 --- a/src/User/src/InputFilter/Input/IdentityInput.php +++ b/src/User/src/InputFilter/Input/IdentityInput.php @@ -10,9 +10,11 @@ use Laminas\InputFilter\Input; use Laminas\Validator\NotEmpty; +use function sprintf; + class IdentityInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); diff --git a/src/User/src/InputFilter/Input/LastNameInput.php b/src/User/src/InputFilter/Input/LastNameInput.php index dd65b37..1ccfbe3 100644 --- a/src/User/src/InputFilter/Input/LastNameInput.php +++ b/src/User/src/InputFilter/Input/LastNameInput.php @@ -10,7 +10,7 @@ class LastNameInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); diff --git a/src/User/src/InputFilter/Input/PasswordConfirmInput.php b/src/User/src/InputFilter/Input/PasswordConfirmInput.php index 6d61aa1..c238790 100644 --- a/src/User/src/InputFilter/Input/PasswordConfirmInput.php +++ b/src/User/src/InputFilter/Input/PasswordConfirmInput.php @@ -12,7 +12,7 @@ class PasswordConfirmInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); @@ -24,7 +24,7 @@ public function __construct(string $name = null, bool $isRequired = true) $this->getValidatorChain() ->attachByName(Identical::class, [ - 'token' => 'password', + 'token' => 'password', 'message' => Message::VALIDATOR_PASSWORD_MISMATCH, ], true); } diff --git a/src/User/src/InputFilter/Input/PasswordInput.php b/src/User/src/InputFilter/Input/PasswordInput.php index 8066a41..88f3f0a 100644 --- a/src/User/src/InputFilter/Input/PasswordInput.php +++ b/src/User/src/InputFilter/Input/PasswordInput.php @@ -11,11 +11,13 @@ use Laminas\Validator\NotEmpty; use Laminas\Validator\StringLength; +use function sprintf; + class PasswordInput extends Input { public const PASSWORD_MIN_LENGTH = 6; - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); @@ -27,7 +29,7 @@ public function __construct(string $name = null, bool $isRequired = true) $this->getValidatorChain() ->attachByName(StringLength::class, [ - 'min' => self::PASSWORD_MIN_LENGTH, + 'min' => self::PASSWORD_MIN_LENGTH, 'message' => sprintf(Message::VALIDATOR_MIN_LENGTH, 'Password', self::PASSWORD_MIN_LENGTH), ], true) ->attachByName(NotEmpty::class, [ diff --git a/src/User/src/InputFilter/Input/StatusInput.php b/src/User/src/InputFilter/Input/StatusInput.php index f6ea2f8..3562a6c 100644 --- a/src/User/src/InputFilter/Input/StatusInput.php +++ b/src/User/src/InputFilter/Input/StatusInput.php @@ -11,9 +11,11 @@ use Laminas\InputFilter\Input; use Laminas\Validator\InArray; +use function sprintf; + class StatusInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); @@ -26,7 +28,7 @@ public function __construct(string $name = null, bool $isRequired = true) $this->getValidatorChain() ->attachByName(InArray::class, [ 'haystack' => User::STATUSES, - 'message' => sprintf(Message::INVALID_VALUE, 'status'), + 'message' => sprintf(Message::INVALID_VALUE, 'status'), ], true); } } diff --git a/src/User/src/InputFilter/Input/UuidInput.php b/src/User/src/InputFilter/Input/UuidInput.php index c6cdba2..a520550 100644 --- a/src/User/src/InputFilter/Input/UuidInput.php +++ b/src/User/src/InputFilter/Input/UuidInput.php @@ -10,7 +10,7 @@ class UuidInput extends Input { - public function __construct(string $name = null, bool $isRequired = true) + public function __construct(?string $name = null, bool $isRequired = true) { parent::__construct($name); diff --git a/src/User/src/InputFilter/RecoverIdentityInputFilter.php b/src/User/src/InputFilter/RecoverIdentityInputFilter.php index 845f5a2..a22f161 100644 --- a/src/User/src/InputFilter/RecoverIdentityInputFilter.php +++ b/src/User/src/InputFilter/RecoverIdentityInputFilter.php @@ -7,6 +7,9 @@ use Api\User\InputFilter\Input\EmailInput; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class RecoverIdentityInputFilter extends InputFilter { public function __construct() diff --git a/src/User/src/InputFilter/ResetPasswordInputFilter.php b/src/User/src/InputFilter/ResetPasswordInputFilter.php index c6e3874..b5024a0 100644 --- a/src/User/src/InputFilter/ResetPasswordInputFilter.php +++ b/src/User/src/InputFilter/ResetPasswordInputFilter.php @@ -8,6 +8,9 @@ use Api\User\InputFilter\Input\IdentityInput; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class ResetPasswordInputFilter extends InputFilter { public function __construct() diff --git a/src/User/src/InputFilter/UpdateAvatarInputFilter.php b/src/User/src/InputFilter/UpdateAvatarInputFilter.php index f39888b..22deb0b 100644 --- a/src/User/src/InputFilter/UpdateAvatarInputFilter.php +++ b/src/User/src/InputFilter/UpdateAvatarInputFilter.php @@ -7,6 +7,9 @@ use Api\User\InputFilter\Input\AvatarInput; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class UpdateAvatarInputFilter extends InputFilter { public function __construct() diff --git a/src/User/src/InputFilter/UpdatePasswordInputFilter.php b/src/User/src/InputFilter/UpdatePasswordInputFilter.php index 1c3432c..5a3161b 100644 --- a/src/User/src/InputFilter/UpdatePasswordInputFilter.php +++ b/src/User/src/InputFilter/UpdatePasswordInputFilter.php @@ -8,6 +8,9 @@ use Api\User\InputFilter\Input\PasswordInput; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class UpdatePasswordInputFilter extends InputFilter { public function __construct() diff --git a/src/User/src/InputFilter/UpdateUserDetailInputFilter.php b/src/User/src/InputFilter/UpdateUserDetailInputFilter.php index e68c126..bc46b9e 100644 --- a/src/User/src/InputFilter/UpdateUserDetailInputFilter.php +++ b/src/User/src/InputFilter/UpdateUserDetailInputFilter.php @@ -9,6 +9,9 @@ use Api\User\InputFilter\Input\LastNameInput; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class UpdateUserDetailInputFilter extends InputFilter { public function __construct() diff --git a/src/User/src/InputFilter/UpdateUserInputFilter.php b/src/User/src/InputFilter/UpdateUserInputFilter.php index a1dc7f0..f84a127 100644 --- a/src/User/src/InputFilter/UpdateUserInputFilter.php +++ b/src/User/src/InputFilter/UpdateUserInputFilter.php @@ -10,6 +10,9 @@ use Laminas\InputFilter\CollectionInputFilter; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class UpdateUserInputFilter extends InputFilter { public function __construct() diff --git a/src/User/src/InputFilter/UserRoleInputFilter.php b/src/User/src/InputFilter/UserRoleInputFilter.php index c87df45..df268ff 100644 --- a/src/User/src/InputFilter/UserRoleInputFilter.php +++ b/src/User/src/InputFilter/UserRoleInputFilter.php @@ -7,6 +7,9 @@ use Api\User\InputFilter\Input\UuidInput; use Laminas\InputFilter\InputFilter; +/** + * @extends InputFilter + */ class UserRoleInputFilter extends InputFilter { public function __construct() diff --git a/src/User/src/Repository/UserAvatarRepository.php b/src/User/src/Repository/UserAvatarRepository.php index 94461d8..2537251 100644 --- a/src/User/src/Repository/UserAvatarRepository.php +++ b/src/User/src/Repository/UserAvatarRepository.php @@ -10,6 +10,7 @@ /** * @Entity(name="Api\User\Entity\UserAvatar") + * @extends EntityRepository */ class UserAvatarRepository extends EntityRepository { diff --git a/src/User/src/Repository/UserDetailRepository.php b/src/User/src/Repository/UserDetailRepository.php index df6addd..6ec977c 100644 --- a/src/User/src/Repository/UserDetailRepository.php +++ b/src/User/src/Repository/UserDetailRepository.php @@ -9,6 +9,8 @@ /** * @Entity(name="Api\User\Entity\UserDetail") + * @extends EntityRepository */ class UserDetailRepository extends EntityRepository -{} +{ +} diff --git a/src/User/src/Repository/UserRepository.php b/src/User/src/Repository/UserRepository.php index b496cc4..d209f47 100644 --- a/src/User/src/Repository/UserRepository.php +++ b/src/User/src/Repository/UserRepository.php @@ -11,16 +11,19 @@ use Api\User\Collection\UserCollection; use Api\User\Entity\User; use Doctrine\ORM\EntityRepository; +use Dot\AnnotatedServices\Annotation\Entity; use Exception; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\UserRepositoryInterface; use Mezzio\Authentication\OAuth2\Entity\UserEntity; -use Dot\AnnotatedServices\Annotation\Entity; use Throwable; +use function password_verify; + /** * @Entity(name="Api\User\Entity\User") + * @extends EntityRepository */ class UserRepository extends EntityRepository implements UserRepositoryInterface { @@ -30,10 +33,6 @@ public function deleteUser(User $user): void $this->getEntityManager()->flush(); } - /** - * @param string $hash - * @return User|null - */ public function findByResetPasswordHash(string $hash): ?User { try { @@ -53,10 +52,6 @@ public function findByResetPasswordHash(string $hash): ?User } } - /** - * @param array $filters - * @return UserCollection - */ public function getUsers(array $filters = []): UserCollection { $page = PaginationHelper::getOffsetAndLimit($filters); @@ -69,11 +64,11 @@ public function getUsers(array $filters = []): UserCollection ->leftJoin('user.avatar', 'avatar') ->leftJoin('user.detail', 'detail') ->leftJoin('user.roles', 'roles') - ->orderBy(($filters['order'] ?? 'user.created'), $filters['dir'] ?? 'desc') + ->orderBy($filters['order'] ?? 'user.created', $filters['dir'] ?? 'desc') ->setFirstResult($page['offset']) ->setMaxResults($page['limit']); - if (!empty($filters['status'])) { + if (! empty($filters['status'])) { $qb->andWhere('user.status = :status')->setParameter('status', $filters['status']); } @@ -88,8 +83,7 @@ public function getUsers(array $filters = []): UserCollection } } - if (!empty($filters['search'])) { - /** @psalm-suppress TooManyArguments */ + if (! empty($filters['search'])) { $qb->andWhere( $qb->expr()->orX( $qb->expr()->like('user.identity', ':search'), @@ -100,7 +94,7 @@ public function getUsers(array $filters = []): UserCollection )->setParameter('search', '%' . $filters['search'] . '%'); } - if (!empty($filters['role'])) { + if (! empty($filters['role'])) { $qb->andWhere('roles.name = :role')->setParameter('role', $filters['role']); } @@ -114,7 +108,7 @@ public function getUsers(array $filters = []): UserCollection */ public function saveUser(User $user): User { - if (!$user->hasRoles()) { + if (! $user->hasRoles()) { throw new Exception(Message::RESTRICTION_ROLES); } @@ -125,6 +119,9 @@ public function saveUser(User $user): User } /** + * @param string $username + * @param string $password + * @param string $grantType * @throws OAuthServerException */ public function getUserEntityByUserCredentials( @@ -135,13 +132,13 @@ public function getUserEntityByUserCredentials( ): ?UserEntity { $qb = $this->getEntityManager()->createQueryBuilder(); switch ($clientEntity->getName()) { - case OAuthClient::NAME_ADMIN : + case OAuthClient::NAME_ADMIN: $qb->select('a.password') ->from(Admin::class, 'a') ->andWhere('a.identity = :identity') ->setParameter('identity', $username); break; - case OAuthClient::NAME_FRONTEND : + case OAuthClient::NAME_FRONTEND: $qb->select(['u.password', 'u.status']) ->from(User::class, 'u') ->andWhere('u.identity = :identity') @@ -159,11 +156,11 @@ public function getUserEntityByUserCredentials( $result = $result[0]; - if (!password_verify($password, $result['password'])) { + if (! password_verify($password, $result['password'])) { return null; } - if ($clientEntity->getName() == 'frontend' && $result['status'] !== User::STATUS_ACTIVE) { + if ($clientEntity->getName() === 'frontend' && $result['status'] !== User::STATUS_ACTIVE) { throw new OAuthServerException(Message::USER_NOT_ACTIVATED, 6, 'inactive_user', 401); } diff --git a/src/User/src/Repository/UserRoleRepository.php b/src/User/src/Repository/UserRoleRepository.php index 2807d60..fc1b4b7 100644 --- a/src/User/src/Repository/UserRoleRepository.php +++ b/src/User/src/Repository/UserRoleRepository.php @@ -12,6 +12,7 @@ /** * @Entity(name="Api\User\Entity\UserRole") + * @extends EntityRepository */ class UserRoleRepository extends EntityRepository { @@ -24,7 +25,7 @@ public function getRoles(array $params = []): UserRoleCollection ->createQueryBuilder() ->select(['role']) ->from(UserRole::class, 'role') - ->orderBy(($params['order'] ?? 'role.created'), $params['dir'] ?? 'desc') + ->orderBy($params['order'] ?? 'role.created', $params['dir'] ?? 'desc') ->setFirstResult($page['offset']) ->setMaxResults($page['limit']) ->getQuery() diff --git a/src/User/src/RoutesDelegator.php b/src/User/src/RoutesDelegator.php index 2a482be..0ef4c41 100644 --- a/src/User/src/RoutesDelegator.php +++ b/src/User/src/RoutesDelegator.php @@ -29,50 +29,61 @@ public function __invoke(ContainerInterface $container, string $serviceName, cal * Admins manage user accounts */ - $app->post('/user', + $app->post( + '/user', UserHandler::class, 'user.create' ); - $app->delete('/user/' . $uuid, + $app->delete( + '/user/' . $uuid, UserHandler::class, 'user.delete' ); - $app->get('/user', + $app->get( + '/user', UserHandler::class, 'user.list' ); - $app->patch('/user/' . $uuid, + $app->patch( + '/user/' . $uuid, UserHandler::class, 'user.update' ); - $app->get('/user/' . $uuid, + $app->get( + '/user/' . $uuid, UserHandler::class, 'user.view' ); - $app->post('/user/' . $uuid . '/activate', + $app->post( + '/user/' . $uuid . '/activate', UserActivateHandler::class, 'user.activate' ); - $app->delete('/user/' . $uuid . '/avatar', + $app->delete( + '/user/' . $uuid . '/avatar', UserAvatarHandler::class, 'user.avatar.delete' ); - $app->get('/user/' . $uuid . '/avatar', + $app->get( + '/user/' . $uuid . '/avatar', UserAvatarHandler::class, 'user.avatar.view' ); - $app->post('/user/' . $uuid . '/avatar', + $app->post( + '/user/' . $uuid . '/avatar', UserAvatarHandler::class, 'user.avatar.create' ); - $app->get('/user/role', + $app->get( + '/user/role', UserRoleHandler::class, 'user.role.list' ); - $app->get('/user/role/' . $uuid, + $app->get( + '/user/role/' . $uuid, UserRoleHandler::class, 'user.role.view' ); @@ -81,28 +92,34 @@ public function __invoke(ContainerInterface $container, string $serviceName, cal * Users manage their own accounts */ - $app->delete('/user/my-account', + $app->delete( + '/user/my-account', AccountHandler::class, 'user.my-account.delete' ); - $app->get('/user/my-account', + $app->get( + '/user/my-account', AccountHandler::class, 'user.my-account.view' ); - $app->patch('/user/my-account', + $app->patch( + '/user/my-account', AccountHandler::class, 'user.my-account.update' ); - $app->post('/user/my-avatar', + $app->post( + '/user/my-avatar', AccountAvatarHandler::class, 'user.my-avatar.create' ); - $app->delete('/user/my-avatar', + $app->delete( + '/user/my-avatar', AccountAvatarHandler::class, 'user.my-avatar.delete' ); - $app->get('/user/my-avatar', + $app->get( + '/user/my-avatar', AccountAvatarHandler::class, 'user.my-avatar.view' ); @@ -111,34 +128,41 @@ public function __invoke(ContainerInterface $container, string $serviceName, cal * Guests manage their accounts */ - $app->post('/account/register', + $app->post( + '/account/register', AccountHandler::class, 'account.register' ); - $app->get('/account/reset-password/{hash}', + $app->get( + '/account/reset-password/{hash}', AccountResetPasswordHandler::class, 'account.reset-password.validate' ); - $app->patch('/account/reset-password/{hash}', + $app->patch( + '/account/reset-password/{hash}', AccountResetPasswordHandler::class, 'account.modify-password' ); - $app->post('/account/reset-password', + $app->post( + '/account/reset-password', AccountResetPasswordHandler::class, 'account.reset-password.request' ); - $app->post('/account/recover-identity', + $app->post( + '/account/recover-identity', AccountRecoveryHandler::class, 'account.recover-identity' ); - $app->patch('/account/activate/{hash}', + $app->patch( + '/account/activate/{hash}', AccountActivateHandler::class, 'account.activate' ); - $app->post('/account/activate', + $app->post( + '/account/activate', AccountActivateHandler::class, 'account.activate.request' ); diff --git a/src/User/src/Service/UserAvatarService.php b/src/User/src/Service/UserAvatarService.php index dcb557f..ba20a24 100644 --- a/src/User/src/Service/UserAvatarService.php +++ b/src/User/src/Service/UserAvatarService.php @@ -12,12 +12,19 @@ use Laminas\Diactoros\UploadedFile; use Psr\Http\Message\UploadedFileInterface; +use function file_exists; +use function is_readable; +use function mkdir; +use function rtrim; +use function sprintf; +use function unlink; + class UserAvatarService implements UserAvatarServiceInterface { public const EXTENSIONS = [ - 'image/jpg' => 'jpg', + 'image/jpg' => 'jpg', 'image/jpeg' => 'jpg', - 'image/png' => 'png' + 'image/png' => 'png', ]; /** @@ -29,7 +36,8 @@ class UserAvatarService implements UserAvatarServiceInterface public function __construct( protected UserAvatarRepository $userAvatarRepository, protected array $config - ) {} + ) { + } public function createAvatar(User $user, UploadedFile $uploadedFile): UserAvatar { @@ -53,7 +61,7 @@ public function createAvatar(User $user, UploadedFile $uploadedFile): UserAvatar public function removeAvatar(User $user): void { - if (!$user->hasAvatar()) { + if (! $user->hasAvatar()) { return; } @@ -80,7 +88,7 @@ protected function deleteAvatarFile(string $path): void protected function ensureDirectoryExists(string $path): void { - if (!file_exists($path)) { + if (! file_exists($path)) { mkdir($path, 0755); } } diff --git a/src/User/src/Service/UserRoleService.php b/src/User/src/Service/UserRoleService.php index 10795a4..d4c01c3 100644 --- a/src/User/src/Service/UserRoleService.php +++ b/src/User/src/Service/UserRoleService.php @@ -18,7 +18,8 @@ class UserRoleService implements UserRoleServiceInterface */ public function __construct( protected UserRoleRepository $roleRepository - ) {} + ) { + } public function findOneBy(array $params = []): ?UserRole { diff --git a/src/User/src/Service/UserService.php b/src/User/src/Service/UserService.php index 3ece7ab..930d4f8 100644 --- a/src/User/src/Service/UserService.php +++ b/src/User/src/Service/UserService.php @@ -4,21 +4,22 @@ namespace Api\User\Service; -use Api\App\Entity\OAuthAccessToken; use Api\App\Message; use Api\App\Repository\OAuthAccessTokenRepository; use Api\App\Repository\OAuthRefreshTokenRepository; use Api\User\Collection\UserCollection; -use Api\User\Entity\UserDetail; use Api\User\Entity\User; +use Api\User\Entity\UserDetail; use Api\User\Entity\UserRole; use Api\User\Repository\UserDetailRepository; use Api\User\Repository\UserRepository; +use Dot\AnnotatedServices\Annotation\Inject; use Dot\Mail\Exception\MailException; use Dot\Mail\Service\MailService; -use Mezzio\Template\TemplateRendererInterface; -use Dot\AnnotatedServices\Annotation\Inject; use Exception; +use Mezzio\Template\TemplateRendererInterface; + +use function date; class UserService implements UserServiceInterface { @@ -38,12 +39,13 @@ public function __construct( protected UserRoleServiceInterface $userRoleService, protected MailService $mailService, protected TemplateRendererInterface $templateRenderer, - protected OAuthAccessTokenRepository $OAuthAccessTokenRepository, - protected OAuthRefreshTokenRepository $OAuthRefreshTokenRepository, + protected OAuthAccessTokenRepository $oAuthAccessTokenRepository, + protected OAuthRefreshTokenRepository $oAuthRefreshTokenRepository, protected UserRepository $userRepository, protected UserDetailRepository $userDetailRepository, protected array $config = [] - ) {} + ) { + } /** * @throws Exception @@ -78,7 +80,7 @@ public function createUser(array $data = []): User ->setStatus($data['status'] ?? User::STATUS_PENDING); $detail->setUser($user); - if (!empty($data['roles'])) { + if (! empty($data['roles'])) { foreach ($data['roles'] as $roleData) { $role = $this->userRoleService->findOneBy(['uuid' => $roleData['uuid']]); if ($role instanceof UserRole) { @@ -97,11 +99,10 @@ public function createUser(array $data = []): User public function revokeTokens(User $user): void { - /** @var OAuthAccessToken[] $accessTokens */ - $accessTokens = $this->OAuthAccessTokenRepository->findAccessTokens($user->getIdentity()); + $accessTokens = $this->oAuthAccessTokenRepository->findAccessTokens($user->getIdentity()); foreach ($accessTokens as $accessToken) { - $this->OAuthAccessTokenRepository->revokeAccessToken($accessToken->getToken()); - $this->OAuthRefreshTokenRepository->revokeRefreshToken($accessToken->getToken()); + $this->oAuthAccessTokenRepository->revokeAccessToken($accessToken->getToken()); + $this->oAuthRefreshTokenRepository->revokeRefreshToken($accessToken->getToken()); } } @@ -127,8 +128,7 @@ public function anonymizeUser(User $user): User ->getDetail() ->setFirstName($placeholder) ->setLastName($placeholder) - ->setEmail($placeholder) - ; + ->setEmail($placeholder); return $this->userRepository->saveUser($user); } @@ -141,7 +141,7 @@ public function exists(string $identity = ''): bool public function existsOther(string $identity = '', string $uuid = ''): bool { $user = $this->findOneBy(['identity' => $identity]); - if (!$user instanceof User) { + if (! $user instanceof User) { return false; } @@ -156,7 +156,7 @@ public function emailExists(string $email = ''): bool public function emailExistsOther(string $email = '', string $uuid = ''): bool { $user = $this->findByEmail($email); - if (!$user instanceof User) { + if (! $user instanceof User) { return false; } @@ -202,7 +202,7 @@ public function sendActivationMail(User $user): bool $this->mailService->setBody( $this->templateRenderer->render('user::activate', [ 'config' => $this->config, - 'user' => $user + 'user' => $user, ]) ); @@ -221,7 +221,7 @@ public function sendResetPasswordRequestedMail(User $user): bool $this->mailService->setBody( $this->templateRenderer->render('user::reset-password-requested', [ 'config' => $this->config, - 'user' => $user + 'user' => $user, ]) ); @@ -229,8 +229,6 @@ public function sendResetPasswordRequestedMail(User $user): bool } /** - * @param User $user - * @return bool * @throws MailException */ public function sendResetPasswordCompletedMail(User $user): bool @@ -242,7 +240,7 @@ public function sendResetPasswordCompletedMail(User $user): bool $this->mailService->setBody( $this->templateRenderer->render('user::reset-password-completed', [ 'config' => $this->config, - 'user' => $user + 'user' => $user, ]) ); @@ -250,8 +248,6 @@ public function sendResetPasswordCompletedMail(User $user): bool } /** - * @param User $user - * @return bool * @throws MailException */ public function sendWelcomeMail(User $user): bool @@ -261,7 +257,7 @@ public function sendWelcomeMail(User $user): bool $this->mailService->setBody( $this->templateRenderer->render('user::welcome', [ 'config' => $this->config, - 'user' => $user + 'user' => $user, ]) ); @@ -310,12 +306,12 @@ public function updateUser(User $user, array $data = []): User } if (isset($data['detail']['email'])) { - if (!$this->emailExists($data['detail']['email'])) { + if (! $this->emailExists($data['detail']['email'])) { $user->getDetail()->setEmail($data['detail']['email']); } } - if (!empty($data['roles'])) { + if (! empty($data['roles'])) { $user->resetRoles(); foreach ($data['roles'] as $roleData) { $role = $this->userRoleService->findOneBy(['uuid' => $roleData['uuid']]); @@ -340,7 +336,7 @@ public function sendRecoverIdentityMail(User $user): bool $this->mailService->setBody( $this->templateRenderer->render('user::recover-identity-requested', [ 'config' => $this->config, - 'user' => $user + 'user' => $user, ]) ); diff --git a/src/User/src/Service/UserServiceInterface.php b/src/User/src/Service/UserServiceInterface.php index e301905..5e4e3ad 100644 --- a/src/User/src/Service/UserServiceInterface.php +++ b/src/User/src/Service/UserServiceInterface.php @@ -62,15 +62,11 @@ public function sendActivationMail(User $user): bool; public function sendResetPasswordRequestedMail(User $user): bool; /** - * @param User $user - * @return bool * @throws MailException */ public function sendResetPasswordCompletedMail(User $user): bool; /** - * @param User $user - * @return bool * @throws MailException */ public function sendWelcomeMail(User $user): bool; diff --git a/tests/AppTest/Functional/src/AbstractFunctionalTest.php b/test/Functional/AbstractFunctionalTest.php similarity index 75% rename from tests/AppTest/Functional/src/AbstractFunctionalTest.php rename to test/Functional/AbstractFunctionalTest.php index 3ccdda4..8d40509 100644 --- a/tests/AppTest/Functional/src/AbstractFunctionalTest.php +++ b/test/Functional/AbstractFunctionalTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace AppTest\Functional; +namespace ApiTest\Functional; use Api\Admin\Entity\Admin; use Api\Admin\Entity\AdminRole; @@ -10,8 +10,8 @@ use Api\User\Entity\User; use Api\User\Entity\UserDetail; use Api\User\Entity\UserRole; -use AppTest\Functional\Traits\AuthenticationTrait; -use AppTest\Functional\Traits\DatabaseTrait; +use ApiTest\Functional\Traits\AuthenticationTrait; +use ApiTest\Functional\Traits\DatabaseTrait; use Doctrine\ORM\EntityManagerInterface; use Fig\Http\Message\RequestMethodInterface; use Fig\Http\Message\StatusCodeInterface; @@ -26,9 +26,16 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -abstract class AbstractFunctionalTest extends TestCase +use function array_merge; +use function getenv; +use function method_exists; +use function putenv; +use function realpath; + +class AbstractFunctionalTest extends TestCase { - use DatabaseTrait, AuthenticationTrait; + use AuthenticationTrait; + use DatabaseTrait; protected Application $app; protected ContainerInterface|ServiceManager $container; @@ -40,8 +47,6 @@ abstract class AbstractFunctionalTest extends TestCase */ public function setUp(): void { - parent::setUp(); - $this->enableTestMode(); $this->initContainer(); @@ -81,7 +86,7 @@ public static function isTestMode(): bool private function initContainer(): void { - $this->container = require realpath(__DIR__ . '/../../../../config/container.php'); + $this->container = require realpath(__DIR__ . '/../../config/container.php'); } /** @@ -100,7 +105,7 @@ private function initApp(): void private function initPipeline(): void { $factory = $this->container->get(MiddlewareFactory::class); - (require realpath(__DIR__ . '/../../../../config/pipeline.php'))($this->app, $factory, $this->container); + (require realpath(__DIR__ . '/../../config/pipeline.php'))($this->app, $factory, $this->container); } /** @@ -110,7 +115,7 @@ private function initPipeline(): void private function initRoutes(): void { $factory = $this->container->get(MiddlewareFactory::class); - (require realpath(__DIR__ . '/../../../../config/routes.php'))($this->app, $factory, $this->container); + (require realpath(__DIR__ . '/../../config/routes.php'))($this->app, $factory, $this->container); } /** @@ -127,15 +132,13 @@ protected function getContainer(): ContainerInterface|ServiceManager return $this->container; } - protected function get - ( + protected function get( string $uri, array $queryParams = [], array $uploadedFiles = [], array $headers = [], array $cookies = [] - ): ResponseInterface - { + ): ResponseInterface { $request = $this->createRequest( $uri, RequestMethodInterface::METHOD_GET, @@ -149,16 +152,14 @@ protected function get return $this->getResponse($request); } - protected function post - ( + protected function post( string $uri, array $parsedBody = [], array $queryParams = [], array $uploadedFiles = [], array $headers = [], array $cookies = [] - ): ResponseInterface - { + ): ResponseInterface { $request = $this->createRequest( $uri, RequestMethodInterface::METHOD_POST, @@ -172,16 +173,14 @@ protected function post return $this->getResponse($request); } - protected function patch - ( + protected function patch( string $uri, array $parsedBody = [], array $queryParams = [], array $uploadedFiles = [], array $headers = [], array $cookies = [] - ): ResponseInterface - { + ): ResponseInterface { $request = $this->createRequest( $uri, RequestMethodInterface::METHOD_PATCH, @@ -195,16 +194,14 @@ protected function patch return $this->getResponse($request); } - protected function put - ( + protected function put( string $uri, array $parsedBody = [], array $queryParams = [], array $uploadedFiles = [], array $headers = [], array $cookies = [] - ): ResponseInterface - { + ): ResponseInterface { $request = $this->createRequest( $uri, RequestMethodInterface::METHOD_PUT, @@ -218,14 +215,12 @@ protected function put return $this->getResponse($request); } - protected function delete - ( + protected function delete( string $uri, array $queryParams = [], array $headers = [], array $cookies = [] - ): ResponseInterface - { + ): ResponseInterface { $request = $this->createRequest( $uri, RequestMethodInterface::METHOD_DELETE, @@ -239,21 +234,7 @@ protected function delete return $this->getResponse($request); } - /** - * @param string $uri - * @param string $method - * @param array $parsedBody - * @param array $queryParams - * @param array $uploadedFiles - * @param array $headers - * @param array $cookies - * @param array $serverParams - * @param string $body - * @param string $protocol - * @return ServerRequestInterface - */ - private function createRequest - ( + private function createRequest( string $uri, string $method, array $parsedBody = [], @@ -264,8 +245,7 @@ private function createRequest array $serverParams = [], string $body = 'php://input', string $protocol = '1.1' - ): ServerRequestInterface - { + ): ServerRequestInterface { if (method_exists($this, 'isAuthenticated') && $this->isAuthenticated()) { $headers = array_merge($headers, $this->getAuthorizationHeader()); } @@ -284,13 +264,6 @@ private function createRequest ); } - /** - * - * Process response and set cursor at position(0) - * - * @param ServerRequestInterface $request - * @return ResponseInterface - */ private function getResponse(ServerRequestInterface $request): ResponseInterface { $response = $this->app->handle($request); @@ -304,7 +277,7 @@ protected function assertResponseOk(ResponseInterface $response): void $this->assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode()); } - protected function assertResponseSuccessful(ResponseInterface $response) + protected function assertResponseSuccessful(ResponseInterface $response): void { $this->assertBetween( $response->getStatusCode(), @@ -328,12 +301,12 @@ protected function assertResponseBadRequest(ResponseInterface $response): void $this->assertSame(StatusCodeInterface::STATUS_BAD_REQUEST, $response->getStatusCode()); } - protected function assertResponseNotFound(ResponseInterface $response) + protected function assertResponseNotFound(ResponseInterface $response): void { $this->assertSame(StatusCodeInterface::STATUS_NOT_FOUND, $response->getStatusCode()); } - protected function assertBetween($value, $from, $to) + protected function assertBetween(int $value, int $from, int $to): void { $this->assertThat( $value, @@ -344,13 +317,7 @@ protected function assertBetween($value, $from, $to) ); } - /** - * Replaces an actual service with a mock instance - * - * @param $service - * @param $mockInstance - */ - protected function replaceService($service, $mockInstance) + protected function replaceService(string $service, object $mockInstance): void { $this->getContainer()->setAllowOverride(true); $this->getContainer()->setService($service, $mockInstance); @@ -360,29 +327,29 @@ protected function replaceService($service, $mockInstance) protected function getValidUserData(array $data = []): array { return [ - 'detail' => [ + 'detail' => [ 'firstName' => $data['detail']['firstName'] ?? 'First', - 'lastName' => $data['detail']['lastName'] ?? 'Last', - 'email' => $data['detail']['email'] ?? 'user@dotkernel.com', + 'lastName' => $data['detail']['lastName'] ?? 'Last', + 'email' => $data['detail']['email'] ?? 'user@dotkernel.com', ], - 'identity' => $data['identity'] ?? 'user@dotkernel.com', - 'password' => $data['password'] ?? self::DEFAULT_PASSWORD, + 'identity' => $data['identity'] ?? 'user@dotkernel.com', + 'password' => $data['password'] ?? self::DEFAULT_PASSWORD, 'passwordConfirm' => $data['password'] ?? self::DEFAULT_PASSWORD, - 'status' => $data['status'] ?? User::STATUS_ACTIVE, + 'status' => $data['status'] ?? User::STATUS_ACTIVE, ]; } protected function getInvalidUserData(): array { return [ - 'detail' => [ + 'detail' => [ 'firstName' => 'invalid', - 'lastName' => 'invalid', - 'email' => 'invalid@dotkernel.com', + 'lastName' => 'invalid', + 'email' => 'invalid@dotkernel.com', ], 'identity' => 'invalid', 'password' => 'invalid', - 'status' => Admin::STATUS_INACTIVE, + 'status' => Admin::STATUS_INACTIVE, ]; } @@ -390,10 +357,10 @@ protected function getValidAdminData(): array { return [ 'firstName' => 'First', - 'identity' => 'admin@dotkernel.com', - 'lastName' => 'Last', - 'password' => self::DEFAULT_PASSWORD, - 'status' => Admin::STATUS_ACTIVE, + 'identity' => 'admin@dotkernel.com', + 'lastName' => 'Last', + 'password' => self::DEFAULT_PASSWORD, + 'status' => Admin::STATUS_ACTIVE, ]; } @@ -401,10 +368,10 @@ protected function getInvalidAdminData(): array { return [ 'firstName' => 'invalid', - 'identity' => 'invalid', - 'lastName' => 'invalid', - 'password' => 'invalid', - 'status' => Admin::STATUS_INACTIVE, + 'identity' => 'invalid', + 'lastName' => 'invalid', + 'password' => 'invalid', + 'status' => Admin::STATUS_INACTIVE, ]; } @@ -412,34 +379,34 @@ protected function getValidFrontendAccessTokenCredentials(array $data = []): arr { $userData = $this->getValidUserData(); return [ - 'client_id' => 'frontend', + 'client_id' => 'frontend', 'client_secret' => 'frontend', - 'grant_type' => 'password', - 'password' => $data['password'] ?? $userData['password'], - 'scope' => 'api', - 'username' => $data['username'] ?? $userData['identity'], + 'grant_type' => 'password', + 'password' => $data['password'] ?? $userData['password'], + 'scope' => 'api', + 'username' => $data['username'] ?? $userData['identity'], ]; } protected function getInvalidFrontendAccessTokenCredentials(): array { return [ - 'client_id' => 'frontend', + 'client_id' => 'frontend', 'client_secret' => 'frontend', - 'grant_type' => 'password', - 'password' => 'invalid', - 'scope' => 'api', - 'username' => 'invalid@dotkernel.com', + 'grant_type' => 'password', + 'password' => 'invalid', + 'scope' => 'api', + 'username' => 'invalid@dotkernel.com', ]; } protected function getValidFrontendRefreshTokenCredentials(): array { return [ - 'grant_type' => 'refresh_token', - 'client_id' => 'frontend', + 'grant_type' => 'refresh_token', + 'client_id' => 'frontend', 'client_secret' => 'frontend', - 'scope' => 'api', + 'scope' => 'api', 'refresh_token' => $this->getRefreshToken(), ]; } @@ -447,10 +414,10 @@ protected function getValidFrontendRefreshTokenCredentials(): array protected function getInvalidFrontendRefreshTokenCredentials(): array { return [ - 'grant_type' => 'refresh_token', - 'client_id' => 'frontend', + 'grant_type' => 'refresh_token', + 'client_id' => 'frontend', 'client_secret' => 'frontend', - 'scope' => 'api', + 'scope' => 'api', 'refresh_token' => 'invalid', ]; } @@ -459,24 +426,24 @@ protected function getValidAdminAccessTokenCredentials(array $data = []): array { $adminData = $this->getValidAdminData(); return [ - 'client_id' => 'admin', + 'client_id' => 'admin', 'client_secret' => 'admin', - 'grant_type' => 'password', - 'password' => $data['password'] ?? $adminData['password'], - 'scope' => 'api', - 'username' => $data['username'] ?? $adminData['identity'], + 'grant_type' => 'password', + 'password' => $data['password'] ?? $adminData['password'], + 'scope' => 'api', + 'username' => $data['username'] ?? $adminData['identity'], ]; } protected function getInvalidAdminAccessTokenCredentials(): array { return [ - 'client_id' => 'admin', + 'client_id' => 'admin', 'client_secret' => 'admin', - 'grant_type' => 'password', - 'password' => 'invalid', - 'scope' => 'api', - 'username' => 'invalid@dotkernel.com', + 'grant_type' => 'password', + 'password' => 'invalid', + 'scope' => 'api', + 'username' => 'invalid@dotkernel.com', ]; } @@ -486,8 +453,9 @@ protected function getInvalidAdminAccessTokenCredentials(): array */ protected function createAdmin(): Admin { - /** @var RoleInterface $adminRole */ $adminRoleRepository = $this->getEntityManager()->getRepository(AdminRole::class); + + /** @var RoleInterface $adminRole */ $adminRole = $adminRoleRepository->findOneBy(['name' => AdminRole::ROLE_ADMIN]); $data = $this->getValidAdminData(); @@ -512,13 +480,14 @@ protected function createAdmin(): Admin */ protected function createUser(array $data = []): User { - /** @var RoleInterface $userRole */ $userRoleRepository = $this->getEntityManager()->getRepository(UserRole::class); + + /** @var RoleInterface $userRole */ $userRole = $userRoleRepository->findOneBy(['name' => UserRole::ROLE_USER]); $userData = $this->getValidUserData(); - $user = new User(); + $user = new User(); $userDetail = (new UserDetail()) ->setUser($user) ->setFirstName($data['detail']['firstName'] ?? $userData['detail']['firstName']) diff --git a/tests/AppTest/Functional/src/AdminTest.php b/test/Functional/AdminTest.php similarity index 77% rename from tests/AppTest/Functional/src/AdminTest.php rename to test/Functional/AdminTest.php index 324c4f7..cd5de95 100644 --- a/tests/AppTest/Functional/src/AdminTest.php +++ b/test/Functional/AdminTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace AppTest\Functional; +namespace ApiTest\Functional; use Api\Admin\Entity\Admin; use Api\Admin\Entity\AdminRole; @@ -11,16 +11,20 @@ use Api\User\Entity\UserDetail; use Api\User\Entity\UserRole; use Dot\Mail\Service\MailService; +use PHPUnit\Framework\MockObject\Exception; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; +use function json_decode; +use function sprintf; + class AdminTest extends AbstractFunctionalTest { /** * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testUserCannotListAdminAccounts() + public function testUserCannotListAdminAccounts(): void { $user = $this->createUser(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); @@ -34,9 +38,9 @@ public function testUserCannotListAdminAccounts() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testUserCannotViewAdminAccount() + public function testUserCannotViewAdminAccount(): void { - $user = $this->createUser(); + $user = $this->createUser(); $admin = $this->createAdmin(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); @@ -50,7 +54,7 @@ public function testUserCannotViewAdminAccount() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testUserCannotCreateAdminAccount() + public function testUserCannotCreateAdminAccount(): void { $user = $this->createUser(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); @@ -64,9 +68,9 @@ public function testUserCannotCreateAdminAccount() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testUserCannotUpdateAdminAccount() + public function testUserCannotUpdateAdminAccount(): void { - $user = $this->createUser(); + $user = $this->createUser(); $admin = $this->createAdmin(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); @@ -79,9 +83,9 @@ public function testUserCannotUpdateAdminAccount() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testUserCannotDeleteAdminAccount() + public function testUserCannotDeleteAdminAccount(): void { - $user = $this->createUser(); + $user = $this->createUser(); $admin = $this->createAdmin(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); @@ -94,7 +98,7 @@ public function testUserCannotDeleteAdminAccount() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAdminCanListAdminAccounts() + public function testAdminCanListAdminAccounts(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); @@ -108,13 +112,13 @@ public function testAdminCanListAdminAccounts() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAdminCanViewAdminAccount() + public function testAdminCanViewAdminAccount(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); $response = $this->get('/admin/' . $admin->getUuid()->toString()); - $data = json_decode($response->getBody()->getContents(), true); + $data = json_decode($response->getBody()->getContents(), true); $this->assertResponseOk($response); $this->assertSame($admin->getUuid()->toString(), $data['uuid']); @@ -124,7 +128,7 @@ public function testAdminCanViewAdminAccount() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testCannotCreateDuplicateAdminAccount() + public function testCannotCreateDuplicateAdminAccount(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); @@ -135,20 +139,20 @@ public function testCannotCreateDuplicateAdminAccount() $adminRole = $adminRoleRepository->findOneBy(['name' => AdminRole::ROLE_ADMIN]); $requestBody = [ - 'identity' => $admin->getIdentity(), - 'password' => self::DEFAULT_PASSWORD, + 'identity' => $admin->getIdentity(), + 'password' => self::DEFAULT_PASSWORD, 'passwordConfirm' => self::DEFAULT_PASSWORD, - 'firstName' => $admin->getFirstName(), - 'lastName' => $admin->getLastName(), - 'roles' => [ + 'firstName' => $admin->getFirstName(), + 'lastName' => $admin->getLastName(), + 'roles' => [ [ - 'uuid' => $adminRole->getUuid()->toString() - ] - ] + 'uuid' => $adminRole->getUuid()->toString(), + ], + ], ]; $response = $this->post('/admin', $requestBody); - $data = json_decode($response->getBody()->getContents(), true); + $data = json_decode($response->getBody()->getContents(), true); $this->assertResponseBadRequest($response); $this->assertArrayHasKey('error', $data); @@ -160,27 +164,27 @@ public function testCannotCreateDuplicateAdminAccount() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testAdminCanCreateAdminAccount() + public function testAdminCanCreateAdminAccount(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); $adminRoleRepository = $this->getEntityManager()->getRepository(AdminRole::class); - $adminRepository = $this->getEntityManager()->getRepository(Admin::class); + $adminRepository = $this->getEntityManager()->getRepository(Admin::class); $adminRole = $adminRoleRepository->findOneBy(['name' => AdminRole::ROLE_ADMIN]); $requestBody = [ - 'identity' => 'newadmin@test.com', - 'password' => self::DEFAULT_PASSWORD, + 'identity' => 'newadmin@test.com', + 'password' => self::DEFAULT_PASSWORD, 'passwordConfirm' => self::DEFAULT_PASSWORD, - 'firstName' => 'Admin', - 'lastName' => 'Test', - 'roles' => [ + 'firstName' => 'Admin', + 'lastName' => 'Test', + 'roles' => [ [ - 'uuid' => $adminRole->getUuid()->toString() - ] - ] + 'uuid' => $adminRole->getUuid()->toString(), + ], + ], ]; $response = $this->post('/admin', $requestBody); @@ -201,18 +205,18 @@ public function testAdminCanCreateAdminAccount() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAdminCanUpdateAdminAccount() + public function testAdminCanUpdateAdminAccount(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); $updateData = [ 'firstName' => 'Test', - 'lastName' => 'Admin', + 'lastName' => 'Admin', ]; $response = $this->patch('/admin/' . $admin->getUuid()->toString(), $updateData); - $data = json_decode($response->getBody()->getContents(), true); + $data = json_decode($response->getBody()->getContents(), true); $this->assertResponseOk($response); $this->assertSame($updateData['firstName'], $data['firstName']); @@ -223,7 +227,7 @@ public function testAdminCanUpdateAdminAccount() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testAdminCanDeleteAdminAccount() + public function testAdminCanDeleteAdminAccount(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); @@ -233,7 +237,7 @@ public function testAdminCanDeleteAdminAccount() $this->assertResponseOk($response); $adminRepository = $this->getEntityManager()->getRepository(Admin::class); - $admin = $adminRepository->find($admin->getUuid()->toString()); + $admin = $adminRepository->find($admin->getUuid()->toString()); $this->assertEmpty($admin); } @@ -242,13 +246,13 @@ public function testAdminCanDeleteAdminAccount() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAdminCanViewPersonalAccount() + public function testAdminCanViewPersonalAccount(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); $response = $this->get('/admin/my-account'); - $data = json_decode($response->getBody()->getContents(), true); + $data = json_decode($response->getBody()->getContents(), true); $this->assertResponseOk($response); $this->assertSame($admin->getUuid()->toString(), $data['uuid']); @@ -261,18 +265,18 @@ public function testAdminCanViewPersonalAccount() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAdminCanUpdatePersonalAccount() + public function testAdminCanUpdatePersonalAccount(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); $updateData = [ 'firstName' => 'test', - 'lastName' => 'admin', + 'lastName' => 'admin', ]; $response = $this->patch('/admin/my-account', $updateData); - $data = json_decode($response->getBody()->getContents(), true); + $data = json_decode($response->getBody()->getContents(), true); $this->assertResponseOk($response); $this->assertSame($updateData['firstName'], $data['firstName']); @@ -283,7 +287,7 @@ public function testAdminCanUpdatePersonalAccount() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAdminCanListAdminRoles() + public function testAdminCanListAdminRoles(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); @@ -297,7 +301,7 @@ public function testAdminCanListAdminRoles() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testAdminCanViewAdminRole() + public function testAdminCanViewAdminRole(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); @@ -308,7 +312,7 @@ public function testAdminCanViewAdminRole() $this->getEntityManager()->flush(); $response = $this->get('/admin/role/' . $adminRole->getUuid()->toString()); - $data = json_decode($response->getBody()->getContents(), true); + $data = json_decode($response->getBody()->getContents(), true); $this->assertResponseOk($response); $this->assertSame($adminRole->getUuid()->toString(), $data['uuid']); @@ -319,7 +323,7 @@ public function testAdminCanViewAdminRole() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testAdminCreateUserAccountDuplicateEmail() + public function testAdminCreateUserAccountDuplicateEmail(): void { $admin = $this->createAdmin(); $this->createUser(['detail' => ['email' => 'user1@test.com']]); @@ -327,14 +331,14 @@ public function testAdminCreateUserAccountDuplicateEmail() $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); $userData = [ - 'identity' => 'test@user.com', - 'password' => self::DEFAULT_PASSWORD, + 'identity' => 'test@user.com', + 'password' => self::DEFAULT_PASSWORD, 'passwordConfirm' => self::DEFAULT_PASSWORD, - 'status' => 'pending', - 'detail' => [ + 'status' => 'pending', + 'detail' => [ 'firstName' => 'User', - 'lastName' => 'Test', - 'email' => 'user1@test.com', + 'lastName' => 'Test', + 'email' => 'user1@test.com', ], ]; @@ -350,30 +354,33 @@ public function testAdminCreateUserAccountDuplicateEmail() /** * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface + * @throws Exception */ - public function testAdminCanCreateUserAccount() + public function testAdminCanCreateUserAccount(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); + $userRoleRepository = $this->getEntityManager()->getRepository(UserRole::class); - $userRole = $userRoleRepository->findOneBy(['name' => UserRole::ROLE_USER]); + $userRole = $userRoleRepository->findOneBy(['name' => UserRole::ROLE_USER]); + $mailService = $this->createMock(MailService::class); - $this->replaceService(MailService::class , $mailService); + $this->replaceService(MailService::class, $mailService); $userData = [ - 'identity' => 'test@user.com', - 'password' => self::DEFAULT_PASSWORD, + 'identity' => 'test@user.com', + 'password' => self::DEFAULT_PASSWORD, 'passwordConfirm' => self::DEFAULT_PASSWORD, - 'status' => 'pending', - 'detail' => [ + 'status' => 'pending', + 'detail' => [ 'firstName' => 'User', - 'lastName' => 'Test', - 'email' => 'test@user.com', + 'lastName' => 'Test', + 'email' => 'test@user.com', ], ]; $response = $this->post('/user', $userData); - $data = json_decode($response->getBody()->getContents(), true); + $data = json_decode($response->getBody()->getContents(), true); $this->assertResponseOk($response); $this->assertArrayHasKey('uuid', $data); @@ -406,10 +413,10 @@ public function testAdminCanCreateUserAccount() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testAdminCanActiveUserAccount() + public function testAdminCanActiveUserAccount(): void { $admin = $this->createAdmin(); - $user = $this->createUser([ + $user = $this->createUser([ 'status' => User::STATUS_PENDING, ]); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); @@ -420,7 +427,7 @@ public function testAdminCanActiveUserAccount() $this->assertResponseOk($response); $userRepository = $this->getEntityManager()->getRepository(User::class); - $user = $userRepository->find($user->getUuid()->toString()); + $user = $userRepository->find($user->getUuid()->toString()); $this->assertTrue($user->isActive()); } @@ -429,10 +436,10 @@ public function testAdminCanActiveUserAccount() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testAdminCanDeleteUserAccount() + public function testAdminCanDeleteUserAccount(): void { $admin = $this->createAdmin(); - $user = $this->createUser(); + $user = $this->createUser(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); $response = $this->delete('/user/' . $user->getUuid()->toString()); @@ -440,7 +447,7 @@ public function testAdminCanDeleteUserAccount() $this->assertResponseOk($response); $userRepository = $this->getEntityManager()->getRepository(User::class); - $user = $userRepository->find($user->getUuid()->toString()); + $user = $userRepository->find($user->getUuid()->toString()); $this->assertTrue($user->isDeleted()); } @@ -449,7 +456,7 @@ public function testAdminCanDeleteUserAccount() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAdminCanListUserAccounts() + public function testAdminCanListUserAccounts(): void { $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); @@ -463,7 +470,7 @@ public function testAdminCanListUserAccounts() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testAdminUpdateUserAccountDuplicateEmail() + public function testAdminUpdateUserAccountDuplicateEmail(): void { $admin = $this->createAdmin(); $user1 = $this->createUser(['identity' => 'user1@test.com', 'detail' => ['email' => 'user1@test.com']]); @@ -474,7 +481,7 @@ public function testAdminUpdateUserAccountDuplicateEmail() $response = $this->patch('/user/' . $user2->getUuid()->toString(), [ 'detail' => [ 'email' => $user1->getDetail()->getEmail(), - ] + ], ]); $this->assertResponseBadRequest($response); @@ -485,7 +492,7 @@ public function testAdminUpdateUserAccountDuplicateEmail() $this->assertSame(Message::DUPLICATE_EMAIL, $data['error']['messages'][0]); $userDetailRepository = $this->getEntityManager()->getRepository(UserDetail::class); - $userDetail = $userDetailRepository->find($user2->getDetail()->getUuid()); + $userDetail = $userDetailRepository->find($user2->getDetail()->getUuid()); $this->assertSame($user2->getDetail()->getEmail(), $userDetail->getEmail()); } @@ -493,28 +500,28 @@ public function testAdminUpdateUserAccountDuplicateEmail() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testAdminCanUpdateUserAccount() + public function testAdminCanUpdateUserAccount(): void { $userRole = (new UserRole())->setName('new_role'); $this->getEntityManager()->persist($userRole); $this->getEntityManager()->flush(); - $user = $this->createUser(); + $user = $this->createUser(); $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); $updateData = [ 'detail' => [ 'firstName' => 'Foo', - 'lastName' => 'Bar', - 'email' => 'foobar@dotkernel.com', + 'lastName' => 'Bar', + 'email' => 'foobar@dotkernel.com', ], 'status' => User::STATUS_ACTIVE, - 'roles' => [ + 'roles' => [ [ 'uuid' => $userRole->getUuid()->toString(), - ] - ] + ], + ], ]; $response = $this->patch('/user/' . $user->getUuid()->toString(), $updateData); @@ -533,9 +540,9 @@ public function testAdminCanUpdateUserAccount() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testAdminCanViewUserAccount() + public function testAdminCanViewUserAccount(): void { - $user = $this->createUser(); + $user = $this->createUser(); $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); @@ -551,9 +558,9 @@ public function testAdminCanViewUserAccount() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAdminViewNotFoundUserAccount() + public function testAdminViewNotFoundUserAccount(): void { - $user = new User(); + $user = new User(); $admin = $this->createAdmin(); $this->loginAs($admin->getIdentity(), self::DEFAULT_PASSWORD, 'admin', 'admin'); diff --git a/tests/AppTest/Functional/src/AuthenticationTest.php b/test/Functional/AuthenticationTest.php similarity index 90% rename from tests/AppTest/Functional/src/AuthenticationTest.php rename to test/Functional/AuthenticationTest.php index 2e42b78..f8afc1b 100644 --- a/tests/AppTest/Functional/src/AuthenticationTest.php +++ b/test/Functional/AuthenticationTest.php @@ -2,18 +2,20 @@ declare(strict_types=1); -namespace AppTest\Functional; +namespace ApiTest\Functional; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; +use function json_decode; + class AuthenticationTest extends AbstractFunctionalTest { /** * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAuthenticateInvalidUser() + public function testAuthenticateInvalidUser(): void { $this->authenticateInvalidIdentity($this->getInvalidFrontendAccessTokenCredentials()); } @@ -22,7 +24,7 @@ public function testAuthenticateInvalidUser() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAuthenticateInvalidAdmin() + public function testAuthenticateInvalidAdmin(): void { $this->authenticateInvalidIdentity($this->getInvalidAdminAccessTokenCredentials()); } @@ -31,7 +33,7 @@ public function testAuthenticateInvalidAdmin() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAuthenticateAdmin() + public function testAuthenticateAdmin(): void { $this->createAdmin(); @@ -53,7 +55,7 @@ public function testAuthenticateAdmin() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAuthenticateUser() + public function testAuthenticateUser(): void { $this->createUser(); @@ -72,7 +74,7 @@ public function testAuthenticateUser() $this->assertNotEmpty($data['refresh_token']); } - public function testInvalidRefreshToken() + public function testInvalidRefreshToken(): void { $response = $this->post('/security/refresh-token', $this->getInvalidFrontendRefreshTokenCredentials()); @@ -91,7 +93,7 @@ public function testInvalidRefreshToken() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testRefreshToken() + public function testRefreshToken(): void { $user = $this->createUser(); @@ -115,13 +117,13 @@ public function testRefreshToken() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testAdminCannotAuthenticateAsUser() + public function testAdminCannotAuthenticateAsUser(): void { - $admin = $this->createAdmin(); + $admin = $this->createAdmin(); $errorMessages = $this->getContainer()->get('config')['authentication']['invalid_credentials']; $response = $this->post('/security/generate-token', $this->getValidFrontendAccessTokenCredentials([ - 'username' => $admin->getIdentity() + 'username' => $admin->getIdentity(), ])); $data = json_decode($response->getBody()->getContents(), true); @@ -139,13 +141,13 @@ public function testAdminCannotAuthenticateAsUser() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testUserCannotAuthenticateAsAdmin() + public function testUserCannotAuthenticateAsAdmin(): void { - $user = $this->createUser(); $errorMessages = $this->getContainer()->get('config')['authentication']['invalid_credentials']; + $user = $this->createUser(); $response = $this->post('/security/generate-token', $this->getValidAdminAccessTokenCredentials([ - 'username' => $user->getIdentity() + 'username' => $user->getIdentity(), ])); $data = json_decode($response->getBody()->getContents(), true); @@ -163,14 +165,14 @@ public function testUserCannotAuthenticateAsAdmin() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - private function authenticateInvalidIdentity(array $credentials) + private function authenticateInvalidIdentity(array $credentials): void { $errorMessages = $this->getContainer()->get('config')['authentication']['invalid_credentials']; $response = $this->post('/security/generate-token', $credentials); - $data = json_decode($response->getBody()->getContents(), true); - $this->assertResponseBadRequest($response); + + $data = json_decode($response->getBody()->getContents(), true); $this->assertArrayHasKey('error', $data); $this->assertArrayHasKey('error_description', $data); $this->assertArrayHasKey('message', $data); diff --git a/tests/AppTest/Functional/src/Exception/AuthenticationException.php b/test/Functional/Exception/AuthenticationException.php similarity index 82% rename from tests/AppTest/Functional/src/Exception/AuthenticationException.php rename to test/Functional/Exception/AuthenticationException.php index 1f445a4..751b608 100644 --- a/tests/AppTest/Functional/src/Exception/AuthenticationException.php +++ b/test/Functional/Exception/AuthenticationException.php @@ -2,12 +2,14 @@ declare(strict_types=1); -namespace AppTest\Functional\Exception; +namespace ApiTest\Functional\Exception; use Fig\Http\Message\StatusCodeInterface; use Psr\Http\Message\ResponseInterface; use RuntimeException; +use function sprintf; + class AuthenticationException extends RuntimeException { public static function fromResponse(ResponseInterface $response): self @@ -19,7 +21,7 @@ public static function invalidResponse(string $key): self { return new self( sprintf('The `%s` key is missing from the response', $key), - StatusCodeInterface::STATUS_BAD_REQUEST + StatusCodeInterface::STATUS_BAD_REQUEST ); } } diff --git a/tests/AppTest/Functional/src/Traits/AuthenticationTrait.php b/test/Functional/Traits/AuthenticationTrait.php similarity index 81% rename from tests/AppTest/Functional/src/Traits/AuthenticationTrait.php rename to test/Functional/Traits/AuthenticationTrait.php index e7223d9..6c009c9 100644 --- a/tests/AppTest/Functional/src/Traits/AuthenticationTrait.php +++ b/test/Functional/Traits/AuthenticationTrait.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace AppTest\Functional\Traits; +namespace ApiTest\Functional\Traits; -use AppTest\Functional\Exception\AuthenticationException; +use ApiTest\Functional\Exception\AuthenticationException; use Fig\Http\Message\RequestMethodInterface; use Fig\Http\Message\StatusCodeInterface; use Laminas\Diactoros\ServerRequest; @@ -14,10 +14,13 @@ use Psr\Container\NotFoundExceptionInterface; use Psr\Http\Message\ResponseFactoryInterface; +use function json_decode; +use function sprintf; + trait AuthenticationTrait { - private string $tokenType = 'Bearer'; - private ?string $accessToken = null; + private string $tokenType = 'Bearer'; + private ?string $accessToken = null; private ?string $refreshToken = null; private function setAccessToken(string $accessToken): self @@ -58,36 +61,34 @@ public function getTokenType(): string public function isAuthenticated(): bool { - return ! is_null($this->accessToken); + return $this->accessToken !== null; } /** * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function loginAs - ( + public function loginAs( string $identity, string $password, string $clientId = 'frontend', string $clientSecret = 'frontend', string $scope = 'api' - ): void - { + ): void { $request = $this->createLoginRequest([ - 'grant_type' => 'password', - 'client_id' => $clientId, + 'grant_type' => 'password', + 'client_id' => $clientId, 'client_secret' => $clientSecret, - 'scope' => $scope, - 'username' => $identity, - 'password' => $password, + 'scope' => $scope, + 'username' => $identity, + 'password' => $password, ]); $authorizationServer = $this->getContainer()->get(AuthorizationServer::class); - $responseFactory = $this->getContainer()->get(ResponseFactoryInterface::class); - $response = $responseFactory->createResponse(); + $responseFactory = $this->getContainer()->get(ResponseFactoryInterface::class); + $response = $responseFactory->createResponse(); try { - $response = $authorizationServer->respondToAccessTokenRequest($request, $responseFactory->createResponse()); + $response = $authorizationServer->respondToAccessTokenRequest($request, $response); } catch (OAuthServerException $exception) { $response = $exception->generateHttpResponse($response); } @@ -97,7 +98,7 @@ public function loginAs throw AuthenticationException::fromResponse($response); } - $body = json_decode($response->getBody()->getContents(),true); + $body = json_decode($response->getBody()->getContents(), true); if (! isset($body['token_type'])) { throw AuthenticationException::invalidResponse('token_type'); } @@ -133,7 +134,7 @@ private function createLoginRequest(array $bodyParams): ServerRequest public function getAuthorizationHeader(): array { return [ - 'Authorization' => sprintf('%s %s', $this->getTokenType(), $this->getAccessToken()) + 'Authorization' => sprintf('%s %s', $this->getTokenType(), $this->getAccessToken()), ]; } } diff --git a/tests/AppTest/Functional/src/Traits/DatabaseTrait.php b/test/Functional/Traits/DatabaseTrait.php similarity index 84% rename from tests/AppTest/Functional/src/Traits/DatabaseTrait.php rename to test/Functional/Traits/DatabaseTrait.php index 715431d..8e6053f 100644 --- a/tests/AppTest/Functional/src/Traits/DatabaseTrait.php +++ b/test/Functional/Traits/DatabaseTrait.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace AppTest\Functional\Traits; +namespace ApiTest\Functional\Traits; use Doctrine\Common\DataFixtures\Executor\ORMExecutor; -use Doctrine\Common\DataFixtures\Purger\ORMPurger; use Doctrine\Common\DataFixtures\Loader; +use Doctrine\Common\DataFixtures\Purger\ORMPurger; use Doctrine\ORM\Tools\SchemaTool; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -21,7 +21,7 @@ public function runMigrations(): void { $entityManager = $this->getEntityManager(); - $metaData = $entityManager->getMetadataFactory()->getAllMetadata(); + $metaData = $entityManager->getMetadataFactory()->getAllMetadata(); $schemaTool = new SchemaTool($entityManager); $schemaTool->updateSchema($metaData); } @@ -33,8 +33,9 @@ public function runMigrations(): void public function runSeeders(): void { $entityManager = $this->getEntityManager(); - $loader = new Loader(); - $purger = new ORMPurger($entityManager); + + $loader = new Loader(); + $purger = new ORMPurger($entityManager); $executor = new ORMExecutor($entityManager, $purger); $path = $this->getContainer()->get('config')['doctrine']['fixtures']; diff --git a/tests/AppTest/Functional/src/UserTest.php b/test/Functional/UserTest.php similarity index 81% rename from tests/AppTest/Functional/src/UserTest.php rename to test/Functional/UserTest.php index 384340e..07e9033 100644 --- a/tests/AppTest/Functional/src/UserTest.php +++ b/test/Functional/UserTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace AppTest\Functional; +namespace ApiTest\Functional; use Api\App\Message; use Api\User\Entity\User; @@ -13,17 +13,30 @@ use DateTimeImmutable; use Dot\Mail\Service\MailService; use Laminas\Diactoros\UploadedFile; +use PHPUnit\Framework\MockObject\Exception; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Psr\Http\Message\UploadedFileInterface; +use function imagecolorallocate; +use function imagecreatetruecolor; +use function imagefilledrectangle; +use function imagejpeg; +use function json_decode; +use function sprintf; +use function unlink; + +use const DIRECTORY_SEPARATOR; +use const UPLOAD_ERR_OK; + class UserTest extends AbstractFunctionalTest { /** * @throws ContainerExceptionInterface + * @throws Exception * @throws NotFoundExceptionInterface */ - public function testRegisterAccountDuplicateIdentity() + public function testRegisterAccountDuplicateIdentity(): void { $this->createUser([ 'status' => User::STATUS_PENDING, @@ -33,7 +46,6 @@ public function testRegisterAccountDuplicateIdentity() $this->replaceService(UserAvatarService::class, $userAvatarService); $response = $this->post('/user', $this->getValidUserData()); - $this->assertResponseBadRequest($response); $data = json_decode($response->getBody()->getContents(), true); @@ -46,20 +58,20 @@ public function testRegisterAccountDuplicateIdentity() /** * @throws ContainerExceptionInterface + * @throws Exception * @throws NotFoundExceptionInterface */ - public function testRegisterAccountDuplicateEmail() + public function testRegisterAccountDuplicateEmail(): void { $this->createUser([ 'identity' => 'foo@dotkernel.com', - 'status' => User::STATUS_PENDING, + 'status' => User::STATUS_PENDING, ]); $userAvatarService = $this->createMock(UserAvatarService::class); $this->replaceService(UserAvatarService::class, $userAvatarService); $response = $this->post('/user', $this->getValidUserData()); - $this->assertResponseBadRequest($response); $data = json_decode($response->getBody()->getContents(), true); @@ -70,12 +82,16 @@ public function testRegisterAccountDuplicateEmail() $this->assertContains(Message::DUPLICATE_EMAIL, $data['error']['messages']); } - public function testRegisterAccount() + /** + * @throws Exception + */ + public function testRegisterAccount(): void { $userAvatarService = $this->createMock(UserAvatarService::class); - $mailService = $this->createMock(MailService::class); $this->replaceService(UserAvatarService::class, $userAvatarService); - $this->replaceService(MailService::class , $mailService); + + $mailService = $this->createMock(MailService::class); + $this->replaceService(MailService::class, $mailService); $user = $this->getValidUserData([ 'status' => User::STATUS_PENDING, @@ -100,15 +116,13 @@ public function testRegisterAccount() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testCreateMyAvatar() + public function testCreateMyAvatar(): void { - $user = $this->createUser(); - $uploadedFile = $this->createUploadedFile(); $userAvatarRepository = $this->getEntityManager()->getRepository(UserAvatar::class); - $userAvatarService = $this->getMockBuilder(UserAvatarService::class) + $userAvatarService = $this->getMockBuilder(UserAvatarService::class) ->setConstructorArgs([ $userAvatarRepository, - [] + [], ]) ->onlyMethods([ 'ensureDirectoryExists', @@ -118,30 +132,30 @@ public function testCreateMyAvatar() 'saveAvatarImage', ]) ->getMock(); - $this->replaceService(UserAvatarService::class, $userAvatarService); + $user = $this->createUser(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); + $uploadedFile = $this->createUploadedFile(); + $response = $this->post('/user/my-avatar', [], [], ['avatar' => $uploadedFile]); + $this->assertResponseOk($response); $path = __DIR__ . DIRECTORY_SEPARATOR . $uploadedFile->getClientFilename(); unlink($path); - - $this->assertResponseOk($response); } /** * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testViewMyAvatarNotFound() + public function testViewMyAvatarNotFound(): void { $user = $this->createUser(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); $response = $this->get('/user/my-avatar'); - $this->assertResponseNotFound($response); } @@ -149,19 +163,20 @@ public function testViewMyAvatarNotFound() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testViewMyAvatar() + public function testViewMyAvatar(): void { $user = $this->createUser(); - $userAvatar = new UserAvatar(); - $userAvatar->setUser($user); - $userAvatar->setName('test'); + + $userAvatar = (new UserAvatar()) + ->setUser($user) + ->setName('test'); + $this->getEntityManager()->persist($userAvatar); $this->getEntityManager()->flush(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); $response = $this->get('/user/my-avatar'); - $this->assertResponseOk($response); } @@ -169,13 +184,12 @@ public function testViewMyAvatar() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testDeleteMyAvatarNotFound() + public function testDeleteMyAvatarNotFound(): void { $user = $this->createUser(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); $response = $this->delete('/user/my-avatar'); - $this->assertResponseNotFound($response); } @@ -183,23 +197,24 @@ public function testDeleteMyAvatarNotFound() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testDeleteMyAvatar() + public function testDeleteMyAvatar(): void { $user = $this->createUser(); + $userAvatar = (new UserAvatar()) ->setUser($user) ->setName('test'); + $this->getEntityManager()->persist($userAvatar); $this->getEntityManager()->flush(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); $response = $this->delete('/user/my-avatar'); - $this->assertResponseOk($response); } - public function testActivateMyAccountInvalidCode() + public function testActivateMyAccountInvalidCode(): void { $response = $this->patch('/account/activate/invalid_hash'); $this->assertResponseBadRequest($response); @@ -209,7 +224,7 @@ public function testActivateMyAccountInvalidCode() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testActivateMyAccountAlreadyActivated() + public function testActivateMyAccountAlreadyActivated(): void { $user = $this->createUser(); @@ -221,9 +236,8 @@ public function testActivateMyAccountAlreadyActivated() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testActivateMyAccount() + public function testActivateMyAccount(): void { - $userRepository = $this->getEntityManager()->getRepository(User::class); $user = $this->createUser([ 'status' => User::STATUS_PENDING, ]); @@ -231,31 +245,32 @@ public function testActivateMyAccount() $response = $this->patch('/account/activate/' . $user->getHash()); $this->assertResponseOk($response); - $user = $userRepository->find($user->getUuid()->toString()); + $userRepository = $this->getEntityManager()->getRepository(User::class); + $user = $userRepository->find($user->getUuid()->toString()); $this->assertTrue($user->isActive()); } /** * @throws ContainerExceptionInterface + * @throws Exception * @throws NotFoundExceptionInterface */ - public function testActivateAccountByEmail() + public function testActivateAccountByEmail(): void { - $mailService = $this->createMock(MailService::class); $user = $this->createUser([ 'status' => User::STATUS_PENDING, ]); + $mailService = $this->createMock(MailService::class); $this->replaceService(MailService::class, $mailService); $response = $this->post('/account/activate', [ - 'email' => $user->getDetail()->getEmail() + 'email' => $user->getDetail()->getEmail(), ]); + $this->assertResponseOk($response); $data = json_decode($response->getBody()->getContents(), true); - - $this->assertResponseOk($response); $this->assertArrayHasKey('info', $data); $this->assertArrayHasKey('messages', $data['info']); $this->assertSame( @@ -268,32 +283,31 @@ public function testActivateAccountByEmail() * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface */ - public function testDeleteMyAccount() + public function testDeleteMyAccount(): void { $user = $this->createUser(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); $response = $this->delete('/user/my-account'); - $this->assertResponseOk($response); - $userRepository = $this->getEntityManager()->getRepository(User::class); - $deletedUser = $userRepository->find($user->getUuid()->toString()); + $userRepository = $this->getEntityManager()->getRepository(User::class); + $deletedUser = $userRepository->find($user->getUuid()->toString()); $this->assertTrue($deletedUser->isDeleted()); } - public function testRequestResetPasswordInvalidHash() + public function testRequestResetPasswordInvalidHash(): void { $response = $this->patch('/account/reset-password/invalid_hash'); - $this->assertResponseNotFound($response); } /** - * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface + * @throws Exception + * @throws NotFoundExceptionInterface */ - public function testRequestResetPasswordExpired() + public function testRequestResetPasswordExpired(): void { $user = $this->createUser(); @@ -302,7 +316,6 @@ public function testRequestResetPasswordExpired() ->setStatus(UserResetPasswordEntity::STATUS_REQUESTED) ->setHash('test') ->setExpires((new DateTimeImmutable())->sub(new DateInterval('P1D'))); - $user->addResetPassword($resetPassword); $this->getEntityManager()->persist($resetPassword); @@ -313,13 +326,12 @@ public function testRequestResetPasswordExpired() $this->replaceService(MailService::class, $mailService); $response = $this->patch('/account/reset-password/' . $resetPassword->getHash(), [ - 'password' => '654321', + 'password' => '654321', 'passwordConfirm' => '654321', ]); + $this->assertResponseBadRequest($response); $data = json_decode($response->getBody()->getContents(), true); - - $this->assertResponseBadRequest($response); $this->assertArrayHasKey('error', $data); $this->assertArrayHasKey('messages', $data['error']); $this->assertNotEmpty($data['error']['messages'][0]); @@ -330,18 +342,21 @@ public function testRequestResetPasswordExpired() } /** - * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface + * @throws Exception + * @throws NotFoundExceptionInterface */ - public function testRequestResetPasswordAlreadyUsed() + public function testRequestResetPasswordAlreadyUsed(): void { $user = $this->createUser(); - $resetPassword = new UserResetPasswordEntity(); - $resetPassword->setUser($user); - $resetPassword->setStatus(UserResetPasswordEntity::STATUS_COMPLETED); - $resetPassword->setHash('test'); - $resetPassword->setExpires((new DateTimeImmutable())->add(new DateInterval('P1D'))); + + $resetPassword = (new UserResetPasswordEntity()) + ->setUser($user) + ->setStatus(UserResetPasswordEntity::STATUS_COMPLETED) + ->setHash('test') + ->setExpires((new DateTimeImmutable())->add(new DateInterval('P1D'))); $user->addResetPassword($resetPassword); + $this->getEntityManager()->persist($resetPassword); $this->getEntityManager()->persist($user); $this->getEntityManager()->flush(); @@ -350,13 +365,12 @@ public function testRequestResetPasswordAlreadyUsed() $this->replaceService(MailService::class, $mailService); $response = $this->patch('/account/reset-password/' . $resetPassword->getHash(), [ - 'password' => '654321', + 'password' => '654321', 'passwordConfirm' => '654321', ]); + $this->assertResponseBadRequest($response); $data = json_decode($response->getBody()->getContents(), true); - - $this->assertResponseBadRequest($response); $this->assertArrayHasKey('error', $data); $this->assertArrayHasKey('messages', $data['error']); $this->assertNotEmpty($data['error']['messages'][0]); @@ -369,16 +383,19 @@ public function testRequestResetPasswordAlreadyUsed() /** * @throws NotFoundExceptionInterface * @throws ContainerExceptionInterface + * @throws Exception */ - public function testResetPassword() + public function testResetPassword(): void { $user = $this->createUser(); - $resetPassword = new UserResetPasswordEntity(); - $resetPassword->setUser($user); - $resetPassword->setStatus(UserResetPasswordEntity::STATUS_REQUESTED); - $resetPassword->setHash('test'); - $resetPassword->setExpires((new DateTimeImmutable())->add(new DateInterval('P1D'))); + + $resetPassword = (new UserResetPasswordEntity()) + ->setUser($user) + ->setStatus(UserResetPasswordEntity::STATUS_REQUESTED) + ->setHash('test') + ->setExpires((new DateTimeImmutable())->add(new DateInterval('P1D'))); $user->addResetPassword($resetPassword); + $this->getEntityManager()->persist($resetPassword); $this->getEntityManager()->persist($user); $this->getEntityManager()->flush(); @@ -387,13 +404,12 @@ public function testResetPassword() $this->replaceService(MailService::class, $mailService); $response = $this->patch('/account/reset-password/' . $resetPassword->getHash(), [ - 'password' => '654321', + 'password' => '654321', 'passwordConfirm' => '654321', ]); + $this->assertResponseOk($response); $data = json_decode($response->getBody()->getContents(), true); - - $this->assertResponseOk($response); $this->assertArrayHasKey('info', $data); $this->assertArrayHasKey('messages', $data['info']); $this->assertNotEmpty($data['info']['messages'][0]); @@ -402,19 +418,19 @@ public function testResetPassword() /** * @throws ContainerExceptionInterface + * @throws Exception * @throws NotFoundExceptionInterface */ - public function testResetPasswordByEmail() + public function testResetPasswordByEmail(): void { - $user = $this->createUser(); - $mailService = $this->createMock(MailService::class); $this->replaceService(MailService::class, $mailService); + $user = $this->createUser(); + $response = $this->post('/account/reset-password', [ 'email' => $user->getDetail()->getEmail(), ]); - $this->assertResponseOk($response); $this->assertCount(1, $user->getResetPasswords()); } @@ -423,13 +439,13 @@ public function testResetPasswordByEmail() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testViewMyAccount() + public function testViewMyAccount(): void { $user = $this->createUser(); $this->loginAs($user->getIdentity(), self::DEFAULT_PASSWORD); - $response = $this->get('/user/my-account'); + $response = $this->get('/user/my-account'); $this->assertResponseOk($response); } @@ -437,7 +453,7 @@ public function testViewMyAccount() * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function testUpdateMyAccount() + public function testUpdateMyAccount(): void { $user = $this->createUser(); @@ -446,24 +462,24 @@ public function testUpdateMyAccount() $updateData = [ 'detail' => [ 'firstName' => 'John', - 'lastName' => 'Doe', - ] + 'lastName' => 'Doe', + ], ]; $response = $this->patch('/user/my-account', $updateData); - $this->assertResponseOk($response); - $data = json_decode($response->getBody()->getContents(), true); + $data = json_decode($response->getBody()->getContents(), true); $this->assertSame($updateData['detail']['firstName'], $data['detail']['firstName']); $this->assertSame($updateData['detail']['lastName'], $data['detail']['lastName']); } /** * @throws ContainerExceptionInterface + * @throws Exception * @throws NotFoundExceptionInterface */ - public function testRecoverAccountByIdentity() + public function testRecoverAccountByIdentity(): void { $user = $this->createUser(); @@ -473,10 +489,9 @@ public function testRecoverAccountByIdentity() $response = $this->post('/account/recover-identity', [ 'email' => $user->getDetail()->getEmail(), ]); + $this->assertResponseOk($response); $data = json_decode($response->getBody()->getContents(), true); - - $this->assertResponseOk($response); $this->assertArrayHasKey('info', $data); $this->assertArrayHasKey('messages', $data['info']); $this->assertNotEmpty($data['info']['messages'][0]); @@ -486,10 +501,10 @@ public function testRecoverAccountByIdentity() private function createUploadedFile(): UploadedFileInterface { $img = imagecreatetruecolor(120, 20); - $bg = imagecolorallocate ($img, 255, 255, 255); - imagefilledrectangle($img,0,0,120,20,$bg); + $bg = imagecolorallocate($img, 255, 255, 255); + imagefilledrectangle($img, 0, 0, 120, 20, $bg); $path = __DIR__ . DIRECTORY_SEPARATOR . 'test.jpg'; - imagejpeg($img, $path,100); + imagejpeg($img, $path, 100); return new UploadedFile($path, 10, UPLOAD_ERR_OK, 'test.jpg', 'image/jpg'); } diff --git a/tests/AppTest/Unit/AdminServiceTest.php b/test/Unit/AdminServiceTest.php similarity index 82% rename from tests/AppTest/Unit/AdminServiceTest.php rename to test/Unit/AdminServiceTest.php index 2383c26..8ad7003 100644 --- a/tests/AppTest/Unit/AdminServiceTest.php +++ b/test/Unit/AdminServiceTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace AppTest\Unit; +namespace ApiTest\Unit; use Api\Admin\Entity\Admin; use Api\Admin\Entity\AdminRole; @@ -13,20 +13,23 @@ use Exception; use PHPUnit\Framework\TestCase; +use function array_merge; +use function count; + class AdminServiceTest extends TestCase { private Subject $subject; private AdminRoleService $adminRoleService; private AdminRepository $adminRepository; + /** + * @throws \PHPUnit\Framework\MockObject\Exception + */ public function setUp(): void { - parent::setUp(); - $this->adminRoleService = $this->createMock(AdminRoleService::class); - $this->adminRepository = $this->createMock(AdminRepository::class); - - $this->subject = $this->getMockBuilder(Subject::class) + $this->adminRepository = $this->createMock(AdminRepository::class); + $this->subject = $this->getMockBuilder(Subject::class) ->setConstructorArgs([ $this->adminRoleService, $this->adminRepository, @@ -40,7 +43,7 @@ public function setUp(): void /** * @throws Exception */ - public function testCreateAdminThrowsDuplicateIdentity() + public function testCreateAdminThrowsDuplicateIdentity(): void { $this->expectException(Exception::class); $this->expectExceptionMessage(Message::DUPLICATE_IDENTITY); @@ -50,7 +53,10 @@ public function testCreateAdminThrowsDuplicateIdentity() $this->subject->createAdmin(['identity' => 'admin@dotkernel.com']); } - public function testCreateAdminSuperAdminRole() + /** + * @throws Exception + */ + public function testCreateAdminSuperAdminRole(): void { $data = $this->getAdmin([ 'roles' => [ @@ -80,10 +86,10 @@ public function testCreateAdminSuperAdminRole() private function getAdmin(array $data = []): array { $admin = [ - 'identity' => 'admin@dotkernel.com', - 'password' => 'dotkernel', + 'identity' => 'admin@dotkernel.com', + 'password' => 'dotkernel', 'firstName' => 'firstname', - 'lastName' => 'lastname', + 'lastName' => 'lastname', ]; return array_merge($admin, $data); diff --git a/tests/AppTest/Unit/AuthenticationMiddlewareTest.php b/test/Unit/AuthenticationMiddlewareTest.php similarity index 76% rename from tests/AppTest/Unit/AuthenticationMiddlewareTest.php rename to test/Unit/AuthenticationMiddlewareTest.php index f596fab..93b1b1c 100644 --- a/tests/AppTest/Unit/AuthenticationMiddlewareTest.php +++ b/test/Unit/AuthenticationMiddlewareTest.php @@ -2,13 +2,14 @@ declare(strict_types=1); -namespace AppTest\Unit; +namespace ApiTest\Unit; use Api\App\Middleware\AuthenticationMiddleware as Subject; use Api\User\Entity\UserRole; use Laminas\Diactoros\ServerRequest; use Mezzio\Authentication\AuthenticationInterface; use Mezzio\Authentication\UserInterface; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -22,25 +23,25 @@ class AuthenticationMiddlewareTest extends TestCase private RequestHandlerInterface $handler; private ResponseInterface $response; + /** + * @throws Exception + */ public function setUp(): void { - parent::setUp(); - - $this->auth = $this->createMock(AuthenticationInterface::class); - $this->handler = $this->createMock(RequestHandlerInterface::class); + $this->auth = $this->createMock(AuthenticationInterface::class); + $this->handler = $this->createMock(RequestHandlerInterface::class); $this->response = $this->createMock(ResponseInterface::class); - $this->request = new ServerRequest(); - - $this->subject = new Subject($this->auth); + $this->request = new ServerRequest(); + $this->subject = new Subject($this->auth); } - public function testAuthenticationFailsFallbackToGuestUser() + public function testAuthenticationFailsFallbackToGuestUser(): void { $this->auth->method('authenticate')->willReturn(null); $this->handler->expects($this->once()) ->method('handle') - ->will($this->returnCallback(function(ServerRequestInterface $request) { + ->will($this->returnCallback(function (ServerRequestInterface $request) { $user = $request->getAttribute(UserInterface::class); $this->assertInstanceOf(UserInterface::class, $user); $this->assertSame(UserRole::ROLE_GUEST, $user->getIdentity()); diff --git a/tests/AppTest/Unit/AuthorizationMiddlewareTest.php b/test/Unit/AuthorizationMiddlewareTest.php similarity index 74% rename from tests/AppTest/Unit/AuthorizationMiddlewareTest.php rename to test/Unit/AuthorizationMiddlewareTest.php index 206bc87..03b1393 100644 --- a/tests/AppTest/Unit/AuthorizationMiddlewareTest.php +++ b/test/Unit/AuthorizationMiddlewareTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace AppTest\Unit; +namespace ApiTest\Unit; use Api\Admin\Entity\Admin; use Api\Admin\Entity\AdminRole; @@ -17,11 +17,15 @@ use Laminas\Http\Response; use Mezzio\Authentication\UserInterface; use Mezzio\Authorization\AuthorizationInterface; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use function json_decode; +use function sprintf; + class AuthorizationMiddlewareTest extends TestCase { private Subject $subject; @@ -32,86 +36,87 @@ class AuthorizationMiddlewareTest extends TestCase private RequestHandlerInterface $handler; private ResponseInterface $response; + /** + * @throws Exception + */ public function setUp(): void { - parent::setUp(); - - $this->userRepository = $this->createMock(UserRepository::class); + $this->userRepository = $this->createMock(UserRepository::class); $this->adminRepository = $this->createMock(AdminRepository::class); - $this->authorization = $this->createMock(AuthorizationInterface::class); - $this->handler = $this->createMock(RequestHandlerInterface::class); - $this->response = $this->createMock(ResponseInterface::class); - $this->request = new ServerRequest(); - - $this->subject = new Subject($this->authorization, $this->userRepository, $this->adminRepository); + $this->authorization = $this->createMock(AuthorizationInterface::class); + $this->handler = $this->createMock(RequestHandlerInterface::class); + $this->response = $this->createMock(ResponseInterface::class); + $this->request = new ServerRequest(); + $this->subject = new Subject( + $this->authorization, + $this->userRepository, + $this->adminRepository + ); } - public function testAuthorizationInvalidClientIdProvided() + public function testAuthorizationInvalidClientIdProvided(): void { - $identity = new UserIdentity('test@dotkernel.com', ['user'], ['oauth_client_id' => 'invalid_client_id']); - + $identity = new UserIdentity('test@dotkernel.com', ['user'], ['oauth_client_id' => 'invalid_client_id']); $this->request = $this->request->withAttribute(UserInterface::class, $identity); $response = $this->subject->process($this->request, $this->handler); - $data = json_decode($response->getBody()->getContents(), true); - $this->assertSame(Response::STATUS_CODE_403, $response->getStatusCode()); + + $data = json_decode($response->getBody()->getContents(), true); $this->assertArrayHasKey('error', $data); $this->assertArrayHasKey('messages', $data['error']); $this->assertContains(Message::INVALID_CLIENT_ID, $data['error']['messages']); } - public function testAuthorizationInactiveAdmin() + public function testAuthorizationInactiveAdmin(): void { - $identity = new UserIdentity('admin@dotkernel.com', ['admin'], ['oauth_client_id' => 'admin']); - $user = (new Admin()) ->setIdentity('admin@dotkernel.com') ->setStatus(Admin::STATUS_INACTIVE) ->addRole((new AdminRole())->setName(AdminRole::ROLE_ADMIN)); - $this->adminRepository->method('findOneBy')->willReturn($user); + + $identity = new UserIdentity('admin@dotkernel.com', ['admin'], ['oauth_client_id' => 'admin']); $this->request = $this->request->withAttribute(UserInterface::class, $identity); $response = $this->subject->process($this->request, $this->handler); - $data = json_decode($response->getBody()->getContents(), true); - $this->assertSame(Response::STATUS_CODE_403, $response->getStatusCode()); + + $data = json_decode($response->getBody()->getContents(), true); $this->assertArrayHasKey('error', $data); $this->assertArrayHasKey('messages', $data['error']); $this->assertContains(Message::ADMIN_NOT_ACTIVATED, $data['error']['messages']); } - public function testAuthorizationInactiveUser() + public function testAuthorizationInactiveUser(): void { - $identity = new UserIdentity('test@dotkernel.com', ['user'], ['oauth_client_id' => 'frontend']); - $user = new User(); + $this->userRepository->method('findOneBy')->willReturn(new User()); - $this->userRepository->method('findOneBy')->willReturn($user); + $identity = new UserIdentity('test@dotkernel.com', ['user'], ['oauth_client_id' => 'frontend']); $this->request = $this->request->withAttribute(UserInterface::class, $identity); $response = $this->subject->process($this->request, $this->handler); - $data = json_decode($response->getBody()->getContents(), true); - $this->assertSame(Response::STATUS_CODE_403, $response->getStatusCode()); + + $data = json_decode($response->getBody()->getContents(), true); $this->assertArrayHasKey('error', $data); $this->assertArrayHasKey('messages', $data['error']); $this->assertContains(Message::USER_NOT_ACTIVATED, $data['error']['messages']); } - public function testAuthorizationUserNotFoundOrDeleted() + public function testAuthorizationUserNotFoundOrDeleted(): void { - $identity = new UserIdentity('test@dotkernel.com', ['user'], ['oauth_client_id' => 'frontend']); $user = (new User())->markAsDeleted(); - $this->userRepository->method('findOneBy')->willReturn($user); - $this->request = $this->request->withAttribute(UserInterface::class, $identity); $this->authorization->method('isGranted')->willReturn(false); - $response = $this->subject->process($this->request, $this->handler); - $data = json_decode($response->getBody()->getContents(), true); + $identity = new UserIdentity('test@dotkernel.com', ['user'], ['oauth_client_id' => 'frontend']); + $this->request = $this->request->withAttribute(UserInterface::class, $identity); + $response = $this->subject->process($this->request, $this->handler); $this->assertSame(Response::STATUS_CODE_403, $response->getStatusCode()); + + $data = json_decode($response->getBody()->getContents(), true); $this->assertArrayHasKey('error', $data); $this->assertArrayHasKey('messages', $data['error']); $this->assertContains( @@ -120,22 +125,21 @@ public function testAuthorizationUserNotFoundOrDeleted() ); } - public function testAuthorizationNotGranted() + public function testAuthorizationNotGranted(): void { - $identity = new UserIdentity('test@dotkernel.com', ['user'], ['oauth_client_id' => 'frontend']); - $user = (new User()) ->setIdentity('test@dotkernel.com') ->activate() ->addRole((new UserRole())->setName(UserRole::ROLE_USER)); - $this->userRepository->method('findOneBy')->willReturn($user); + + $identity = new UserIdentity('test@dotkernel.com', ['user'], ['oauth_client_id' => 'frontend']); $this->request = $this->request->withAttribute(UserInterface::class, $identity); $response = $this->subject->process($this->request, $this->handler); - $data = json_decode($response->getBody()->getContents(), true); - $this->assertSame(Response::STATUS_CODE_403, $response->getStatusCode()); + + $data = json_decode($response->getBody()->getContents(), true); $this->assertArrayHasKey('error', $data); $this->assertArrayHasKey('messages', $data['error']); $this->assertContains( @@ -144,23 +148,22 @@ public function testAuthorizationNotGranted() ); } - public function testAuthorizationAccessGranted() + public function testAuthorizationAccessGranted(): void { - $identity = new UserIdentity('test@dotkernel.com', ['user'], ['oauth_client_id' => 'frontend']); - $user = (new User()) ->setIdentity('test@dotkernel.com') ->activate() ->addRole((new UserRole())->setName(UserRole::ROLE_USER)); - $this->userRepository->method('findOneBy')->willReturn($user); - $this->request = $this->request->withAttribute(UserInterface::class, $identity); $this->authorization->method('isGranted')->willReturn(true); + $identity = new UserIdentity('test@dotkernel.com', ['user'], ['oauth_client_id' => 'frontend']); + $this->request = $this->request->withAttribute(UserInterface::class, $identity); + $this->handler ->expects($this->once()) ->method('handle') - ->will($this->returnCallback(function(ServerRequestInterface $request) use($identity) { + ->will($this->returnCallback(function (ServerRequestInterface $request) use ($identity) { $user = $request->getAttribute(UserInterface::class); $this->assertSame($identity->getIdentity(), $user->getIdentity()); $this->assertSame($identity->getDetails(), $user->getDetails()); diff --git a/tests/AppTest/Unit/UserAvatarTest.php b/test/Unit/UserAvatarTest.php similarity index 69% rename from tests/AppTest/Unit/UserAvatarTest.php rename to test/Unit/UserAvatarTest.php index 4e4af15..8ff5445 100644 --- a/tests/AppTest/Unit/UserAvatarTest.php +++ b/test/Unit/UserAvatarTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace AppTest\Unit; +namespace ApiTest\Unit; use Api\User\Entity\User; use Api\User\Entity\UserAvatar; @@ -10,6 +10,7 @@ use Api\User\Service\UserAvatarService; use Api\User\Service\UserAvatarService as Subject; use Laminas\Diactoros\UploadedFile; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; class UserAvatarTest extends TestCase @@ -17,14 +18,14 @@ class UserAvatarTest extends TestCase private Subject $subject; private UploadedFile $uploadedFile; + /** + * @throws Exception + */ public function setUp(): void { - parent::setUp(); - $userAvatarRepository = $this->createMock(UserAvatarRepository::class); - $this->uploadedFile = $this->createMock(UploadedFile::class); - - $this->subject = $this->getMockBuilder(UserAvatarService::class) + $this->uploadedFile = $this->createMock(UploadedFile::class); + $this->subject = $this->getMockBuilder(UserAvatarService::class) ->setConstructorArgs([ $userAvatarRepository, [], @@ -38,58 +39,39 @@ public function setUp(): void ->getMock(); $this->uploadedFile->method('getClientMediaType')->willReturn('image/jpg'); - } - public function testCreateAvatarOverwrite() + public function testCreateAvatarOverwrite(): void { - $path = '/test'; $fileName = 'file_name'; - $user = $this->getUser(); - $this->subject->method('getUserAvatarDirectoryPath')->willReturn($path); + $this->subject->method('getUserAvatarDirectoryPath')->willReturn('/test'); $this->subject->method('createFileName')->willReturn($fileName); + $user = $this->getUser(); $avatar = $this->subject->createAvatar($user, $this->uploadedFile); $this->assertInstanceOf(UserAvatar::class, $avatar); $this->assertSame($fileName, $avatar->getName()); } - public function testCreateAvatarDefault() + public function testCreateAvatarDefault(): void { - $path = '/test'; $fileName = 'file_name'; - $user = new User(); - $this->subject->method('getUserAvatarDirectoryPath')->willReturn($path); + $this->subject->method('getUserAvatarDirectoryPath')->willReturn('/test'); $this->subject->method('createFileName')->willReturn($fileName); + $user = new User(); $avatar = $this->subject->createAvatar($user, $this->uploadedFile); $this->assertInstanceOf(UserAvatar::class, $avatar); $this->assertSame($fileName, $avatar->getName()); } - public function removeAvatarUserAvatarNotFound() - { - $result = $this->subject->removeAvatar(new User()); - - $this->assertFalse($result); - } - - public function removeAvatar() - { - $this->subject->method('deleteAvatarFile')->willReturn(true); - - $result = $this->subject->removeAvatar(new User()); - - $this->assertTrue($result); - } - private function getUser(): User { - $user = new User(); + $user = new User(); $avatar = new UserAvatar(); $avatar->setName('test'); $avatar->setUser($user); diff --git a/tests/AppTest/Unit/UserServiceTest.php b/test/Unit/UserServiceTest.php similarity index 80% rename from tests/AppTest/Unit/UserServiceTest.php rename to test/Unit/UserServiceTest.php index 2707f85..8252372 100644 --- a/tests/AppTest/Unit/UserServiceTest.php +++ b/test/Unit/UserServiceTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace AppTest\Unit; +namespace ApiTest\Unit; use Api\App\Repository\OAuthAccessTokenRepository; use Api\App\Repository\OAuthRefreshTokenRepository; @@ -18,6 +18,9 @@ use Mezzio\Template\TemplateRendererInterface; use PHPUnit\Framework\TestCase; +use function array_merge; +use function count; + class UserServiceTest extends TestCase { private Subject $subject; @@ -25,29 +28,27 @@ class UserServiceTest extends TestCase private UserRepository $userRepository; private UserDetailRepository $userDetailRepository; + /** + * @throws \PHPUnit\Framework\MockObject\Exception + */ public function setUp(): void { - $mailService = $this->createMock(MailService::class); - $this->userRoleService = $this->createMock(UserRoleService::class); - $templateRendererInterface = $this->createMock(TemplateRendererInterface::class); - $oAuthAccessTokenRepository = $this->createMock(OAuthAccessTokenRepository::class); - $oAuthRefreshTokenRepository = $this->createMock(OAuthRefreshTokenRepository::class); - $this->userRepository = $this->createMock(UserRepository::class); + $this->userRoleService = $this->createMock(UserRoleService::class); + $this->userRepository = $this->createMock(UserRepository::class); $this->userDetailRepository = $this->createMock(UserDetailRepository::class); - - $this->subject = new Subject( + $this->subject = new Subject( $this->userRoleService, - $mailService, - $templateRendererInterface, - $oAuthAccessTokenRepository, - $oAuthRefreshTokenRepository, + $this->createMock(MailService::class), + $this->createMock(TemplateRendererInterface::class), + $this->createMock(OAuthAccessTokenRepository::class), + $this->createMock(OAuthRefreshTokenRepository::class), $this->userRepository, $this->userDetailRepository, - [], + [] ); } - public function testCreateUserThrowsExceptionDuplicateIdentity() + public function testCreateUserThrowsExceptionDuplicateIdentity(): void { $this->userRepository->method('findOneBy')->willReturn( $this->getUserEntity($this->getUser()) @@ -60,7 +61,10 @@ public function testCreateUserThrowsExceptionDuplicateIdentity() ]); } - public function testCreateUserWithMultipleRoles() + /** + * @throws Exception + */ + public function testCreateUserWithMultipleRoles(): void { $data = $this->getUser([ 'roles' => [ @@ -72,7 +76,7 @@ public function testCreateUserWithMultipleRoles() 'uuid' => 'uuid', 'name' => UserRole::ROLE_USER, ], - ] + ], ]); $this->userRoleService->method('findOneBy')->willReturn(new UserRole()); @@ -84,15 +88,18 @@ public function testCreateUserWithMultipleRoles() $this->assertCount(count($data['roles']), $user->getRoles()); } - public function testCreateUserWithDefaultRole() + /** + * @throws Exception + */ + public function testCreateUserWithDefaultRole(): void { $data = $this->getUser([ 'roles' => [ [ 'uuid' => 'uuid', 'name' => UserRole::ROLE_USER, - ] - ] + ], + ], ]); $defaultRole = (new UserRole())->setName(UserRole::ROLE_USER); @@ -107,7 +114,10 @@ public function testCreateUserWithDefaultRole() $this->assertSame($defaultRole->getName(), ($user->getRoles()->first())->getName()); } - public function testCreateUser() + /** + * @throws Exception + */ + public function testCreateUser(): void { $this->userRoleService->method('findOneBy')->willReturn(new UserRole()); $this->userRepository->method('saveUser')->willReturn( @@ -128,7 +138,7 @@ public function testCreateUser() $this->assertFalse($user->isActive()); } - public function testUpdateUserThrowsExceptionDuplicateUserDetailEmail() + public function testUpdateUserThrowsExceptionDuplicateUserDetailEmail(): void { $this->userDetailRepository->method('findOneBy')->willReturn($this->getUserEntity()->getDetail()); @@ -136,12 +146,15 @@ public function testUpdateUserThrowsExceptionDuplicateUserDetailEmail() $this->subject->updateUser($this->getUserEntity(), [ 'detail' => [ - 'email' => 'test@dotkernel.com' + 'email' => 'test@dotkernel.com', ], ]); } - public function testUpdateUser() + /** + * @throws Exception + */ + public function testUpdateUser(): void { $user = $this->getUserEntity($this->getUser()); @@ -150,10 +163,10 @@ public function testUpdateUser() $updateData = [ 'identity' => 'test@test.com', 'password' => '654321', - 'detail' => [ + 'detail' => [ 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'email' => 'email@test.com', + 'lastName' => 'lastname', + 'email' => 'email@test.com', ], ]; @@ -170,11 +183,11 @@ private function getUser(array $data = []): array $user = [ 'identity' => 'test@dotkernel.com', 'password' => 'dotkernel', - 'detail' => [ + 'detail' => [ 'firstName' => 'first', - 'lastName' => 'last', - 'email' => 'test@dotkernel2.com', - ] + 'lastName' => 'last', + 'email' => 'test@dotkernel2.com', + ], ]; return array_merge($user, $data); @@ -204,4 +217,3 @@ private function getUserEntity(array $data = []): User return $user; } } -