From 3f0c85b159c326c0501af8795ffe79dd7db443e0 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sat, 18 Mar 2023 12:52:50 +0200 Subject: [PATCH 01/20] Upgrade framework to 3.5, move to Symfony/Mailer, update codebase --- .editorconfig | 15 + .rr.yaml | 21 +- app.php | 11 +- app/config/cache.php | 6 +- app/config/cycle.php | 6 +- app/config/database.php | 30 +- app/config/queue.php | 6 +- app/config/tokenizer.php | 11 + app/helpers/debug.php | 48 + app/locale/ru/messages.en.php | 15 - app/src/App.php | 107 +- app/src/Bootloader/CheckerBootloader.php | 6 +- .../Bootloader/ExceptionHandlerBootloader.php | 58 + app/src/Bootloader/FirebaseBootloader.php | 1 - app/src/Bootloader/LoggingBootloader.php | 42 +- app/src/Bootloader/MailerBootloader.php | 26 +- app/src/Bootloader/RouteGroupsBootloader.php | 20 - app/src/Bootloader/RoutesBootloader.php | 59 + app/src/Config/AppConfig.php | 2 +- app/src/Config/CdnConfig.php | 2 +- app/src/Config/CorsConfig.php | 2 +- app/src/Config/FirebaseConfig.php | 2 +- app/src/Config/JwtConfig.php | 2 +- app/src/Config/MailConfig.php | 2 +- app/src/Config/S3Config.php | 2 +- app/src/Controller/Auth/LoginController.php | 7 +- .../Controller/Auth/RegisterController.php | 15 +- .../Controller/Profile/ProfileController.php | 13 +- app/src/Controller/Tags/ChargesController.php | 12 +- app/src/Controller/Tags/CommonController.php | 4 +- app/src/Controller/Tags/TagsController.php | 10 +- .../Controller/Wallets/ActiveController.php | 8 +- .../Controller/Wallets/ArchiveController.php | 8 +- .../Wallets/Charges/ChargesController.php | 24 +- .../Wallets/Charges/TagsController.php | 12 +- .../Controller/Wallets/IndexController.php | 16 +- app/src/Controller/Wallets/TagsController.php | 18 +- .../Controller/Wallets/UsersController.php | 18 +- .../Controller/Wallets/WalletsController.php | 10 +- app/src/Request/CheckNickNameRequest.php | 39 +- app/src/Request/JsonErrorsRenderer.php | 22 + app/src/Security/PasswordChecker.php | 2 +- app/src/Security/UniqueChecker.php | 2 +- app/src/Service/Mailer/Mail.php | 28 +- app/src/Service/Mailer/Mailer.php | 21 +- composer.json | 187 +- composer.lock | 2208 ++++++++++++----- phpunit.xml | 42 +- tests/App/{TestApp.php => TestKernel.php} | 2 +- .../Bootloader/LoggingBootloaderTest.php | 25 - .../Mail/EmailConfirmationMailTest.php | 11 +- tests/Feature/Mail/ForgotPasswordMailTest.php | 11 +- tests/Feature/Mail/TestMailTest.php | 11 +- tests/Feature/Mail/WalletShareMailTest.php | 11 +- tests/Feature/Service/CorsServiceTest.php | 22 +- tests/Feature/Service/Mailer/MailTest.php | 55 +- tests/Feature/Service/Mailer/MailerTest.php | 28 +- tests/TestCase.php | 17 +- tests/Traits/InteractsWithHttp.php | 12 +- 59 files changed, 2360 insertions(+), 1073 deletions(-) create mode 100644 .editorconfig create mode 100644 app/config/tokenizer.php create mode 100644 app/helpers/debug.php delete mode 100644 app/locale/ru/messages.en.php create mode 100644 app/src/Bootloader/ExceptionHandlerBootloader.php delete mode 100644 app/src/Bootloader/RouteGroupsBootloader.php create mode 100644 app/src/Bootloader/RoutesBootloader.php create mode 100644 app/src/Request/JsonErrorsRenderer.php rename tests/App/{TestApp.php => TestKernel.php} (74%) delete mode 100644 tests/Feature/Bootloader/LoggingBootloaderTest.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..bcc67af --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.yml] +indent_size = 2 + +[*.yaml] +indent_size = 2 diff --git a/.rr.yaml b/.rr.yaml index 2a7092f..3ddafc8 100644 --- a/.rr.yaml +++ b/.rr.yaml @@ -4,19 +4,34 @@ rpc: listen: tcp://127.0.0.1:6001 server: - command: "php app.php" + command: 'php app.php' relay: pipes http: address: 0.0.0.0:8080 - middleware: [ "gzip", "http_metrics" ] + middleware: + - gzip + - http_metrics pool: + num_workers: 1 supervisor: max_worker_memory: 100 +kv: + local: + driver: memory + config: + interval: 60 + logs: mode: production level: info metrics: - address: 0.0.0.0:2112 \ No newline at end of file + address: 0.0.0.0:2112 + +jobs: + pool: + num_workers: 2 + max_worker_memory: 100 + consume: { } diff --git a/app.php b/app.php index aaa3cc7..d4ca321 100644 --- a/app.php +++ b/app.php @@ -4,21 +4,18 @@ use App\App; -// // If you forgot to configure some of this in your php.ini file, // then don't worry, we will set the standard environment // settings for you. -// -mb_internal_encoding('UTF-8'); -error_reporting(E_ALL | E_STRICT ^ E_DEPRECATED); -ini_set('display_errors', 'stderr'); +\mb_internal_encoding('UTF-8'); +\error_reporting(E_ALL | E_STRICT ^ E_DEPRECATED); +\ini_set('display_errors', 'stderr'); require __DIR__ . '/vendor/autoload.php'; -// + // Initialize shared container, bindings, directories and etc. -// $app = App::create(['root' => __DIR__])->run(); if ($app === null) { diff --git a/app/config/cache.php b/app/config/cache.php index a780dd0..b245c5b 100644 --- a/app/config/cache.php +++ b/app/config/cache.php @@ -16,7 +16,11 @@ * Aliases, if you want to use domain specific storages. */ 'aliases' => [ - 'user-data' => 'localMemory', + // 'user-data' => [ + // 'storage' => 'rr-local', + // 'prefix' => 'user_' + // ], + // 'blog-data' => 'rr-local', ], /** diff --git a/app/config/cycle.php b/app/config/cycle.php index 9589930..cd5ab78 100644 --- a/app/config/cycle.php +++ b/app/config/cycle.php @@ -14,7 +14,7 @@ * false - Schema won't be stored in a cache after compilation. * It will be automatically changed after entity modification. (Development mode) */ - 'cache' => true, + 'cache' => env('CYCLE_SCHEMA_CACHE', true), /** * The CycleORM provides the ability to manage default settings for @@ -25,7 +25,7 @@ // SchemaInterface::REPOSITORY => \Cycle\ORM\Select\Repository::class, // SchemaInterface::SCOPE => null, // SchemaInterface::TYPECAST_HANDLER => [ - // \Cycle\ORM\Parser\Typecast::class + // \Cycle\ORM\Parser\Typecast::class, \App\Infrastructure\CycleORM\Typecaster\UuidTypecast::class, // ], ], @@ -58,6 +58,8 @@ // ], ], + 'warmup' => env('CYCLE_SCHEMA_WARMUP', false), + /** * Custom relation types for entities */ diff --git a/app/config/database.php b/app/config/database.php index be3cc71..d1d6ed2 100644 --- a/app/config/database.php +++ b/app/config/database.php @@ -5,10 +5,38 @@ use Cycle\Database\Config; return [ + 'logger' => [ + 'default' => null, + 'drivers' => [ + // 'runtime' => 'stdout' + ], + ], + + /** + * Default database connection + */ 'default' => 'default', + + /** + * The Spiral/Database module provides support to manage multiple databases + * in one application, use read/write connections and logically separate + * multiple databases within one connection using prefixes. + * + * To register a new database simply add a new one into + * "databases" section below. + */ 'databases' => [ - 'default' => ['driver' => 'default'], + 'default' => [ + 'driver' => 'default', + ], ], + + /** + * Each database instance must have an associated connection object. + * Connections used to provide low-level functionality and wrap different + * database drivers. To register a new connection you have to specify + * the driver class and its connection options. + */ 'drivers' => [ 'default' => new Config\MySQLDriverConfig( connection: new Config\MySQL\DsnConnectionConfig( diff --git a/app/config/queue.php b/app/config/queue.php index e6c9911..32b4ea2 100644 --- a/app/config/queue.php +++ b/app/config/queue.php @@ -2,10 +2,12 @@ declare(strict_types=1); +use Spiral\Queue\Driver\SyncDriver; use Spiral\RoadRunner\Jobs\Queue\MemoryCreateInfo; use Spiral\RoadRunner\Jobs\Queue\AMQPCreateInfo; use Spiral\RoadRunner\Jobs\Queue\BeanstalkCreateInfo; use Spiral\RoadRunner\Jobs\Queue\SQSCreateInfo; +use Spiral\RoadRunnerBridge\Queue\Queue; return [ /** @@ -61,8 +63,8 @@ ], 'driverAliases' => [ - 'sync' => \Spiral\Queue\Driver\SyncDriver::class, - 'roadrunner' => \Spiral\RoadRunnerBridge\Queue\Queue::class, + 'sync' => SyncDriver::class, + 'roadrunner' => Queue::class, ], 'registry' => [ diff --git a/app/config/tokenizer.php b/app/config/tokenizer.php new file mode 100644 index 0000000..6578683 --- /dev/null +++ b/app/config/tokenizer.php @@ -0,0 +1,11 @@ + [ + 'directory' => directory('runtime') . 'cache', + 'enabled' => true, + ], + 'debug' => false, +]; diff --git a/app/helpers/debug.php b/app/helpers/debug.php new file mode 100644 index 0000000..807c119 --- /dev/null +++ b/app/helpers/debug.php @@ -0,0 +1,48 @@ +addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); + + // Set new handler and store previous one + $prevent = VarDumper::setHandler(static fn ($value) => $dumper->dump($cloner->cloneVar($value))); + $result = VarDumper::dump($value); + + foreach ($values as $v) { + VarDumper::dump($v); + } + + // Reset handler + VarDumper::setHandler($prevent); + + if ($previous) { + $_SERVER['VAR_DUMPER_FORMAT'] = $previous; + } + + return $result; + } +} diff --git a/app/locale/ru/messages.en.php b/app/locale/ru/messages.en.php deleted file mode 100644 index 7c6466b..0000000 --- a/app/locale/ru/messages.en.php +++ /dev/null @@ -1,15 +0,0 @@ - 'GitHub', - 'Exception' => 'Страница ошибки', - 'Create Queue Task' => 'Создать фоновую задачу', - 'Application Metrics' => 'Prometheus метрики', - 'Website and Documentation' => 'Документация', - 'Welcome To Spiral' => 'Добро пожаловать', - 'Welcome to Spiral Framework' => 'Вас приветствует Spiral Framework', - 'This view file is located in' => 'Данный шаблон находится в файле', - 'and rendered by' => 'и вызван контроллером', -]; diff --git a/app/src/App.php b/app/src/App.php index 5caf319..1f27ca0 100644 --- a/app/src/App.php +++ b/app/src/App.php @@ -5,85 +5,127 @@ namespace App; use App\Bootloader; +use Spiral\Boot\Bootloader\CoreBootloader; use Spiral\Bootloader as Framework; -use Spiral\DotEnv\Bootloader as DotEnv; +use Spiral\Bootloader\Views\TranslatedCacheBootloader; +use Spiral\Cache\Bootloader\CacheBootloader; +use Spiral\Distribution\Bootloader\DistributionBootloader; +use Spiral\DotEnv\Bootloader\DotenvBootloader; +use Spiral\Events\Bootloader\EventsBootloader; use Spiral\Framework\Kernel; +use Spiral\Filters\Bootloader\FiltersBootloader; +use Spiral\League\Event\Bootloader\EventBootloader; use Spiral\Monolog\Bootloader as Monolog; -use Spiral\Nyholm\Bootloader as Nyholm; +use Spiral\Nyholm\Bootloader\NyholmBootloader; use Spiral\Prototype\Bootloader as Prototype; +use Spiral\Queue\Bootloader\QueueBootloader; use Spiral\Router\Bootloader as Router; -use Spiral\Scaffolder\Bootloader as Scaffolder; -use Spiral\Stempler\Bootloader as Stempler; +use Spiral\Scaffolder\Bootloader\ScaffolderBootloader; +use Spiral\Scheduler\Bootloader\SchedulerBootloader; +use Spiral\SendIt\Bootloader\MailerBootloader; use Spiral\Cycle\Bootloader as CycleBridge; use Spiral\RoadRunnerBridge\Bootloader as RoadRunnerBridge; +use Spiral\Stempler\Bootloader\StemplerBootloader; +use Spiral\Storage\Bootloader\StorageBootloader; +use Spiral\Tokenizer\Bootloader\TokenizerListenerBootloader; +use Spiral\Validation\Bootloader\ValidationBootloader; +use Spiral\Validator\Bootloader\ValidatorBootloader; +use Spiral\Views\Bootloader\ViewsBootloader; class App extends Kernel { + protected const SYSTEM = [ + CoreBootloader::class, + TokenizerListenerBootloader::class, + DotenvBootloader::class, + ]; + /* * List of components and extensions to be automatically registered * within system container on application start. */ protected const LOAD = [ - RoadRunnerBridge\CacheBootloader::class, - RoadRunnerBridge\GRPCBootloader::class, - RoadRunnerBridge\HttpBootloader::class, - RoadRunnerBridge\QueueBootloader::class, - RoadRunnerBridge\RoadRunnerBootloader::class, - - // Base extensions - DotEnv\DotenvBootloader::class, + // Logging and exceptions handling + Bootloader\LoggingBootloader::class, Monolog\MonologBootloader::class, + Bootloader\ExceptionHandlerBootloader::class, - // Application specific logs - Bootloader\LoggingBootloader::class, + // RoadRunner + RoadRunnerBridge\LoggerBootloader::class, + RoadRunnerBridge\QueueBootloader::class, + RoadRunnerBridge\HttpBootloader::class, + RoadRunnerBridge\CacheBootloader::class, + RoadRunnerBridge\GRPCBootloader::class, // Core Services Framework\SnapshotsBootloader::class, - Framework\I18nBootloader::class, // Security and validation Framework\Security\EncrypterBootloader::class, - Framework\Security\ValidationBootloader::class, Framework\Security\FiltersBootloader::class, Framework\Security\GuardBootloader::class, // HTTP extensions - Nyholm\NyholmBootloader::class, Framework\Http\RouterBootloader::class, - Bootloader\CorsBootloader::class, - Router\AnnotatedRoutesBootloader::class, - Framework\Http\ErrorHandlerBootloader::class, Framework\Http\JsonPayloadsBootloader::class, Framework\Http\CookiesBootloader::class, Framework\Http\SessionBootloader::class, Framework\Http\CsrfBootloader::class, Framework\Http\PaginationBootloader::class, + Framework\Http\ErrorHandlerBootloader::class, + + Bootloader\CorsBootloader::class, + Router\AnnotatedRoutesBootloader::class, // Databases CycleBridge\DatabaseBootloader::class, CycleBridge\MigrationsBootloader::class, - // CycleBridge\DisconnectsBootloader::class, // ORM CycleBridge\SchemaBootloader::class, CycleBridge\CycleOrmBootloader::class, CycleBridge\AnnotatedBootloader::class, - CycleBridge\CommandBootloader::class, + CycleBridge\ValidationBootloader::class, Bootloader\EntityBehaviorBootloader::class, + // Event Dispatcher + EventsBootloader::class, + EventBootloader::class, + + // Scheduler + SchedulerBootloader::class, + // Views and view translation - Framework\Views\ViewsBootloader::class, - Framework\Views\TranslatedCacheBootloader::class, + ViewsBootloader::class, + TranslatedCacheBootloader::class, + StemplerBootloader::class, + + // Queue + QueueBootloader::class, - // Additional dispatchers - // Framework\Jobs\JobsBootloader::class, + // Cache + CacheBootloader::class, - // Extensions and bridges - Stempler\StemplerBootloader::class, + // Mailer + MailerBootloader::class, - // Framework commands + // Storage + StorageBootloader::class, + DistributionBootloader::class, + + ValidationBootloader::class, + ValidatorBootloader::class, + + RoadRunnerBridge\MetricsBootloader::class, + + NyholmBootloader::class, + + // Console commands Framework\CommandBootloader::class, - Scaffolder\ScaffolderBootloader::class, + RoadRunnerBridge\CommandBootloader::class, + CycleBridge\CommandBootloader::class, + ScaffolderBootloader::class, + CycleBridge\ScaffolderBootloader::class, // Debug and debug extensions Framework\DebugBootloader::class, @@ -95,8 +137,7 @@ class App extends Kernel Auth\Jwt\TokensBootloader::class, Service\Pagination\PaginationBootloader::class, - - RoadRunnerBridge\CommandBootloader::class, + FiltersBootloader::class, ]; /* @@ -104,7 +145,7 @@ class App extends Kernel */ protected const APP = [ Auth\AuthBootloader::class, - Bootloader\RouteGroupsBootloader::class, + Bootloader\RoutesBootloader::class, Bootloader\UserBootloader::class, Bootloader\LocaleSelectorBootloader::class, Bootloader\CheckerBootloader::class, diff --git a/app/src/Bootloader/CheckerBootloader.php b/app/src/Bootloader/CheckerBootloader.php index feb1d05..cdffa2d 100644 --- a/app/src/Bootloader/CheckerBootloader.php +++ b/app/src/Bootloader/CheckerBootloader.php @@ -7,14 +7,14 @@ use App\Security\PasswordChecker; use App\Security\UniqueChecker; use Spiral\Boot\Bootloader\Bootloader; -use Spiral\Bootloader\Security\ValidationBootloader; +use Spiral\Validator\Bootloader\ValidatorBootloader; class CheckerBootloader extends Bootloader { /** - * @param \Spiral\Bootloader\Security\ValidationBootloader $validation + * @param \Spiral\Validator\Bootloader\ValidatorBootloader $validation */ - public function boot(ValidationBootloader $validation): void + public function boot(ValidatorBootloader $validation): void { $validation->addChecker('password', PasswordChecker::class); $validation->addChecker('unique', UniqueChecker::class); diff --git a/app/src/Bootloader/ExceptionHandlerBootloader.php b/app/src/Bootloader/ExceptionHandlerBootloader.php new file mode 100644 index 0000000..fd57586 --- /dev/null +++ b/app/src/Bootloader/ExceptionHandlerBootloader.php @@ -0,0 +1,58 @@ + EnvSuppressErrors::class, + RendererInterface::class => ViewRenderer::class, + ]; + + public function __construct(private readonly ExceptionHandler $handler) + { + } + + public function init(AbstractKernel $kernel): void + { + // Register the console renderer, that will be used when the application + // is running in the console. + $this->handler->addRenderer(new ConsoleRenderer()); + + $kernel->running(function (): void { + // Register the JSON renderer, that will be used when the application is + // running in the HTTP context and a JSON response is expected. + $this->handler->addRenderer(new JsonRenderer()); + }); + } + + public function boot(LoggerReporter $logger, FileReporter $files): void + { + // Register the logger reporter, that will be used to log the exceptions using + // the logger component. + $this->handler->addReporter($logger); + + // Register the file reporter. It allows you to save detailed information about an exception to a file + // known as snapshot. + $this->handler->addReporter($files); + } +} diff --git a/app/src/Bootloader/FirebaseBootloader.php b/app/src/Bootloader/FirebaseBootloader.php index 8a6625a..ee004a8 100644 --- a/app/src/Bootloader/FirebaseBootloader.php +++ b/app/src/Bootloader/FirebaseBootloader.php @@ -6,7 +6,6 @@ use App\Config\FirebaseConfig; use Kreait\Firebase\Factory; -use Kreait\Firebase\Storage; use Spiral\Boot\Bootloader\Bootloader; use Spiral\Core\Container; diff --git a/app/src/Bootloader/LoggingBootloader.php b/app/src/Bootloader/LoggingBootloader.php index 7791a66..2a46ffa 100644 --- a/app/src/Bootloader/LoggingBootloader.php +++ b/app/src/Bootloader/LoggingBootloader.php @@ -39,17 +39,23 @@ public function boot(MonologBootloader $monolog, EnvironmentInterface $env): voi private function configureCommonHandlers(MonologBootloader $monolog): void { // app level errors - $monolog->addHandler(self::DEFAULT_CHANNEL, $monolog->logRotate( - directory('runtime') . 'logs/error.log', - Logger::ERROR, - 25, - false - )); + $monolog->addHandler( + channel: self::DEFAULT_CHANNEL, + handler: $monolog->logRotate( + filename: directory('runtime') . 'logs/error.log', + level: Logger::ERROR, + maxFiles: 25, + bubble: false + ) + ); // http level errors - $monolog->addHandler(ErrorHandlerMiddleware::class, $monolog->logRotate( - directory('runtime') . 'logs/http.log' - )); + $monolog->addHandler( + channel: ErrorHandlerMiddleware::class, + handler: $monolog->logRotate( + filename: directory('runtime') . 'logs/http.log' + ) + ); } /** @@ -58,13 +64,19 @@ private function configureCommonHandlers(MonologBootloader $monolog): void private function configureDebugHandlers(MonologBootloader $monolog): void { // debug and info messages via global LoggerInterface - $monolog->addHandler(self::DEFAULT_CHANNEL, $monolog->logRotate( - directory('runtime') . 'logs/debug.log' - )); + $monolog->addHandler( + channel: self::DEFAULT_CHANNEL, + handler: $monolog->logRotate( + filename: directory('runtime') . 'logs/debug.log' + ) + ); // debug database queries - $monolog->addHandler(MySQLDriver::class, $monolog->logRotate( - directory('runtime') . 'logs/db.log' - )); + $monolog->addHandler( + channel: MySQLDriver::class, + handler: $monolog->logRotate( + filename: directory('runtime') . 'logs/db.log' + ) + ); } } diff --git a/app/src/Bootloader/MailerBootloader.php b/app/src/Bootloader/MailerBootloader.php index e8b8145..17e6c43 100644 --- a/app/src/Bootloader/MailerBootloader.php +++ b/app/src/Bootloader/MailerBootloader.php @@ -10,6 +10,8 @@ use Spiral\Boot\Bootloader\Bootloader; use Spiral\Core\Container; use Spiral\Views\ViewsInterface; +use Symfony\Component\Mailer\Transport; +use Symfony\Component\Mailer\Mailer as SymfonyMailer; class MailerBootloader extends Bootloader { @@ -35,7 +37,7 @@ public function __construct(MailConfig $config) public function boot(Container $container): void { $container->bind(MailerInterface::class, function (ViewsInterface $views): MailerInterface { - $mailer = new Mailer(new \Swift_Mailer($this->getTransport()), $views); + $mailer = new Mailer(new SymfonyMailer($this->getTransport()), $views); $mailer->setDefaultFromName($this->config->getSenderName()); $mailer->setDefaultFromAddress($this->config->getSenderAddress()); @@ -45,9 +47,9 @@ public function boot(Container $container): void } /** - * @return \Swift_Transport + * @return \Symfony\Component\Mailer\Transport\TransportInterface */ - private function getTransport(): \Swift_Transport + private function getTransport(): Transport\TransportInterface { switch ($this->config->getDriver()) { case MailConfig::DRIVER_SMTP: @@ -58,16 +60,18 @@ private function getTransport(): \Swift_Transport } /** - * @return \Swift_SmtpTransport + * @return \Symfony\Component\Mailer\Transport\TransportInterface */ - private function getSmtpTransport(): \Swift_SmtpTransport + private function getSmtpTransport(): Transport\TransportInterface { - $transport = new \Swift_SmtpTransport(); + $transport = new Transport\Smtp\EsmtpTransport( + host: $this->config->getSmtpHost(), + port: (int) $this->config->getSmtpPort(), + ); - return $transport->setHost($this->config->getSmtpHost()) - ->setPort((int) $this->config->getSmtpPort()) - ->setUsername($this->config->getSmtpUsername()) - ->setPassword($this->config->getSmtpPassword()) - ->setEncryption($this->config->getSmtpEncryption()); + $transport->setUsername($this->config->getSmtpUsername()); + $transport->setPassword($this->config->getSmtpPassword()); + + return $transport; } } diff --git a/app/src/Bootloader/RouteGroupsBootloader.php b/app/src/Bootloader/RouteGroupsBootloader.php deleted file mode 100644 index dcadfa2..0000000 --- a/app/src/Bootloader/RouteGroupsBootloader.php +++ /dev/null @@ -1,20 +0,0 @@ -getGroup('auth')->addMiddleware(AuthMiddleware::class); - } -} diff --git a/app/src/Bootloader/RoutesBootloader.php b/app/src/Bootloader/RoutesBootloader.php new file mode 100644 index 0000000..3e1ce56 --- /dev/null +++ b/app/src/Bootloader/RoutesBootloader.php @@ -0,0 +1,59 @@ + JsonErrorsRenderer::class, + ]; + + protected const DEPENDENCIES = [ + AnnotatedRoutesBootloader::class, + ]; + + protected function globalMiddleware(): array + { + return [ + // If you want to automatically detect the user's locale based on the + // "Accept-Language" header uncomment this middleware and add \Spiral\Bootloader\I18nBootloader + // to the Kernel + // LocaleSelector::class, + + ErrorHandlerMiddleware::class, + JsonPayloadMiddleware::class, + HttpCollector::class, + InitAuthMiddleware::class, + ValidationHandlerMiddleware::class, + ]; + } + + protected function middlewareGroups(): array + { + return [ + 'auth' => [ + AuthMiddleware::class, + ], + ]; + } + + protected function defineRoutes(RoutingConfigurator $routes): void + { + // + } +} diff --git a/app/src/Config/AppConfig.php b/app/src/Config/AppConfig.php index cca68b5..3756a6d 100644 --- a/app/src/Config/AppConfig.php +++ b/app/src/Config/AppConfig.php @@ -13,7 +13,7 @@ class AppConfig extends InjectableConfig /** * @internal For internal usage. Will be hydrated in the constructor. */ - protected $config = [ + protected array $config = [ 'url' => '', 'website_url' => '', 'web_app_url' => '', diff --git a/app/src/Config/CdnConfig.php b/app/src/Config/CdnConfig.php index ba5684e..b498e4b 100644 --- a/app/src/Config/CdnConfig.php +++ b/app/src/Config/CdnConfig.php @@ -13,7 +13,7 @@ class CdnConfig extends InjectableConfig /** * @internal For internal usage. Will be hydrated in the constructor. */ - protected $config = [ + protected array $config = [ 'host' => null, 'bucket' => null ]; diff --git a/app/src/Config/CorsConfig.php b/app/src/Config/CorsConfig.php index 89aabbd..06cdf9b 100644 --- a/app/src/Config/CorsConfig.php +++ b/app/src/Config/CorsConfig.php @@ -13,7 +13,7 @@ class CorsConfig extends InjectableConfig /** * @internal For internal usage. Will be hydrated in the constructor. */ - protected $config = [ + protected array $config = [ 'allowedOrigins' => [], 'allowedOriginsPatterns' => [], 'supportsCredentials' => false, diff --git a/app/src/Config/FirebaseConfig.php b/app/src/Config/FirebaseConfig.php index 8358e2f..24e6130 100644 --- a/app/src/Config/FirebaseConfig.php +++ b/app/src/Config/FirebaseConfig.php @@ -13,7 +13,7 @@ class FirebaseConfig extends InjectableConfig /** * @internal For internal usage. Will be hydrated in the constructor. */ - protected $config = [ + protected array $config = [ 'databaseUri' => null, 'storageBucket' => null, 'projectId' => null, diff --git a/app/src/Config/JwtConfig.php b/app/src/Config/JwtConfig.php index 5d2f3a9..6f40909 100644 --- a/app/src/Config/JwtConfig.php +++ b/app/src/Config/JwtConfig.php @@ -13,7 +13,7 @@ class JwtConfig extends InjectableConfig /** * @internal For internal usage. Will be hydrated in the constructor. */ - protected $config = [ + protected array $config = [ 'secret' => null, 'ttl' => null, 'refreshTtl' => null, diff --git a/app/src/Config/MailConfig.php b/app/src/Config/MailConfig.php index 96c763f..fc745b8 100644 --- a/app/src/Config/MailConfig.php +++ b/app/src/Config/MailConfig.php @@ -14,7 +14,7 @@ class MailConfig extends InjectableConfig /** * @internal For internal usage. Will be hydrated in the constructor. */ - protected $config = [ + protected array $config = [ 'sender' => [ 'name' => null, 'address' => null, diff --git a/app/src/Config/S3Config.php b/app/src/Config/S3Config.php index 259ac02..a4f507e 100644 --- a/app/src/Config/S3Config.php +++ b/app/src/Config/S3Config.php @@ -13,7 +13,7 @@ class S3Config extends InjectableConfig /** * @internal For internal usage. Will be hydrated in the constructor. */ - protected $config = [ + protected array $config = [ 'region' => null, 'endpoint' => null, 'key' => null, diff --git a/app/src/Controller/Auth/LoginController.php b/app/src/Controller/Auth/LoginController.php index 4443bd6..e072e56 100644 --- a/app/src/Controller/Auth/LoginController.php +++ b/app/src/Controller/Auth/LoginController.php @@ -34,12 +34,7 @@ public function __construct( ) { } - /** - * @Route(route="/auth/login", name="auth.login", methods="POST") - * - * @param \App\Request\LoginRequest $request - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/auth/login', name: 'auth.login', methods: 'POST')] public function login(LoginRequest $request): ResponseInterface { if (! $request->isValid()) { diff --git a/app/src/Controller/Auth/RegisterController.php b/app/src/Controller/Auth/RegisterController.php index 62af904..28c41cd 100644 --- a/app/src/Controller/Auth/RegisterController.php +++ b/app/src/Controller/Auth/RegisterController.php @@ -85,20 +85,9 @@ public function register(RegisterRequest $request): ResponseInterface return $this->responseTokensWithUser($accessToken, $refreshToken, $user); } - /** - * @Route(route="/auth/register/check/nick-name", name="auth.register.check.nickname", methods="POST") - * - * @param \App\Request\CheckNickNameRequest $request - * @return \Psr\Http\Message\ResponseInterface - */ - public function checkNickName(CheckNickNameRequest $request): ResponseInterface + #[Route(route: '/auth/register/check/nick-name', name: 'auth.register.check.nickname', methods: 'POST')] + public function checkNickName(CheckNickNameRequest $_): ResponseInterface { - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - return $this->response->json([ 'message' => 'Nick name are free to register' ]); diff --git a/app/src/Controller/Profile/ProfileController.php b/app/src/Controller/Profile/ProfileController.php index 64a7ef1..e0dbfad 100644 --- a/app/src/Controller/Profile/ProfileController.php +++ b/app/src/Controller/Profile/ProfileController.php @@ -14,8 +14,11 @@ use Psr\Http\Message\ResponseInterface; use Psr\Log\LoggerInterface; use Spiral\Auth\AuthScope; +use Spiral\Http\Request\InputManager; use Spiral\Http\ResponseWrapper; use Spiral\Router\Annotation\Route; +use Spiral\Validation\ValidationProviderInterface; +use Spiral\Validator\FilterDefinition; class ProfileController extends AuthAwareController { @@ -54,13 +57,15 @@ public function index(): ResponseInterface * @param \App\Request\CheckNickNameRequest $request * @return \Psr\Http\Message\ResponseInterface */ - public function checkNickName(CheckNickNameRequest $request): ResponseInterface + public function checkNickName(InputManager $input, ValidationProviderInterface $provider): ResponseInterface { - $request->setContext($this->user); + $validator = $provider->getValidation(FilterDefinition::class)->validate([ + 'nickName' => $input->data('nickName'), + ], CheckNickNameRequest::validationRules())->withContext($this->user); - if (! $request->isValid()) { + if (! $validator->isValid()) { return $this->response->json([ - 'errors' => $request->getErrors(), + 'errors' => $validator->getErrors(), ], 422); } diff --git a/app/src/Controller/Tags/ChargesController.php b/app/src/Controller/Tags/ChargesController.php index 33ada39..383dee8 100644 --- a/app/src/Controller/Tags/ChargesController.php +++ b/app/src/Controller/Tags/ChargesController.php @@ -39,9 +39,9 @@ public function __construct( } #[Route(route: '/tags//charges', name: 'tag.charges', methods: 'GET', group: 'auth')] - public function list(int $id, InputManager $input): ResponseInterface + public function list($id, InputManager $input): ResponseInterface { - $tag = $this->tagRepository->findByPKByUsersPK($id, $this->userRepository->getCommonUserIDs($this->user)); + $tag = $this->tagRepository->findByPKByUsersPK((int) $id, $this->userRepository->getCommonUserIDs($this->user)); if (! $tag instanceof Tag) { return $this->response->create(404); } @@ -56,9 +56,9 @@ public function list(int $id, InputManager $input): ResponseInterface } #[Route(route: '/tags//charges/total', name: 'tag.charges.total', methods: 'GET', group: 'auth')] - public function total(int $id, InputManager $input): ResponseInterface + public function total($id, InputManager $input): ResponseInterface { - $tag = $this->tagRepository->findByPKByUsersPK($id, $this->userRepository->getCommonUserIDs($this->user)); + $tag = $this->tagRepository->findByPKByUsersPK((int) $id, $this->userRepository->getCommonUserIDs($this->user)); if (! $tag instanceof Tag) { return $this->response->create(404); } @@ -79,9 +79,9 @@ public function total(int $id, InputManager $input): ResponseInterface } #[Route(route: '/tags//charges/graph', name: 'tag.charges.graph', methods: 'GET', group: 'auth')] - public function graph(int $id, InputManager $input, ChargeAmountGraph $graph): ResponseInterface + public function graph($id, InputManager $input, ChargeAmountGraph $graph): ResponseInterface { - $tag = $this->tagRepository->findByPKByUsersPK($id, $this->userRepository->getCommonUserIDs($this->user)); + $tag = $this->tagRepository->findByPKByUsersPK((int) $id, $this->userRepository->getCommonUserIDs($this->user)); if (! $tag instanceof Tag) { return $this->response->create(404); } diff --git a/app/src/Controller/Tags/CommonController.php b/app/src/Controller/Tags/CommonController.php index 5c1e5ab..9e9a5ef 100644 --- a/app/src/Controller/Tags/CommonController.php +++ b/app/src/Controller/Tags/CommonController.php @@ -37,9 +37,9 @@ public function list(): ResponseInterface } #[Route(route: '/tags/common/', name: 'tag.common.index', methods: 'GET', group: 'auth')] - public function index(int $id): ResponseInterface + public function index($id): ResponseInterface { - $tag = $this->tagRepository->findByPKByUsersPK($id, $this->userRepository->getCommonUserIDs($this->user)); + $tag = $this->tagRepository->findByPKByUsersPK((int) $id, $this->userRepository->getCommonUserIDs($this->user)); if (! $tag instanceof Tag) { return $this->response->create(404); diff --git a/app/src/Controller/Tags/TagsController.php b/app/src/Controller/Tags/TagsController.php index e8f9f66..dc640aa 100644 --- a/app/src/Controller/Tags/TagsController.php +++ b/app/src/Controller/Tags/TagsController.php @@ -62,16 +62,16 @@ public function create(CreateRequest $request): ResponseInterface } #[Route(route: '/tags/', name: 'tag.update', methods: 'PUT', group: 'auth')] - public function update(int $id, UpdateRequest $request): ResponseInterface + public function update($id, UpdateRequest $request): ResponseInterface { - $tag = $this->tagRepository->findByPKByUserPK($id, (int) $this->user->id); + $tag = $this->tagRepository->findByPKByUserPK((int) $id, (int) $this->user->id); if (! $tag instanceof Tag) { return $this->response->create(404); } $request->setValue([ - 'id' => $id, + 'id' => $tag->id, 'user_id' => $this->user->id ]); @@ -104,9 +104,9 @@ public function update(int $id, UpdateRequest $request): ResponseInterface } #[Route(route: '/tags/', name: 'tag.delete', methods: 'DELETE', group: 'auth')] - public function delete(int $id): ResponseInterface + public function delete($id): ResponseInterface { - $tag = $this->tagRepository->findByPKByUserPK($id, (int) $this->user->id); + $tag = $this->tagRepository->findByPKByUserPK((int) $id, (int) $this->user->id); if (! $tag instanceof Tag) { return $this->response->create(404); diff --git a/app/src/Controller/Wallets/ActiveController.php b/app/src/Controller/Wallets/ActiveController.php index 4fd067d..162227d 100644 --- a/app/src/Controller/Wallets/ActiveController.php +++ b/app/src/Controller/Wallets/ActiveController.php @@ -31,9 +31,9 @@ public function __construct( * @param int $id * @return \Psr\Http\Message\ResponseInterface */ - public function activate(int $id): ResponseInterface + public function activate($id): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); @@ -63,9 +63,9 @@ public function activate(int $id): ResponseInterface * @param int $id * @return \Psr\Http\Message\ResponseInterface */ - public function disable(int $id): ResponseInterface + public function disable($id): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); diff --git a/app/src/Controller/Wallets/ArchiveController.php b/app/src/Controller/Wallets/ArchiveController.php index dc84d93..d5d072c 100644 --- a/app/src/Controller/Wallets/ArchiveController.php +++ b/app/src/Controller/Wallets/ArchiveController.php @@ -31,9 +31,9 @@ public function __construct( * @param int $id * @return \Psr\Http\Message\ResponseInterface */ - public function archive(int $id): ResponseInterface + public function archive($id): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); @@ -63,9 +63,9 @@ public function archive(int $id): ResponseInterface * @param int $id * @return \Psr\Http\Message\ResponseInterface */ - public function unArchive(int $id): ResponseInterface + public function unArchive($id): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); diff --git a/app/src/Controller/Wallets/Charges/ChargesController.php b/app/src/Controller/Wallets/Charges/ChargesController.php index 88d72f7..4aaea9e 100644 --- a/app/src/Controller/Wallets/Charges/ChargesController.php +++ b/app/src/Controller/Wallets/Charges/ChargesController.php @@ -41,9 +41,9 @@ public function __construct( } #[Route(route: '/wallets//charges', name: 'wallet.charge.list', methods: 'GET', group: 'auth')] - public function list(int $walletId, InputManager $input): ResponseInterface + public function list($walletId, InputManager $input): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($walletId, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); @@ -58,9 +58,9 @@ public function list(int $walletId, InputManager $input): ResponseInterface } #[Route(route: '/wallets//charges/graph', name: 'wallet.charge.graph', methods: 'GET', group: 'auth')] - public function graph(int $walletId, InputManager $input, ChargeAmountGraph $graph) + public function graph($walletId, InputManager $input, ChargeAmountGraph $graph) { - $wallet = $this->walletRepository->findByPKByUserPK($walletId, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); @@ -75,9 +75,9 @@ public function graph(int $walletId, InputManager $input, ChargeAmountGraph $gra } #[Route(route: '/wallets//charges', name: 'wallet.charge.create', methods: 'POST', group: 'auth')] - public function create(int $walletId, CreateRequest $request): ResponseInterface + public function create($walletId, CreateRequest $request): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($walletId, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); @@ -125,15 +125,15 @@ public function create(int $walletId, CreateRequest $request): ResponseInterface } #[Route(route: '/wallets//charges/', name: 'wallet.charge.update', methods: 'PUT', group: 'auth')] - public function update(int $walletId, string $chargeId, CreateRequest $request): ResponseInterface + public function update($walletId, string $chargeId, CreateRequest $request): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($walletId, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); } - $charge = $this->chargeRepository->findByPKByWalletPK($chargeId, $walletId); + $charge = $this->chargeRepository->findByPKByWalletPK($chargeId, (int) $wallet->id); if (! $charge instanceof Charge) { return $this->response->create(404); @@ -181,15 +181,15 @@ public function update(int $walletId, string $chargeId, CreateRequest $request): } #[Route(route: '/wallets//charges/', name: 'wallet.charge.delete', methods: 'DELETE', group: 'auth')] - public function delete(int $walletId, string $chargeId): ResponseInterface + public function delete($walletId, string $chargeId): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($walletId, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); } - $charge = $this->chargeRepository->findByPKByWalletPK($chargeId, $walletId); + $charge = $this->chargeRepository->findByPKByWalletPK($chargeId, (int) $wallet->id); if (! $charge instanceof Charge) { return $this->response->create(404); diff --git a/app/src/Controller/Wallets/Charges/TagsController.php b/app/src/Controller/Wallets/Charges/TagsController.php index 940bef6..03455dd 100644 --- a/app/src/Controller/Wallets/Charges/TagsController.php +++ b/app/src/Controller/Wallets/Charges/TagsController.php @@ -36,14 +36,14 @@ public function __construct( } #[Route(route: '/wallets//tags//charges', name: 'wallet.tag.charge.list', methods: 'GET', group: 'auth')] - public function list(int $walletId, int $tagId, InputManager $input): ResponseInterface + public function list($walletId, $tagId, InputManager $input): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($walletId, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); } - $tag = $this->tagRepository->findByPKByUsersPK($tagId, $wallet->getUserIDs()); + $tag = $this->tagRepository->findByPKByUsersPK((int) $tagId, $wallet->getUserIDs()); if (! $tag instanceof Tag) { return $this->response->create(404); } @@ -57,14 +57,14 @@ public function list(int $walletId, int $tagId, InputManager $input): ResponseIn } #[Route(route: '/wallets//tags//charges/graph', name: 'wallet.tag.charge.graph', methods: 'GET', group: 'auth')] - public function graph(int $walletId, int $tagId, InputManager $input, ChargeAmountGraph $graph): ResponseInterface + public function graph($walletId, $tagId, InputManager $input, ChargeAmountGraph $graph): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($walletId, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); } - $tag = $this->tagRepository->findByPKByUsersPK($tagId, $this->userRepository->getCommonUserIDs($this->user)); + $tag = $this->tagRepository->findByPKByUsersPK((int) $tagId, $this->userRepository->getCommonUserIDs($this->user)); if (! $tag instanceof Tag) { return $this->response->create(404); } diff --git a/app/src/Controller/Wallets/IndexController.php b/app/src/Controller/Wallets/IndexController.php index 5a398d8..41804f5 100644 --- a/app/src/Controller/Wallets/IndexController.php +++ b/app/src/Controller/Wallets/IndexController.php @@ -30,9 +30,9 @@ public function __construct( } #[Route(route: '/wallets/', name: 'wallet.index', methods: 'GET', group: 'auth')] - public function index(int $id): ResponseInterface + public function index($id): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); @@ -42,9 +42,9 @@ public function index(int $id): ResponseInterface } #[Route(route: '/wallets//total', name: 'wallet.index.total', methods: 'GET', group: 'auth')] - public function indexTotal(int $id, InputManager $input): ResponseInterface + public function indexTotal($id, InputManager $input): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); @@ -52,14 +52,14 @@ public function indexTotal(int $id, InputManager $input): ResponseInterface $this->chargeRepository->filter($input->query->fetch(['date-from', 'date-to'])); - $income = $this->chargeRepository->totalByWalletPK($id, Charge::TYPE_INCOME); - $expense = $this->chargeRepository->totalByWalletPK($id, Charge::TYPE_EXPENSE); + $income = $this->chargeRepository->totalByWalletPK((int) $wallet->id, Charge::TYPE_INCOME); + $expense = $this->chargeRepository->totalByWalletPK((int) $wallet->id, Charge::TYPE_EXPENSE); return $this->response->json([ 'data' => [ 'totalAmount' => $this->chargeWalletService->totalByIncomeAndExpense($income, $expense), - 'totalIncomeAmount' => $this->chargeRepository->totalByWalletPK($id, Charge::TYPE_INCOME), - 'totalExpenseAmount' => $this->chargeRepository->totalByWalletPK($id, Charge::TYPE_EXPENSE), + 'totalIncomeAmount' => $income, + 'totalExpenseAmount' => $expense, ], ]); } diff --git a/app/src/Controller/Wallets/TagsController.php b/app/src/Controller/Wallets/TagsController.php index a90dcf5..64e915c 100644 --- a/app/src/Controller/Wallets/TagsController.php +++ b/app/src/Controller/Wallets/TagsController.php @@ -33,9 +33,9 @@ public function __construct( } #[Route(route: '/wallets//tags', name: 'wallet.tags.list', methods: 'GET', group: 'auth')] - public function list(int $id): ResponseInterface + public function list($id): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); @@ -47,15 +47,15 @@ public function list(int $id): ResponseInterface } #[Route(route: '/wallets//tags//total', name: 'wallet.tags.total', methods: 'GET', group: 'auth')] - public function total(int $walletId, int $tagId, InputManager $input): ResponseInterface + public function total($walletId, $tagId, InputManager $input): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($walletId, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); } - $tag = $this->tagRepository->findByPKByUsersPK($tagId, $wallet->getUserIDs()); + $tag = $this->tagRepository->findByPKByUsersPK((int) $tagId, $wallet->getUserIDs()); if (! $tag instanceof Tag) { return $this->response->create(404); @@ -63,8 +63,8 @@ public function total(int $walletId, int $tagId, InputManager $input): ResponseI $this->chargeRepository->filter($input->query->fetch(['date-from', 'date-to'])); - $income = $this->chargeRepository->totalByWalletPKAndTagId($walletId, $tagId, Charge::TYPE_INCOME); - $expense = $this->chargeRepository->totalByWalletPKAndTagId($walletId, $tagId, Charge::TYPE_EXPENSE); + $income = $this->chargeRepository->totalByWalletPKAndTagId((int) $wallet->id, (int) $tag->id, Charge::TYPE_INCOME); + $expense = $this->chargeRepository->totalByWalletPKAndTagId((int) $wallet->id, (int) $tag->id, Charge::TYPE_EXPENSE); return $this->response->json([ 'data' => [ @@ -76,9 +76,9 @@ public function total(int $walletId, int $tagId, InputManager $input): ResponseI } #[Route(route: '/wallets//tags/find/', name: 'wallet.tags.find', methods: 'GET', group: 'auth')] - public function find(int $walletId, string $query = ''): ResponseInterface + public function find($walletId, string $query = ''): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($walletId, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); diff --git a/app/src/Controller/Wallets/UsersController.php b/app/src/Controller/Wallets/UsersController.php index 2d086b3..052900e 100644 --- a/app/src/Controller/Wallets/UsersController.php +++ b/app/src/Controller/Wallets/UsersController.php @@ -36,9 +36,9 @@ public function __construct( * @param int $id * @return \Psr\Http\Message\ResponseInterface */ - public function users(int $id): ResponseInterface + public function users($id): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPKWithUsers($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPKWithUsers((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); @@ -54,16 +54,16 @@ public function users(int $id): ResponseInterface * @param int $userId * @return \Psr\Http\Message\ResponseInterface */ - public function patch(int $id, int $userId): ResponseInterface + public function patch($id, $userId): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPKWithUsers($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPKWithUsers((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); } /** @var \App\Database\User|null $user */ - $user = $this->userRepository->findByPK($userId); + $user = $this->userRepository->findByPK((int) $userId); if (! $user instanceof User) { return $this->response->create(404); @@ -96,23 +96,23 @@ public function patch(int $id, int $userId): ResponseInterface * @param int $userId * @return \Psr\Http\Message\ResponseInterface */ - public function delete(int $id, int $userId): ResponseInterface + public function delete($id, $userId): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPKWithUsers($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPKWithUsers((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); } /** @var \App\Database\User|null $user */ - $user = $this->userRepository->findByPK($userId); + $user = $this->userRepository->findByPK((int) $userId); if (! $user instanceof User) { return $this->response->create(404); } if ($wallet->users->count() === 1) { - if ($this->user->id === $userId) { + if ($this->user->id === (int) $userId) { return $this->response->json([ 'message' => 'Unable to revoke user from wallet. You are only one member. Delete wallet if you do not need them anymore.', 'error' => 'Current user is the one wallet owner.', diff --git a/app/src/Controller/Wallets/WalletsController.php b/app/src/Controller/Wallets/WalletsController.php index d36cc02..8912c92 100644 --- a/app/src/Controller/Wallets/WalletsController.php +++ b/app/src/Controller/Wallets/WalletsController.php @@ -54,9 +54,9 @@ public function create(CreateRequest $request): ResponseInterface } #[Route(route: '/wallets/', name: 'wallet.update', methods: 'PUT', group: 'auth')] - public function update(int $id, UpdateRequest $request): ResponseInterface + public function update($id, UpdateRequest $request): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); @@ -112,10 +112,10 @@ public function update(int $id, UpdateRequest $request): ResponseInterface return $this->walletView->json($wallet); } - #[Route(route: '/wallets/', name: 'wallet.delete', methods: 'DELETE', group: 'auth')] - public function delete(int $id): ResponseInterface + #[Route(route: '/wallets/', name: 'wallet.delete', methods: 'DELETE', group: 'auth')] + public function delete($id): ResponseInterface { - $wallet = $this->walletRepository->findByPKByUserPK($id, (int) $this->user->id); + $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); if (! $wallet instanceof Wallet) { return $this->response->create(404); diff --git a/app/src/Request/CheckNickNameRequest.php b/app/src/Request/CheckNickNameRequest.php index c997269..a535819 100644 --- a/app/src/Request/CheckNickNameRequest.php +++ b/app/src/Request/CheckNickNameRequest.php @@ -5,21 +5,32 @@ namespace App\Request; use App\Database\User; -use Spiral\Filters\Filter; +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; -class CheckNickNameRequest extends Filter +class CheckNickNameRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'nickName' => 'data:nickName', - ]; + #[Data] + public string $nickName; - protected const VALIDATES = [ - 'nickName' => [ - 'is_string', - 'type::notEmpty', - ['string::longer', 3], - ['string::regexp', '/^[a-zA-Z0-9_]*$/'], - ['entity::unique', User::class, 'nickName', 'error' => 'Nick name already claimed'], - ], - ]; + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition(validationRules: self::validationRules()); + } + + public static function validationRules(): array + { + return [ + 'nickName' => [ + 'is_string', + 'type::notEmpty', + ['string::longer', 3], + ['string::regexp', '/^[a-zA-Z0-9_]*$/'], + ['entity::unique', User::class, 'nickName', 'error' => 'Nick name already claimed'], + ], + ]; + } } diff --git a/app/src/Request/JsonErrorsRenderer.php b/app/src/Request/JsonErrorsRenderer.php new file mode 100644 index 0000000..b93f12c --- /dev/null +++ b/app/src/Request/JsonErrorsRenderer.php @@ -0,0 +1,22 @@ +wrapper->json([ + 'errors' => $errors, + ])->withStatus(422, 'The given data was invalid.'); + } +} diff --git a/app/src/Security/PasswordChecker.php b/app/src/Security/PasswordChecker.php index f76c598..1ee132f 100644 --- a/app/src/Security/PasswordChecker.php +++ b/app/src/Security/PasswordChecker.php @@ -5,7 +5,7 @@ namespace App\Security; use App\Service\Auth\AuthService; -use Spiral\Validation\AbstractChecker; +use Spiral\Validator\AbstractChecker; class PasswordChecker extends AbstractChecker { diff --git a/app/src/Security/UniqueChecker.php b/app/src/Security/UniqueChecker.php index bbad2a2..e450bda 100644 --- a/app/src/Security/UniqueChecker.php +++ b/app/src/Security/UniqueChecker.php @@ -5,7 +5,7 @@ namespace App\Security; use Cycle\ORM\ORMInterface; -use Spiral\Validation\AbstractChecker; +use Spiral\Validator\AbstractChecker; class UniqueChecker extends AbstractChecker { diff --git a/app/src/Service/Mailer/Mail.php b/app/src/Service/Mailer/Mail.php index 29700e2..51b4eaa 100644 --- a/app/src/Service/Mailer/Mail.php +++ b/app/src/Service/Mailer/Mail.php @@ -5,11 +5,13 @@ namespace App\Service\Mailer; use Spiral\Views\ViewsInterface; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; abstract class Mail { /** - * @var \Swift_Message + * @var \Symfony\Component\Mime\Email */ private $message; @@ -23,7 +25,7 @@ abstract class Mail */ public function __construct() { - $this->message = new \Swift_Message(); + $this->message = new Email(); } /** @@ -34,9 +36,9 @@ public function __construct() abstract public function build(): Mail; /** - * @return \Swift_Message + * @return \Symfony\Component\Mime\Email */ - public function getSwiftMessage(): \Swift_Message + public function getEmailMessage(): Email { return $this->message; } @@ -49,7 +51,7 @@ public function getSwiftMessage(): \Swift_Message */ public function subject(string $subject): Mail { - $this->message->setSubject($subject); + $this->message->subject($subject); return $this; } @@ -61,7 +63,7 @@ public function subject(string $subject): Mail */ public function from(string $address, string $fullName = null): Mail { - $this->message->addFrom($address, $fullName); + $this->message->from(new Address($address, $fullName)); return $this; } @@ -73,7 +75,7 @@ public function from(string $address, string $fullName = null): Mail */ public function to(string $address, string $fullName = null): Mail { - $this->message->addTo($address, $fullName); + $this->message->to(new Address($address, $fullName)); return $this; } @@ -85,7 +87,7 @@ public function to(string $address, string $fullName = null): Mail */ public function replyTo(string $address, string $fullName = null): Mail { - $this->message->addReplyTo($address, $fullName); + $this->message->replyTo(new Address($address, $fullName)); return $this; } @@ -97,7 +99,7 @@ public function replyTo(string $address, string $fullName = null): Mail */ public function cc(string $address, string $fullName = null): Mail { - $this->message->addCc($address, $fullName); + $this->message->cc(new Address($address, $fullName)); return $this; } @@ -109,7 +111,7 @@ public function cc(string $address, string $fullName = null): Mail */ public function bcc(string $address, string $fullName = null): Mail { - $this->message->addBcc($address, $fullName); + $this->message->bcc(new Address($address, $fullName)); return $this; } @@ -131,11 +133,7 @@ public function view(string $name): Mail */ public function render(ViewsInterface $views): Mail { - $this->message->setBody( - $views->render($this->viewName, $this->buildRenderData()), - 'text/html', - 'utf-8', - ); + $this->message->html($views->render($this->viewName, $this->buildRenderData())); return $this; } diff --git a/app/src/Service/Mailer/Mailer.php b/app/src/Service/Mailer/Mailer.php index 34286f0..992fb2b 100644 --- a/app/src/Service/Mailer/Mailer.php +++ b/app/src/Service/Mailer/Mailer.php @@ -5,11 +5,14 @@ namespace App\Service\Mailer; use Spiral\Views\ViewsInterface; +use Symfony\Component\Mailer\MailerInterface as SymfonyMailerInterface; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; class Mailer implements MailerInterface { /** - * @var \Swift_Mailer + * @var \Symfony\Component\Mailer\MailerInterface */ private $mailer; @@ -31,10 +34,10 @@ class Mailer implements MailerInterface /** * Mailer constructor. * - * @param \Swift_Mailer $mailer + * @param \Symfony\Component\Mailer\MailerInterface $mailer * @param \Spiral\Views\ViewsInterface $views */ - public function __construct(\Swift_Mailer $mailer, ViewsInterface $views) + public function __construct(SymfonyMailerInterface $mailer, ViewsInterface $views) { $this->mailer = $mailer; $this->views = $views; @@ -81,21 +84,21 @@ public function send(Mail $mail): void */ public function render(Mail $mail): string { - return $this->build($mail)->getBody(); + return $this->build($mail)->getHtmlBody(); } /** * Convert Mail instance to Swift_Message instance * * @param \App\Service\Mailer\Mail $mail - * @return \Swift_Message + * @return \Symfony\Component\Mime\Email */ - private function build(Mail $mail): \Swift_Message + private function build(Mail $mail): Email { - $message = $mail->build()->render($this->views)->getSwiftMessage(); + $message = $mail->build()->render($this->views)->getEmailMessage(); - if (!is_array($message->getFrom()) || count($message->getFrom()) == 0) { - $message = $message->addFrom($this->defaultFromAddress, $this->defaultFromName); + if (count($message->getFrom()) === 0) { + $message = $message->from(new Address($this->defaultFromAddress, $this->defaultFromName)); } return $message; diff --git a/composer.json b/composer.json index 8f79bfc..e780a11 100644 --- a/composer.json +++ b/composer.json @@ -1,93 +1,98 @@ { - "name": "cash-track/api", - "type": "project", - "description": "The core service to handle API requests from different clients", - "license": "MIT", - "support": { - "issues": "https://github.com/cash-track/api/issues", - "source": "https://github.com/cash-track/api" - }, - "authors": [ - { - "name": "Volodymyr Komarov", - "email": "vovikems@gmail.com" - } - ], - "require": { - "php": ">=8.1", - "ext-mbstring": "*", - "ext-openssl": "*", - "ext-pdo": "*", - "aws/aws-sdk-php": "^3.190", - "cycle/entity-behavior": "^1.1", - "cycle/entity-behavior-uuid": "^1.0", - "doctrine/collections": "^1.6", - "firebase/php-jwt": "^6.0", - "illuminate/collections": "^9.8", - "kreait/firebase-php": "^5.0", - "laminas/laminas-diactoros": "^2.6", - "lcobucci/jwt": "4.1.5", - "ramsey/uuid": "^4.2", - "spiral/cycle-bridge": "^v1.2", - "spiral/framework": "^2.11", - "spiral/nyholm-bridge": "^1.1", - "spiral/roadrunner-bridge": "^1.0", - "swiftmailer/swiftmailer": "^6.2", - "voku/portable-utf8": "^5.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "3.*", - "spiral/testing": "^1.1", - "vimeo/psalm": "^4.9" - }, - "scripts": { - "post-create-project-cmd": [ - "php -r \"copy('.env.sample', '.env');\"", - "php app.php encrypt:key -m .env", - "php app.php configure -vv", - "spiral get-binary" - ], - "checks": [ - "./vendor/bin/phpunit", - "./vendor/bin/phpcs -p -n --standard=PSR12 --colors --report=code ./app/src", - "./vendor/bin/psalm --php-version=8.0 --show-info=true --no-cache" - ], - "phpunit": [ - "./vendor/bin/phpunit" - ], - "phpcs": [ - "./vendor/bin/phpcs -p -n --standard=PSR12 --colors --report=code ./app/src" - ], - "psalm": [ - "./vendor/bin/psalm --php-version=8.1 --show-info=true --no-cache" - ] - }, - "autoload": { - "files": [ - "app/helpers/str.php" - ], - "psr-4": { - "App\\": "app/src/" - } - }, - "autoload-dev": { - "psr-4": { - "Tests\\": "tests/" - } - }, - "extra": { - "publish-cmd": "php app.php publish", - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "config": { - "sort-packages": true, - "allow-plugins": { - "spiral/composer-publish-plugin": true - } - }, - "minimum-stability": "dev", - "prefer-stable": true + "name": "cash-track/api", + "type": "project", + "license": "MIT", + "description": "The core service to handle API requests from different clients", + "homepage": "https://cash-track.app", + "support": { + "issues": "https://github.com/cash-track/api/issues", + "source": "https://github.com/cash-track/api" + }, + "require": { + "php": ">=8.1", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-pdo": "*", + "ext-sockets": "*", + "aws/aws-sdk-php": "^3.190", + "cycle/entity-behavior": "^1.1", + "cycle/entity-behavior-uuid": "^1.0", + "doctrine/collections": "^1.8", + "firebase/php-jwt": "^6.0", + "illuminate/collections": "^9.8", + "kreait/firebase-php": "^7.0", + "laminas/laminas-diactoros": "^2.6", + "lcobucci/jwt": "^4.1", + "ramsey/uuid": "^4.2", + "spiral-packages/league-event": "^1.0", + "spiral-packages/scheduler": "^2.1", + "spiral/cycle-bridge": "^2.1", + "spiral/filters-bridge": "^1.0", + "spiral/framework": "^3.6", + "spiral/nyholm-bridge": "^1.3", + "spiral/roadrunner-bridge": "^2.1", + "spiral/roadrunner-cli": "^2.4", + "spiral/stempler-bridge": "^3.2", + "spiral/validator": "^1.1", + "symfony/mailer": "^6.2", + "voku/portable-utf8": "^5.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "qossmic/deptrac-shim": "^1.0", + "squizlabs/php_codesniffer": "3.*", + "spiral/testing": "^2.2", + "symfony/var-dumper": "^6.1", + "vimeo/psalm": "^4.9" + }, + "scripts": { + "post-create-project-cmd": [ + "php -r \"copy('.env.sample', '.env');\"", + "php app.php encrypt:key -m .env", + "php app.php configure -vv", + "spiral get-binary" + ], + "checks": [ + "./vendor/bin/phpunit", + "./vendor/bin/phpcs -p -n --standard=PSR12 --colors --report=code ./app/src", + "./vendor/bin/psalm --php-version=8.0 --show-info=true --no-cache" + ], + "phpunit": [ + "./vendor/bin/phpunit" + ], + "phpcs": [ + "./vendor/bin/phpcs -p -n --standard=PSR12 --colors --report=code ./app/src" + ], + "psalm": [ + "./vendor/bin/psalm --php-version=8.1 --show-info=true --no-cache" + ] + }, + "autoload": { + "files": [ + "app/helpers/debug.php", + "app/helpers/str.php" + ], + "psr-4": { + "App\\": "app/src/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "extra": { + "publish-cmd": "php app.php publish", + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "spiral/composer-publish-plugin": true + } + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/composer.lock b/composer.lock index 4112fdd..d2786d4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f62a06529700411cd2eb2b2aacad29ad", + "content-hash": "bb48f6c4e1b92292a01d8079a5953ee4", "packages": [ { "name": "aws/aws-crt-php", @@ -150,6 +150,128 @@ }, "time": "2023-03-10T19:25:08+00:00" }, + { + "name": "beste/clock", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/beste/clock.git", + "reference": "7004b55fcd54737b539886244b3a3b2188181974" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beste/clock/zipball/7004b55fcd54737b539886244b3a3b2188181974", + "reference": "7004b55fcd54737b539886244b3a3b2188181974", + "shasum": "" + }, + "require": { + "php": "^8.0", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9.1", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^9.5.26", + "psalm/plugin-phpunit": "^0.16.1", + "vimeo/psalm": "^4.29" + }, + "type": "library", + "autoload": { + "files": [ + "src/Clock.php" + ], + "psr-4": { + "Beste\\Clock\\": "src/Clock" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jérôme Gamez", + "email": "jerome@gamez.name" + } + ], + "description": "A collection of Clock implementations", + "keywords": [ + "clock", + "clock-interface", + "psr-20", + "psr20" + ], + "support": { + "issues": "https://github.com/beste/clock/issues", + "source": "https://github.com/beste/clock/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/jeromegamez", + "type": "github" + } + ], + "time": "2022-11-26T18:03:05+00:00" + }, + { + "name": "beste/json", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/beste/json.git", + "reference": "28bb9e0adf03160a124ada37ab4c099598b2de84" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beste/json/zipball/28bb9e0adf03160a124ada37ab4c099598b2de84", + "reference": "28bb9e0adf03160a124ada37ab4c099598b2de84", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "~8.1.0 || ~8.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.26" + }, + "type": "library", + "autoload": { + "files": [ + "src/Json.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jérôme Gamez", + "email": "jerome@gamez.name" + } + ], + "description": "A simple JSON helper to decode and encode JSON", + "keywords": [ + "helper", + "json" + ], + "support": { + "issues": "https://github.com/beste/json/issues", + "source": "https://github.com/beste/json/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/jeromegamez", + "type": "github" + } + ], + "time": "2022-12-19T10:26:16+00:00" + }, { "name": "brick/math", "version": "0.10.2", @@ -206,6 +328,64 @@ ], "time": "2022-08-10T22:54:19+00:00" }, + { + "name": "butschster/cron-expression-generator", + "version": "v1.10.2", + "source": { + "type": "git", + "url": "https://github.com/butschster/CronExpressionGenerator.git", + "reference": "d47e3d36b7d67c58c6e46e6f14e0f00ed6a37ef4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/butschster/CronExpressionGenerator/zipball/d47e3d36b7d67c58c6e46e6f14e0f00ed6a37ef4", + "reference": "d47e3d36b7d67c58c6e46e6f14e0f00ed6a37ef4", + "shasum": "" + }, + "require": { + "dragonmantank/cron-expression": "^3.1", + "php": "^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpunit/phpunit": "^9.5", + "spatie/ray": "^1.28", + "vimeo/psalm": "^4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Butschster\\CronExpression\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pavel Buchnev", + "email": "butschster@gmail.com" + } + ], + "description": "Cron expression generator", + "keywords": [ + "cron", + "generator", + "php8" + ], + "support": { + "issues": "https://github.com/butschster/CronExpressionGenerator/issues", + "source": "https://github.com/butschster/CronExpressionGenerator/tree/v1.10.2" + }, + "funding": [ + { + "url": "https://github.com/butschster", + "type": "github" + } + ], + "time": "2021-08-09T13:39:19+00:00" + }, { "name": "cocur/slugify", "version": "v3.2", @@ -594,30 +774,30 @@ }, { "name": "cycle/migrations", - "version": "v3.1.1", + "version": "v4.0.1", "source": { "type": "git", "url": "https://github.com/cycle/migrations.git", - "reference": "ad0a80cdb0e5c56eb72e2d893e717846f743c7ba" + "reference": "943f42c7a0cbb311b0a3c027d5e944c74d0ac7de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cycle/migrations/zipball/ad0a80cdb0e5c56eb72e2d893e717846f743c7ba", - "reference": "ad0a80cdb0e5c56eb72e2d893e717846f743c7ba", + "url": "https://api.github.com/repos/cycle/migrations/zipball/943f42c7a0cbb311b0a3c027d5e944c74d0ac7de", + "reference": "943f42c7a0cbb311b0a3c027d5e944c74d0ac7de", "shasum": "" }, "require": { "cycle/database": "^2.3", - "php": ">=8.0", - "spiral/core": "^2.7", - "spiral/files": "^2.7", - "spiral/reactor": "^2.7", - "spiral/tokenizer": "^2.7" + "php": ">=8.1", + "spiral/core": "^3.0", + "spiral/files": "^3.0", + "spiral/reactor": "^3.0", + "spiral/tokenizer": "^3.0" }, "require-dev": { - "mockery/mockery": "^1.1", + "mockery/mockery": "^1.5", "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^4.29" + "vimeo/psalm": "dev-master" }, "type": "library", "autoload": { @@ -632,9 +812,9 @@ "description": "Database migrations, migration scaffolding", "support": { "issues": "https://github.com/cycle/migrations/issues", - "source": "https://github.com/cycle/migrations/tree/v3.1.1" + "source": "https://github.com/cycle/migrations/tree/v4.0.1" }, - "time": "2023-01-03T14:06:22+00:00" + "time": "2023-01-03T14:02:11+00:00" }, { "name": "cycle/orm", @@ -727,29 +907,30 @@ }, { "name": "cycle/schema-migrations-generator", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/cycle/schema-migrations-generator.git", - "reference": "683df818822a57b1ad74f6783334442bc90c9ec8" + "reference": "93190e85d98c2e7cf20de9d6d51c4878dc8084de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cycle/schema-migrations-generator/zipball/683df818822a57b1ad74f6783334442bc90c9ec8", - "reference": "683df818822a57b1ad74f6783334442bc90c9ec8", + "url": "https://api.github.com/repos/cycle/schema-migrations-generator/zipball/93190e85d98c2e7cf20de9d6d51c4878dc8084de", + "reference": "93190e85d98c2e7cf20de9d6d51c4878dc8084de", "shasum": "" }, "require": { "cycle/database": "^2.0", - "cycle/migrations": "^3.0", + "cycle/migrations": "^4.0", "cycle/schema-builder": "^2.0", - "php": ">=8.0" + "php": ">=8.1" }, "require-dev": { "cycle/annotated": "^3.0", "cycle/orm": "^2.0", - "phpunit/phpunit": "~8.0", - "spiral/debug": "^2.7" + "phpunit/phpunit": "^9.5", + "spiral/debug": "^3.0", + "spiral/framework": "^3.0" }, "type": "library", "autoload": { @@ -764,9 +945,9 @@ "description": "Cycle ORM Migration generation", "support": { "issues": "https://github.com/cycle/schema-migrations-generator/issues", - "source": "https://github.com/cycle/schema-migrations-generator/tree/2.0.0" + "source": "https://github.com/cycle/schema-migrations-generator/tree/2.1.0" }, - "time": "2021-12-22T16:49:23+00:00" + "time": "2023-01-12T10:29:49+00:00" }, { "name": "cycle/schema-renderer", @@ -1302,28 +1483,89 @@ ], "time": "2022-12-14T08:49:07+00:00" }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-09-10T18:51:20+00:00" + }, { "name": "egulias/email-validator", - "version": "3.2.5", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "b531a2311709443320c786feb4519cfaf94af796" + "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b531a2311709443320c786feb4519cfaf94af796", - "reference": "b531a2311709443320c786feb4519cfaf94af796", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/3a85486b709bc384dae8eb78fb2eec649bdb64ff", + "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff", "shasum": "" }, "require": { - "doctrine/lexer": "^1.2|^2", - "php": ">=7.2", - "symfony/polyfill-intl-idn": "^1.15" + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" }, "require-dev": { - "phpunit/phpunit": "^8.5.8|^9.3.3", - "vimeo/psalm": "^4" + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^4.30" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -1331,7 +1573,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { @@ -1359,7 +1601,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/3.2.5" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.1" }, "funding": [ { @@ -1367,7 +1609,7 @@ "type": "github" } ], - "time": "2023-01-02T17:26:14+00:00" + "time": "2023-01-14T14:17:03+00:00" }, { "name": "fig/http-message-util", @@ -1872,6 +2114,50 @@ ], "time": "2023-02-25T20:23:15+00:00" }, + { + "name": "grpc/grpc", + "version": "1.52.0", + "source": { + "type": "git", + "url": "https://github.com/grpc/grpc-php.git", + "reference": "98394cd601a587ca68294e6209bd713856969105" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grpc/grpc-php/zipball/98394cd601a587ca68294e6209bd713856969105", + "reference": "98394cd601a587ca68294e6209bd713856969105", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "google/auth": "^v1.3.0" + }, + "suggest": { + "ext-protobuf": "For better performance, install the protobuf C extension.", + "google/protobuf": "To get started using grpc quickly, install the native protobuf library." + }, + "type": "library", + "autoload": { + "psr-4": { + "Grpc\\": "src/lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "gRPC library for PHP", + "homepage": "https://grpc.io", + "keywords": [ + "rpc" + ], + "support": { + "source": "https://github.com/grpc/grpc-php/tree/v1.52.0" + }, + "time": "2023-02-25T05:20:08+00:00" + }, { "name": "guzzlehttp/guzzle", "version": "7.5.0", @@ -2398,108 +2684,58 @@ }, "time": "2022-08-09T13:29:29+00:00" }, - { - "name": "kreait/clock", - "version": "1.2", - "source": { - "type": "git", - "url": "https://github.com/kreait/clock-php.git", - "reference": "49e103382ca36cb2bc2e86ff3b8d11d44d0e0b31" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kreait/clock-php/zipball/49e103382ca36cb2bc2e86ff3b8d11d44d0e0b31", - "reference": "49e103382ca36cb2bc2e86ff3b8d11d44d0e0b31", - "shasum": "" - }, - "require": { - "php": "^7.0|^8.0", - "stella-maris/clock": "^0.1.4" - }, - "require-dev": { - "phpunit/phpunit": "^6.5.14" - }, - "type": "library", - "autoload": { - "files": [ - "src/Clock.php" - ], - "psr-4": { - "Kreait\\Clock\\": "src/Clock" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jérôme Gamez", - "email": "jerome@gamez.name" - } - ], - "description": "A PHP 7.0 compatible clock abstraction", - "support": { - "issues": "https://github.com/kreait/clock-php/issues", - "source": "https://github.com/kreait/clock-php/tree/1.2" - }, - "time": "2022-04-20T14:14:35+00:00" - }, { "name": "kreait/firebase-php", - "version": "5.26.4", + "version": "7.1.0", "source": { "type": "git", "url": "https://github.com/kreait/firebase-php.git", - "reference": "01c129ee628dc988b1da4b6cbaf1ee421d951aed" + "reference": "9139a6bfa1c6a810103d8d5eee817cefa2c7fcfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kreait/firebase-php/zipball/01c129ee628dc988b1da4b6cbaf1ee421d951aed", - "reference": "01c129ee628dc988b1da4b6cbaf1ee421d951aed", + "url": "https://api.github.com/repos/kreait/firebase-php/zipball/9139a6bfa1c6a810103d8d5eee817cefa2c7fcfa", + "reference": "9139a6bfa1c6a810103d8d5eee817cefa2c7fcfa", "shasum": "" }, "require": { + "beste/clock": "^3.0", + "beste/json": "^1.2.1", "ext-ctype": "*", "ext-json": "*", "ext-mbstring": "*", "ext-openssl": "*", - "google/auth": "^1.18", - "google/cloud-core": "^1.42.2", - "google/cloud-storage": "^1.24.1", - "guzzlehttp/guzzle": "^6.5.5|^7.3", - "guzzlehttp/promises": "^1.4", - "guzzlehttp/psr7": "^1.7|^2.0", - "kreait/clock": "^1.1", - "kreait/firebase-tokens": "^1.16.1", + "fig/http-message-util": "^1.1.5", + "firebase/php-jwt": "^6.3.2", + "google/auth": "^1.24", + "google/cloud-core": "^1.48.1", + "google/cloud-storage": "^1.30.1", + "guzzlehttp/guzzle": "^7.5", + "kreait/firebase-tokens": "^4.0", + "lcobucci/jwt": "^4.3.0|^5.0", "mtdowling/jmespath.php": "^2.6.1", - "php": "^7.4|^8.0", + "php": "~8.1.0|~8.2.0", "psr/cache": "^1.0.1|^2.0|^3.0", "psr/http-message": "^1.0.1", "psr/log": "^1.1|^2.0|^3.0", - "psr/simple-cache": "^1.0", - "riverline/multipart-parser": "^2.0.8", - "symfony/polyfill-php80": "^1.23", - "symfony/polyfill-php81": "^1.23" + "riverline/multipart-parser": "^2.0.9" }, "require-dev": { - "giggsey/libphonenumber-for-php": "^8.12.27", - "google/cloud-firestore": "^1.19.3", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.5.10", - "rector/rector": "^0.12.5", - "symfony/var-dumper": "^5.4|^6.0", - "symplify/easy-coding-standard": "^10.0" + "google/cloud-firestore": "^1.27.3", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9.17", + "phpstan/phpstan-phpunit": "^1.3.4", + "phpunit/phpunit": "^10.0.7", + "roave/backward-compatibility-check": "^8.3", + "symfony/var-dumper": "^6.2.5" }, "suggest": { - "giggsey/libphonenumber-for-php": "^8.9 to validate phone numbers before attempting to send them to Firebase", "google/cloud-firestore": "^1.0 to use the Firestore component" }, "type": "library", "extra": { "branch-alias": { + "dev-7.x": "7.x-dev", "dev-6.x": "6.x-dev", "dev-5.x": "5.x-dev", "dev-4.x": "4.x-dev" @@ -2530,7 +2766,6 @@ "sdk" ], "support": { - "chat": "https://discord.gg/Yacm7unBsr", "docs": "https://firebase-php.readthedocs.io", "issues": "https://github.com/kreait/firebase-php/issues", "source": "https://github.com/kreait/firebase-php" @@ -2541,55 +2776,50 @@ "type": "github" } ], - "time": "2023-01-24T00:00:03+00:00" + "time": "2023-02-28T23:07:09+00:00" }, { "name": "kreait/firebase-tokens", - "version": "1.17.0", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/kreait/firebase-tokens-php.git", - "reference": "cad73174508a20961bae72a4c4a621208be83ee4" + "reference": "07e44112129172f0407a54e351e39d47211be3c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kreait/firebase-tokens-php/zipball/cad73174508a20961bae72a4c4a621208be83ee4", - "reference": "cad73174508a20961bae72a4c4a621208be83ee4", + "url": "https://api.github.com/repos/kreait/firebase-tokens-php/zipball/07e44112129172f0407a54e351e39d47211be3c3", + "reference": "07e44112129172f0407a54e351e39d47211be3c3", "shasum": "" }, "require": { + "beste/clock": "^3.0", "ext-json": "*", "ext-openssl": "*", "fig/http-message-util": "^1.1.5", - "guzzlehttp/guzzle": "^6.3.1|^7.0", - "kreait/clock": "^1.1.0", - "lcobucci/jwt": "^4.0.4|^4.1.5", - "php": "^7.4|^8.0", - "psr/cache": "^1.0|^2.0|^3.0", - "psr/simple-cache": "^1.0.1" + "guzzlehttp/guzzle": "^7.5", + "lcobucci/clock": "^3.0", + "lcobucci/jwt": "^4.3.0|^5.0", + "php": "~8.1.0|~8.2.0", + "psr/cache": "^1.0|^2.0|^3.0" }, "require-dev": { - "firebase/php-jwt": "^5.5.1", - "friendsofphp/php-cs-fixer": "^3.10", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^0.12.99", - "phpstan/phpstan-phpunit": "^0.12.22", - "phpunit/phpunit": "^9.5.23", - "rector/rector": "^0.11.60", - "symfony/cache": "^5.4.11", - "symfony/var-dumper": "^5.4.11" + "friendsofphp/php-cs-fixer": "^3.13", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpunit/phpunit": "^9.5.26", + "rector/rector": "^0.12.23", + "roave/backward-compatibility-check": "^8.1", + "symfony/cache": "^5.4|^6.1.7", + "symfony/var-dumper": "^5.3|^6.1.6" }, "suggest": { - "firebase/php-jwt": "^5.0 can be used to create and parse tokens", - "guzzlehttp/guzzle": "^6.2.1|^7.0 can be used as an HTTP handler", - "lcobucci/jwt": "^3.2 can be used to create and parse tokens", - "psr/cache-implementation": "to cache fetched remote public keys", - "psr/simple-cache-implementation": "to cache fetched remote public keys" + "psr/cache-implementation": "to cache fetched remote public keys" }, "type": "library", "autoload": { "psr-4": { - "Firebase\\Auth\\Token\\": "src/Firebase/Auth/Token", "Kreait\\Firebase\\JWT\\": "src/JWT" } }, @@ -2614,7 +2844,7 @@ ], "support": { "issues": "https://github.com/kreait/firebase-tokens-php/issues", - "source": "https://github.com/kreait/firebase-tokens-php/tree/1.17.0" + "source": "https://github.com/kreait/firebase-tokens-php/tree/4.1.0" }, "funding": [ { @@ -2622,7 +2852,7 @@ "type": "github" } ], - "time": "2022-08-22T21:31:22+00:00" + "time": "2023-02-28T00:15:57+00:00" }, { "name": "laminas/laminas-diactoros", @@ -2723,21 +2953,21 @@ }, { "name": "lcobucci/clock", - "version": "2.3.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/lcobucci/clock.git", - "reference": "c7aadcd6fd97ed9e199114269c0be3f335e38876" + "reference": "039ef98c6b57b101d10bd11d8fdfda12cbd996dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/clock/zipball/c7aadcd6fd97ed9e199114269c0be3f335e38876", - "reference": "c7aadcd6fd97ed9e199114269c0be3f335e38876", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/039ef98c6b57b101d10bd11d8fdfda12cbd996dc", + "reference": "039ef98c6b57b101d10bd11d8fdfda12cbd996dc", "shasum": "" }, "require": { "php": "~8.1.0 || ~8.2.0", - "stella-maris/clock": "^0.1.7" + "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -2771,7 +3001,7 @@ "description": "Yet another clock abstraction", "support": { "issues": "https://github.com/lcobucci/clock/issues", - "source": "https://github.com/lcobucci/clock/tree/2.3.0" + "source": "https://github.com/lcobucci/clock/tree/3.0.0" }, "funding": [ { @@ -2783,20 +3013,20 @@ "type": "patreon" } ], - "time": "2022-12-19T14:38:11+00:00" + "time": "2022-12-19T15:00:24+00:00" }, { "name": "lcobucci/jwt", - "version": "4.1.5", + "version": "4.3.0", "source": { "type": "git", "url": "https://github.com/lcobucci/jwt.git", - "reference": "fe2d89f2eaa7087af4aa166c6f480ef04e000582" + "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/fe2d89f2eaa7087af4aa166c6f480ef04e000582", - "reference": "fe2d89f2eaa7087af4aa166c6f480ef04e000582", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/4d7de2fe0d51a96418c0d04004986e410e87f6b4", + "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4", "shasum": "" }, "require": { @@ -2805,19 +3035,19 @@ "ext-mbstring": "*", "ext-openssl": "*", "ext-sodium": "*", - "lcobucci/clock": "^2.0", + "lcobucci/clock": "^2.0 || ^3.0", "php": "^7.4 || ^8.0" }, "require-dev": { "infection/infection": "^0.21", "lcobucci/coding-standard": "^6.0", "mikey179/vfsstream": "^1.6.7", - "phpbench/phpbench": "^1.0", + "phpbench/phpbench": "^1.2", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-deprecation-rules": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpstan/phpstan-strict-rules": "^0.12", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", "phpunit/php-invoker": "^3.1", "phpunit/phpunit": "^9.5" }, @@ -2845,7 +3075,7 @@ ], "support": { "issues": "https://github.com/lcobucci/jwt/issues", - "source": "https://github.com/lcobucci/jwt/tree/4.1.5" + "source": "https://github.com/lcobucci/jwt/tree/4.3.0" }, "funding": [ { @@ -2857,7 +3087,66 @@ "type": "patreon" } ], - "time": "2021-09-28T19:34:56+00:00" + "time": "2023-01-02T13:28:00+00:00" + }, + { + "name": "league/event", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/event.git", + "reference": "221867a61087ee265ca07bd39aa757879afca820" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/event/zipball/221867a61087ee265ca07bd39aa757879afca820", + "reference": "221867a61087ee265ca07bd39aa757879afca820", + "shasum": "" + }, + "require": { + "php": ">=7.2.0", + "psr/event-dispatcher": "^1.0" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.45", + "phpunit/phpunit": "^8.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Event\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Event package", + "keywords": [ + "emitter", + "event", + "listener" + ], + "support": { + "issues": "https://github.com/thephpleague/event/issues", + "source": "https://github.com/thephpleague/event/tree/3.0.2" + }, + "time": "2022-10-29T09:31:25+00:00" }, { "name": "league/flysystem", @@ -3223,6 +3512,263 @@ ], "time": "2023-03-08T13:26:56+00:00" }, + { + "name": "nesbot/carbon", + "version": "2.66.0", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "496712849902241f04902033b0441b269effe001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/496712849902241f04902033b0441b269effe001", + "reference": "496712849902241f04902033b0441b269effe001", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.1.4", + "doctrine/orm": "^2.7", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "*", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.x-dev", + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2023-01-29T18:53:47+00:00" + }, + { + "name": "nette/php-generator", + "version": "v4.0.5", + "source": { + "type": "git", + "url": "https://github.com/nette/php-generator.git", + "reference": "a8d6abeae5d8c7202cd69600e086a7a72877fc86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/php-generator/zipball/a8d6abeae5d8c7202cd69600e086a7a72877fc86", + "reference": "a8d6abeae5d8c7202cd69600e086a7a72877fc86", + "shasum": "" + }, + "require": { + "nette/utils": "^3.2.7 || ^4.0", + "php": ">=8.0 <8.3" + }, + "require-dev": { + "nette/tester": "^2.4", + "nikic/php-parser": "^4.15", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.8" + }, + "suggest": { + "nikic/php-parser": "to use ClassType::from(withBodies: true) & ClassType::fromCode()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🐘 Nette PHP Generator: generates neat PHP code for you. Supports new PHP 8.2 features.", + "homepage": "https://nette.org", + "keywords": [ + "code", + "nette", + "php", + "scaffolding" + ], + "support": { + "issues": "https://github.com/nette/php-generator/issues", + "source": "https://github.com/nette/php-generator/tree/v4.0.5" + }, + "time": "2022-11-02T20:37:46+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/cacdbf5a91a657ede665c541eda28941d4b09c1e", + "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e", + "shasum": "" + }, + "require": { + "php": ">=8.0 <8.3" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.4", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()", + "ext-xml": "to use Strings::length() etc. when mbstring is not available" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.0" + }, + "time": "2023-02-02T10:41:53+00:00" + }, { "name": "nikic/php-parser", "version": "v4.15.4", @@ -4126,25 +4672,25 @@ }, { "name": "psr/simple-cache", - "version": "1.0.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -4159,7 +4705,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interfaces for simple caching", @@ -4171,9 +4717,9 @@ "simple-cache" ], "support": { - "source": "https://github.com/php-fig/simple-cache/tree/master" + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, - "time": "2017-10-23T01:57:42+00:00" + "time": "2021-10-29T13:26:27+00:00" }, { "name": "ralouphie/getallheaders", @@ -4508,46 +5054,516 @@ "type": "custom" }, { - "url": "https://github.com/rezigned", - "type": "github" + "url": "https://github.com/rezigned", + "type": "github" + }, + { + "url": "https://opencollective.com/rize-uri-template", + "type": "open_collective" + } + ], + "time": "2022-10-12T17:22:51+00:00" + }, + { + "name": "roadrunner-php/app-logger", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/roadrunner-php/app-logger.git", + "reference": "3844e1bf7e1d59f80fcf590be34cb3efdbb0c189" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/roadrunner-php/app-logger/zipball/3844e1bf7e1d59f80fcf590be34cb3efdbb0c189", + "reference": "3844e1bf7e1d59f80fcf590be34cb3efdbb0c189", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.1", + "spiral/goridge": "^3.1" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "phpunit/phpunit": "^8.0", + "vimeo/psalm": ">=4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "RoadRunner\\Logger\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kirill Astakhov (kastahov)", + "email": "kirill.astakhov@spiralscout.com" + }, + { + "name": "RoadRunner Community", + "homepage": "https://github.com/spiral/roadrunner/graphs/contributors" + } + ], + "description": "Send log messages to RoadRunner", + "support": { + "issues": "https://github.com/roadrunner-php/app-logger/issues", + "source": "https://github.com/roadrunner-php/app-logger/tree/1.0.0" + }, + "time": "2022-11-03T15:47:26+00:00" + }, + { + "name": "roadrunner-php/centrifugo", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/roadrunner-php/centrifugo.git", + "reference": "5b6e67ef4242b66d5f95032b507b1f341818a28c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/roadrunner-php/centrifugo/zipball/5b6e67ef4242b66d5f95032b507b1f341818a28c", + "reference": "5b6e67ef4242b66d5f95032b507b1f341818a28c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "google/protobuf": "^3.7", + "php": ">=8.1", + "spiral/goridge": "^3.1", + "spiral/roadrunner-worker": "^2.2" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "phpunit/phpunit": "^9.5", + "spiral/roadrunner-cli": "^2.3", + "vimeo/psalm": "^4.29" + }, + "type": "library", + "autoload": { + "psr-4": { + "GPBMetadata\\": "generated/GPBMetadata", + "RoadRunner\\Centrifugo\\": [ + "src", + "generated/RoadRunner/Centrifugo" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anton Titov / Wolfy-J", + "email": "wolfy.jd@gmail.com" + } + ], + "description": "RoadRunner: Centrifugo bridge", + "support": { + "issues": "https://github.com/roadrunner-php/centrifugo/issues", + "source": "https://github.com/roadrunner-php/centrifugo/tree/1.0.0" + }, + "time": "2022-11-11T18:30:55+00:00" + }, + { + "name": "spiral-packages/league-event", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/spiral-packages/league-event.git", + "reference": "ef6e87e2e5a2d12ecfc92e99a6e6f0aec72f7aaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spiral-packages/league-event/zipball/ef6e87e2e5a2d12ecfc92e99a6e6f0aec72f7aaf", + "reference": "ef6e87e2e5a2d12ecfc92e99a6e6f0aec72f7aaf", + "shasum": "" + }, + "require": { + "league/event": "^3.0", + "php": "^8.1", + "spiral/events": "^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "phpunit/phpunit": "^9.5", + "roave/security-advisories": "dev-latest", + "spiral/testing": "^2.0", + "vimeo/psalm": "^4.22" + }, + "type": "library", + "extra": { + "spiral": { + "bootloaders": [ + "Spiral\\League\\Event\\Bootloader\\EventBootloader" + ] + } + }, + "autoload": { + "psr-4": { + "Spiral\\League\\Event\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The League Event bridge for Spiral Framework", + "homepage": "https://github.com/spiral-packages/symfony-event-dispatcher", + "keywords": [ + "event-dispatcher", + "spiral", + "spiral-packages" + ], + "support": { + "issues": "https://github.com/spiral-packages/league-event/issues", + "source": "https://github.com/spiral-packages/league-event/tree/1.0.1" + }, + "time": "2022-09-14T08:02:26+00:00" + }, + { + "name": "spiral-packages/scheduler", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/spiral-packages/scheduler.git", + "reference": "c9da23e1b4c799db24dc1a5b010f69d6b7a4d597" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spiral-packages/scheduler/zipball/c9da23e1b4c799db24dc1a5b010f69d6b7a4d597", + "reference": "c9da23e1b4c799db24dc1a5b010f69d6b7a4d597", + "shasum": "" + }, + "require": { + "butschster/cron-expression-generator": "^1.10", + "nesbot/carbon": "^2.52", + "php": "^8.1", + "psr/event-dispatcher": "^1", + "spiral/attributes": "^2.8 || ^3.0", + "spiral/cache": "^3.0", + "spiral/queue": "^3.0", + "spiral/snapshots": "^3.0", + "symfony/process": "^6.0" + }, + "require-dev": { + "spiral/framework": "^3.0", + "spiral/testing": "^2.0", + "vimeo/psalm": "^4.9" + }, + "type": "library", + "extra": { + "spiral": { + "bootloaders": [ + "Spiral\\Scheduler\\Bootloader\\SchedulerBootloader" + ] + } + }, + "autoload": { + "psr-4": { + "Spiral\\Scheduler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "butschster", + "email": "butschster@gmail.com", + "role": "PHP web developer" + } + ], + "description": "The scheduler is a package for spiral framework. It will help to managing scheduled tasks on your server.", + "homepage": "https://github.com/spiral-packages/scheduler", + "keywords": [ + "scheduler", + "spiral", + "spiral-packages" + ], + "support": { + "issues": "https://github.com/spiral-packages/scheduler/issues", + "source": "https://github.com/spiral-packages/scheduler/tree/2.1.0" + }, + "time": "2022-07-20T09:48:15+00:00" + }, + { + "name": "spiral/attributes", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/spiral/attributes.git", + "reference": "442712c83c703366856d7da7370a95b471f3b51e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spiral/attributes/zipball/442712c83c703366856d7da7370a95b471f3b51e", + "reference": "442712c83c703366856d7da7370a95b471f3b51e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": ">=1.0", + "psr/simple-cache": "1 - 3" + }, + "require-dev": { + "doctrine/annotations": "^1.12", + "jetbrains/phpstorm-attributes": "^1.0", + "phpunit/phpunit": "^9.5.20", + "symfony/var-dumper": "^5.2 || ^6.0", + "vimeo/psalm": "^4.21" + }, + "suggest": { + "doctrine/annotations": "^1.0 for Doctrine metadata driver support" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.14.x-dev" + } + }, + "autoload": { + "files": [ + "src/polyfill.php" + ], + "psr-4": { + "Spiral\\Attributes\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kirill Nesmeyanov (SerafimArts)", + "email": "kirill.nesmeyanov@spiralscout.com" + } + ], + "description": "PHP attributes reader", + "homepage": "https://spiral.dev", + "support": { + "issues": "https://github.com/spiral/attributes/issues", + "source": "https://github.com/spiral/attributes" + }, + "time": "2022-05-26T15:05:35+00:00" + }, + { + "name": "spiral/composer-publish-plugin", + "version": "v1.1.2", + "source": { + "type": "git", + "url": "https://github.com/spiral/composer-publish-plugin.git", + "reference": "8d25c228389fcc0d4315a83913b8a5eb26c4e45b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spiral/composer-publish-plugin/zipball/8d25c228389fcc0d4315a83913b8a5eb26c4e45b", + "reference": "8d25c228389fcc0d4315a83913b8a5eb26c4e45b", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1|^2.0", + "php": ">=7.1" + }, + "require-dev": { + "composer/composer": "^1.7", + "phpunit/phpunit": "~7.0", + "spiral/code-style": "^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Spiral\\Composer\\PublishPlugin" + }, + "autoload": { + "psr-4": { + "Spiral\\Composer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Wolfy-J", + "email": "wolfy.jd@gmail.com" + } + ], + "support": { + "issues": "https://github.com/spiral/composer-publish-plugin/issues", + "source": "https://github.com/spiral/composer-publish-plugin/tree/v1.1.2" + }, + "time": "2020-11-12T23:10:18+00:00" + }, + { + "name": "spiral/cycle-bridge", + "version": "v2.4.1", + "source": { + "type": "git", + "url": "https://github.com/spiral/cycle-bridge.git", + "reference": "89672007b58034033cb3dc360401013c39e5f746" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spiral/cycle-bridge/zipball/89672007b58034033cb3dc360401013c39e5f746", + "reference": "89672007b58034033cb3dc360401013c39e5f746", + "shasum": "" + }, + "require": { + "cycle/annotated": "^3.1", + "cycle/migrations": "^4.0.1", + "cycle/orm": "^2.0.2", + "cycle/schema-migrations-generator": "^2.1", + "cycle/schema-renderer": "^1.2", + "doctrine/inflector": "^1.4 || ^2.0", + "php": ">=8.1", + "psr/container": "^1.1 || ^2.0", + "spiral/attributes": "^2.10 || ^3.0", + "spiral/data-grid-bridge": "^3.0", + "spiral/framework": "^3.3", + "spiral/prototype": "^3.0", + "spiral/reactor": "^3.0", + "spiral/scaffolder": "^3.0" + }, + "require-dev": { + "doctrine/collections": "^1.6", + "illuminate/collections": "^9.0", + "infection/infection": "^0.26.6", + "mockery/mockery": "^1.5", + "phpunit/phpunit": "^9.5.20", + "spiral/testing": "^2.0", + "spiral/validator": "^1.2", + "vimeo/psalm": "^4.27" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spiral\\Cycle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anton Titov (wolfy-j)", + "email": "wolfy-j@spiralscout.com" + }, + { + "name": "Pavel Buchnev (butschster)", + "email": "pavel.buchnev@spiralscout.com" + } + ], + "description": "Cycle ORM integration package", + "homepage": "https://spiral.dev", + "support": { + "issues": "https://github.com/spiral/framework/issues", + "source": "https://github.com/spiral/cycle-bridge" + }, + "time": "2023-03-11T10:20:58+00:00" + }, + { + "name": "spiral/data-grid", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/spiral/data-grid.git", + "reference": "dde45cec1a42802f84da191df6a67b11f7f30e3f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spiral/data-grid/zipball/dde45cec1a42802f84da191df6a67b11f7f30e3f", + "reference": "dde45cec1a42802f84da191df6a67b11f7f30e3f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "spiral/attributes": "^2.10 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.20", + "ramsey/uuid": "^4.2.3", + "vimeo/psalm": "^4.27" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-3.0": "3.0.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spiral\\DataGrid\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Valentin Vintsukevich (vvval)", + "email": "valentin@spiralscout.com" }, { - "url": "https://opencollective.com/rize-uri-template", - "type": "open_collective" + "name": "Anton Titov (wolfy-j)", + "email": "wolfy-j@spiralscout.com" } ], - "time": "2022-10-12T17:22:51+00:00" + "description": "Data Grid specification builder", + "homepage": "https://spiral.dev", + "support": { + "issues": "https://github.com/spiral/framework/issues", + "source": "https://github.com/spiral/data-grid" + }, + "time": "2022-09-14T18:34:05+00:00" }, { - "name": "spiral/composer-publish-plugin", - "version": "v1.1.2", + "name": "spiral/data-grid-bridge", + "version": "v3.0.1", "source": { "type": "git", - "url": "https://github.com/spiral/composer-publish-plugin.git", - "reference": "8d25c228389fcc0d4315a83913b8a5eb26c4e45b" + "url": "https://github.com/spiral/data-grid-bridge.git", + "reference": "29e8b7c9faf486d57bad8a3d14d53c2a7ccdf584" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spiral/composer-publish-plugin/zipball/8d25c228389fcc0d4315a83913b8a5eb26c4e45b", - "reference": "8d25c228389fcc0d4315a83913b8a5eb26c4e45b", + "url": "https://api.github.com/repos/spiral/data-grid-bridge/zipball/29e8b7c9faf486d57bad8a3d14d53c2a7ccdf584", + "reference": "29e8b7c9faf486d57bad8a3d14d53c2a7ccdf584", "shasum": "" }, "require": { - "composer-plugin-api": "^1.1|^2.0", - "php": ">=7.1" + "ext-json": "*", + "php": ">=8.1", + "spiral/attributes": "^2.10 || ^3.0", + "spiral/boot": "^3.0", + "spiral/data-grid": "^3.0", + "spiral/http": "^3.0" }, "require-dev": { - "composer/composer": "^1.7", - "phpunit/phpunit": "~7.0", - "spiral/code-style": "^1.0" + "phpunit/phpunit": "^9.5.20", + "spiral/hmvc": "^3.0", + "vimeo/psalm": "^4.27" }, - "type": "composer-plugin", + "type": "library", "extra": { - "class": "Spiral\\Composer\\PublishPlugin" + "branch-alias": { + "dev-3.0": "3.0.x-dev" + } }, "autoload": { "psr-4": { - "Spiral\\Composer\\": "src/" + "Spiral\\DataGrid\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -4556,61 +5572,55 @@ ], "authors": [ { - "name": "Wolfy-J", - "email": "wolfy.jd@gmail.com" + "name": "Anton Titov (wolfy-j)", + "email": "wolfy-j@spiralscout.com" } ], + "description": "Data Grid specification builder adapter for Spiral Framework", + "homepage": "https://spiral.dev", "support": { - "issues": "https://github.com/spiral/composer-publish-plugin/issues", - "source": "https://github.com/spiral/composer-publish-plugin/tree/v1.1.2" + "issues": "https://github.com/spiral/framework/issues", + "source": "https://github.com/spiral/data-grid-bridge" }, - "time": "2020-11-12T23:10:18+00:00" + "time": "2022-09-14T18:48:30+00:00" }, { - "name": "spiral/cycle-bridge", - "version": "v1.3.0", + "name": "spiral/filters-bridge", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/spiral/cycle-bridge.git", - "reference": "dfa3d8a9045cf38eafde87636b6db3469b2179d5" + "url": "https://github.com/spiral/filters-bridge.git", + "reference": "cea42b3c13cf8183b8f6053b5ee30e835836a257" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spiral/cycle-bridge/zipball/dfa3d8a9045cf38eafde87636b6db3469b2179d5", - "reference": "dfa3d8a9045cf38eafde87636b6db3469b2179d5", + "url": "https://api.github.com/repos/spiral/filters-bridge/zipball/cea42b3c13cf8183b8f6053b5ee30e835836a257", + "reference": "cea42b3c13cf8183b8f6053b5ee30e835836a257", "shasum": "" }, "require": { - "cycle/annotated": "^3.1", - "cycle/migrations": "^3.0", - "cycle/orm": "^2.0.2", - "cycle/schema-migrations-generator": "^2.0", - "cycle/schema-renderer": "^1.1", - "doctrine/inflector": "^1.4|^2.0", - "php": ">=8.0", - "spiral/attributes": "^2.10", - "spiral/framework": "^2.10", - "spiral/prototype": "^2.10", - "spiral/reactor": "^2.10", - "spiral/scaffolder": "^2.10" + "php": ">=8.1", + "spiral/boot": "^3.0", + "spiral/console": "^3.0", + "spiral/filters": "^3.0", + "spiral/scaffolder": "^3.0", + "spiral/translator": "^3.0", + "spiral/validator": "^1.0" }, "require-dev": { - "doctrine/collections": "^1.6", - "illuminate/collections": "^9.0", - "infection/infection": "^0.26.6", "mockery/mockery": "^1.5", - "phpunit/phpunit": "^9.5", - "spiral/roadrunner": "^2.0", - "spiral/testing": "^1.1.1", + "phpunit/phpunit": "^9.5.20", "vimeo/psalm": "^4.9" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, "autoload": { - "files": [ - "src/polyfill.php" - ], "psr-4": { - "Spiral\\Cycle\\": "src" + "Spiral\\Filters\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -4627,64 +5637,61 @@ "email": "pavel.buchnev@spiralscout.com" } ], - "description": "Cycle ORM integration package", + "description": "Deep Structural Validation, Data Mapper, Inheritance", "homepage": "https://spiral.dev", "support": { "issues": "https://github.com/spiral/framework/issues", - "source": "https://github.com/spiral/cycle-bridge" + "source": "https://github.com/spiral/filters" }, - "time": "2023-02-07T08:40:51+00:00" + "time": "2022-09-14T18:13:19+00:00" }, { "name": "spiral/framework", - "version": "2.14.1", + "version": "3.6.1", "source": { "type": "git", "url": "https://github.com/spiral/framework.git", - "reference": "1e3447f7c41ccbb31aacbc15f783756eb2630a92" + "reference": "2b2b046322c7ad04ec3e459f2ae2ba954376fbed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spiral/framework/zipball/1e3447f7c41ccbb31aacbc15f783756eb2630a92", - "reference": "1e3447f7c41ccbb31aacbc15f783756eb2630a92", + "url": "https://api.github.com/repos/spiral/framework/zipball/2b2b046322c7ad04ec3e459f2ae2ba954376fbed", + "reference": "2b2b046322c7ad04ec3e459f2ae2ba954376fbed", "shasum": "" }, "require": { "cocur/slugify": "^3.2", "codedungeon/php-cli-colors": "^1.11", - "cycle/database": "^1.0.3|^2.0", "defuse/php-encryption": "^2.2", "doctrine/annotations": "^1.12", "doctrine/inflector": "^1.4|^2.0", "ext-json": "*", "ext-mbstring": "*", - "laminas/laminas-diactoros": "^2.8", - "league/flysystem": "^2.0", + "ext-tokenizer": "*", + "league/flysystem": "^2.3.1", "monolog/monolog": "^2.2", "myclabs/deep-copy": "^1.9", - "nikic/php-parser": "^4.1", - "opis/closure": "^3.6", - "php": ">=7.4", - "psr/cache": ">=1.0", + "nette/php-generator": "^4.0.1", + "nikic/php-parser": "^4.13.2", + "php": ">=8.1", "psr/container": "^1.1|^2.0", + "psr/event-dispatcher": "^1.0", "psr/http-factory": "^1.0", + "psr/http-factory-implementation": "^1.0", "psr/http-message": "^1.0", "psr/http-server-middleware": "^1.0", "psr/log": "1 - 3", - "psr/simple-cache": "1 - 2", + "psr/simple-cache": "2 - 3", + "spiral/attributes": "^2.8|^3.0", "spiral/composer-publish-plugin": "^1.0", - "symfony/console": "^5.3|^6.0", - "symfony/finder": "^5.1|^6.0", + "symfony/console": "^6.1", + "symfony/finder": "^5.3.7|^6.0", "symfony/mailer": "^5.1|^6.0", - "symfony/polyfill-php73": "^1.22", - "symfony/polyfill-php80": "^1.22", "symfony/translation": "^5.1|^6.0", "vlucas/phpdotenv": "^5.4" }, "replace": { "spiral/annotated-routes": "self.version", - "spiral/annotations": "self.version", - "spiral/attributes": "self.version", "spiral/auth": "self.version", "spiral/auth-http": "self.version", "spiral/boot": "self.version", @@ -4695,13 +5702,11 @@ "spiral/cookies": "self.version", "spiral/core": "self.version", "spiral/csrf": "self.version", - "spiral/data-grid": "self.version", - "spiral/data-grid-bridge": "self.version", "spiral/debug": "self.version", "spiral/distribution": "self.version", "spiral/dotenv-bridge": "self.version", - "spiral/dumper": "self.version", "spiral/encrypter": "self.version", + "spiral/events": "self.version", "spiral/exceptions": "self.version", "spiral/files": "self.version", "spiral/filters": "self.version", @@ -4719,12 +5724,14 @@ "spiral/scaffolder": "self.version", "spiral/security": "self.version", "spiral/sendit": "self.version", + "spiral/serializer": "self.version", "spiral/session": "self.version", "spiral/snapshots": "self.version", "spiral/stempler": "self.version", "spiral/stempler-bridge": "self.version", "spiral/storage": "self.version", "spiral/streams": "self.version", + "spiral/telemetry": "self.version", "spiral/tokenizer": "self.version", "spiral/translator": "self.version", "spiral/validation": "self.version", @@ -4732,50 +5739,33 @@ }, "require-dev": { "aws/aws-sdk-php": "^3.0", - "cycle/annotated": "^2.0.6", - "cycle/migrations": "^1.0.1", - "cycle/orm": "^1.8.1", - "cycle/proxy-factory": "^1.2", - "cycle/schema-builder": "^1.1", "guzzlehttp/psr7": "^1.7", "jetbrains/phpstorm-attributes": "^1.0", - "laminas/laminas-hydrator": "^3.0|^4.0", "league/flysystem-async-aws-s3": "^2.0", "league/flysystem-aws-s3-v3": "^2.0", "mikey179/vfsstream": "^1.6", "mockery/mockery": "^1.5", - "nyholm/psr7": "^1.5.0", - "phpunit/phpunit": "^8.5|^9.5", - "ramsey/uuid": "^4.2", + "phpunit/phpunit": "^9.5.20", + "ramsey/collection": "^1.2", + "ramsey/uuid": "^4.2.3", "rector/rector": "0.12.15", - "spiral/broadcast": "^2.0", - "spiral/broadcast-ws": "^1.0", - "spiral/code-style": "^1.0", - "spiral/jobs": "^2.2", - "spiral/migrations": "^2.3", - "spiral/php-grpc": "^1.4", - "spiral/roadrunner": "^1.9.2", - "symfony/var-dumper": "^5.2|^6.0", - "symplify/monorepo-builder": "^10.0", - "vimeo/psalm": "^4.21" + "spiral/code-style": "^1.1", + "spiral/nyholm-bridge": "^1.2", + "spiral/testing": "^2.2", + "spiral/validator": "^1.2", + "symplify/monorepo-builder": "^10.2.7", + "vimeo/psalm": "^4.27" }, - "bin": [ - "bin/spiral" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.14.x-dev" + "dev-master": "3.7.x-dev" } }, "autoload": { "files": [ - "src/Attributes/src/polyfill.php", "src/Boot/src/helpers.php", - "src/DataGrid/src/helpers.php", - "src/Dumper/src/helpers.php", "src/Framework/helpers.php", - "src/Framework/polyfill.php", "src/Scaffolder/src/helpers.php", "src/Stempler/src/helpers.php", "src/Translator/src/helpers.php" @@ -4794,15 +5784,13 @@ "Spiral\\Csrf\\": "src/Csrf/src", "Spiral\\Http\\": "src/Http/src", "Spiral\\Cache\\": "src/Cache/src", - "Spiral\\Debug\\": [ - "src/Debug/src", - "src/Dumper/src" - ], + "Spiral\\Debug\\": "src/Debug/src", "Spiral\\Files\\": "src/Files/src", "Spiral\\Queue\\": "src/Queue/src", "Spiral\\Views\\": "src/Views/src", "Spiral\\Config\\": "src/Config/src", "Spiral\\DotEnv\\": "src/Bridge/Dotenv/src", + "Spiral\\Events\\": "src/Events/src", "Spiral\\Logger\\": "src/Logger/src", "Spiral\\Mailer\\": "src/Mailer/src", "Spiral\\Models\\": "src/Models/src", @@ -4819,10 +5807,6 @@ "Spiral\\Session\\": "src/Session/src", "Spiral\\Storage\\": "src/Storage/src", "Spiral\\Streams\\": "src/Streams/src", - "Spiral\\DataGrid\\": [ - "src/Bridge/DataGrid/src", - "src/DataGrid/src" - ], "Spiral\\Security\\": "src/Security/src", "Spiral\\Stempler\\": [ "src/Bridge/Stempler/src", @@ -4831,14 +5815,14 @@ "Spiral\\Encrypter\\": "src/Encrypter/src", "Spiral\\Prototype\\": "src/Prototype/src", "Spiral\\Snapshots\\": "src/Snapshots/src", + "Spiral\\Telemetry\\": "src/Telemetry/src", "Spiral\\Tokenizer\\": "src/Tokenizer/src", - "Spiral\\Attributes\\": "src/Attributes/src", "Spiral\\Exceptions\\": "src/Exceptions/src", "Spiral\\Pagination\\": "src/Pagination/src", "Spiral\\Scaffolder\\": "src/Scaffolder/src", + "Spiral\\Serializer\\": "src/Serializer/src", "Spiral\\Translator\\": "src/Translator/src", "Spiral\\Validation\\": "src/Validation/src", - "Spiral\\Annotations\\": "src/Annotations/src", "Spiral\\Broadcasting\\": "src/Broadcasting/src", "Spiral\\Distribution\\": "src/Distribution/src" } @@ -4849,8 +5833,20 @@ ], "authors": [ { - "name": "Wolfy-J", - "email": "wolfy.jd@gmail.com" + "name": "Anton Titov (wolfy-j)", + "email": "wolfy-j@spiralscout.com" + }, + { + "name": "Pavel Butchnev (butschster)", + "email": "pavel.buchnev@spiralscout.com" + }, + { + "name": "Aleksei Gagarin (roxblnfk)", + "email": "alexey.gagarin@spiralscout.com" + }, + { + "name": "Maksim Smakouz (msmakouz)", + "email": "maksim.smakouz@spiralscout.com" } ], "description": "Spiral, High-Performance PHP/Go Framework", @@ -4859,7 +5855,7 @@ "issues": "https://github.com/spiral/framework/issues", "source": "https://github.com/spiral/framework" }, - "time": "2022-09-12T15:05:09+00:00" + "time": "2023-02-20T15:36:00+00:00" }, { "name": "spiral/goridge", @@ -4925,28 +5921,27 @@ }, { "name": "spiral/nyholm-bridge", - "version": "1.1.1", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/spiral/nyholm-bridge.git", - "reference": "ec2c5aab394d42a6efddf6ef78d768d78df19c18" + "reference": "e3d99a09a56450fa42d652bdcc3b96434f92448e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spiral/nyholm-bridge/zipball/ec2c5aab394d42a6efddf6ef78d768d78df19c18", - "reference": "ec2c5aab394d42a6efddf6ef78d768d78df19c18", + "url": "https://api.github.com/repos/spiral/nyholm-bridge/zipball/e3d99a09a56450fa42d652bdcc3b96434f92448e", + "reference": "e3d99a09a56450fa42d652bdcc3b96434f92448e", "shasum": "" }, "require": { - "nyholm/psr7": "^1.1", - "php": ">=7.2", - "spiral/framework": "^2.0", - "spiral/http": "^1.0|^2.0" + "nyholm/psr7": "^1.4", + "php": ">=8.1", + "spiral/boot": "^3.0", + "spiral/http": "^3.0" }, "require-dev": { - "mockery/mockery": "^1.1", - "phpunit/phpunit": "~7.0", - "spiral/code-style": "^1.0" + "phpunit/phpunit": "^9.5.20", + "vimeo/psalm": ">=4.4" }, "type": "library", "autoload": { @@ -4967,9 +5962,9 @@ "description": "Spiral Framework: Nyholm PSR-7/PSR-17 bridge", "support": { "issues": "https://github.com/spiral/nyholm-bridge/issues", - "source": "https://github.com/spiral/nyholm-bridge/tree/1.1.1" + "source": "https://github.com/spiral/nyholm-bridge/tree/v1.3.0" }, - "time": "2022-04-07T17:25:18+00:00" + "time": "2022-09-19T07:50:08+00:00" }, { "name": "spiral/roadrunner", @@ -5028,40 +6023,41 @@ }, { "name": "spiral/roadrunner-bridge", - "version": "1.3.0", + "version": "2.5.1", "source": { "type": "git", "url": "https://github.com/spiral/roadrunner-bridge.git", - "reference": "293c5262dd9792c83a62245f9da9c3293fe1c453" + "reference": "00620efe32026c1d737ba37dc5999c7a528ac4f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spiral/roadrunner-bridge/zipball/293c5262dd9792c83a62245f9da9c3293fe1c453", - "reference": "293c5262dd9792c83a62245f9da9c3293fe1c453", + "url": "https://api.github.com/repos/spiral/roadrunner-bridge/zipball/00620efe32026c1d737ba37dc5999c7a528ac4f9", + "reference": "00620efe32026c1d737ba37dc5999c7a528ac4f9", "shasum": "" }, "require": { - "laminas/laminas-diactoros": "^2.4", - "php": ">=7.4", - "psr/simple-cache": "^1.0", - "spiral/framework": "^2.13", + "grpc/grpc": "^1.42", + "php": ">=8.1", + "php-http/message-factory": "^1.0", + "psr/simple-cache": "2 - 3", + "roadrunner-php/app-logger": "^1.0", + "roadrunner-php/centrifugo": "^1.0", + "spiral/framework": "^3.5", "spiral/roadrunner-broadcast": "^2.0", "spiral/roadrunner-grpc": "^2.0", - "spiral/roadrunner-jobs": "^2.0", - "spiral/roadrunner-kv": "^2.0", + "spiral/roadrunner-jobs": "^2.6", + "spiral/roadrunner-kv": "^2.2||^3.0", "spiral/roadrunner-metrics": "^2.0", - "spiral/roadrunner-tcp": "^2.0" + "spiral/roadrunner-tcp": "^2.0", + "spiral/serializer": "^3.2" }, "require-dev": { - "mockery/mockery": "^1.3", - "phpunit/phpunit": "^8.5|^9.0", - "vimeo/psalm": "^4.9" + "spiral/nyholm-bridge": "^1.2", + "spiral/testing": "^2.0.x-dev", + "vimeo/psalm": "^4.22" }, "type": "library", "autoload": { - "files": [ - "src/polyfill.php" - ], "psr-4": { "Spiral\\RoadRunnerBridge\\": "src" } @@ -5086,7 +6082,7 @@ "issues": "https://github.com/spiral/framework/issues", "source": "https://github.com/spiral/roadrunner-bridge" }, - "time": "2022-08-24T09:11:33+00:00" + "time": "2023-03-01T12:17:18+00:00" }, { "name": "spiral/roadrunner-broadcast", @@ -5420,26 +6416,25 @@ }, { "name": "spiral/roadrunner-kv", - "version": "v2.2.1", + "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/spiral/roadrunner-kv.git", - "reference": "dd99912d0f60518bbfe2cf83bb3a49f92d46e184" + "reference": "4e9694faad119d3ece2d7632f85f980f14ab5daf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spiral/roadrunner-kv/zipball/dd99912d0f60518bbfe2cf83bb3a49f92d46e184", - "reference": "dd99912d0f60518bbfe2cf83bb3a49f92d46e184", + "url": "https://api.github.com/repos/spiral/roadrunner-kv/zipball/4e9694faad119d3ece2d7632f85f980f14ab5daf", + "reference": "4e9694faad119d3ece2d7632f85f980f14ab5daf", "shasum": "" }, "require": { "ext-json": "*", "google/protobuf": "^3.7", - "php": ">=7.4", - "psr/simple-cache": "1 - 2", + "php": ">=8.1", + "psr/simple-cache": "2 - 3", "spiral/goridge": "^3.1", - "spiral/roadrunner": "^2.0", - "symfony/polyfill-php80": "^1.23" + "spiral/roadrunner": "^2.0" }, "require-dev": { "phpunit/phpunit": "^8.0", @@ -5484,9 +6479,9 @@ "description": "RoadRunner kv plugin bridge", "support": { "issues": "https://github.com/spiral/roadrunner-kv/issues", - "source": "https://github.com/spiral/roadrunner-kv/tree/v2.2.1" + "source": "https://github.com/spiral/roadrunner-kv/tree/v3.0.0" }, - "time": "2022-11-05T14:55:12+00:00" + "time": "2022-09-26T08:54:18+00:00" }, { "name": "spiral/roadrunner-metrics", @@ -5666,127 +6661,57 @@ "time": "2022-10-28T09:36:29+00:00" }, { - "name": "stella-maris/clock", - "version": "0.1.7", - "source": { - "type": "git", - "url": "https://github.com/stella-maris-solutions/clock.git", - "reference": "fa23ce16019289a18bb3446fdecd45befcdd94f8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/stella-maris-solutions/clock/zipball/fa23ce16019289a18bb3446fdecd45befcdd94f8", - "reference": "fa23ce16019289a18bb3446fdecd45befcdd94f8", - "shasum": "" - }, - "require": { - "php": "^7.0|^8.0", - "psr/clock": "^1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "StellaMaris\\Clock\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Andreas Heigl", - "role": "Maintainer" - } - ], - "description": "A pre-release of the proposed PSR-20 Clock-Interface", - "homepage": "https://gitlab.com/stella-maris/clock", - "keywords": [ - "clock", - "datetime", - "point in time", - "psr20" - ], - "support": { - "source": "https://github.com/stella-maris-solutions/clock/tree/0.1.7" - }, - "time": "2022-11-25T16:15:06+00:00" - }, - { - "name": "swiftmailer/swiftmailer", - "version": "v6.3.0", + "name": "spiral/validator", + "version": "1.2.1", "source": { "type": "git", - "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c" + "url": "https://github.com/spiral/validator.git", + "reference": "3bf210249145fc30f1db42b7a192ffe228d1720f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/8a5d5072dca8f48460fce2f4131fcc495eec654c", - "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c", + "url": "https://api.github.com/repos/spiral/validator/zipball/3bf210249145fc30f1db42b7a192ffe228d1720f", + "reference": "3bf210249145fc30f1db42b7a192ffe228d1720f", "shasum": "" }, "require": { - "egulias/email-validator": "^2.0|^3.1", - "php": ">=7.0.0", - "symfony/polyfill-iconv": "^1.0", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0" + "ext-json": "*", + "php": ">=8.1", + "spiral/core": "^3.1", + "spiral/files": "^3.1", + "spiral/filters": "^3.1", + "spiral/streams": "^3.1", + "spiral/translator": "^3.1", + "spiral/validation": "^3.1" }, "require-dev": { - "mockery/mockery": "^1.0", - "symfony/phpunit-bridge": "^4.4|^5.4" - }, - "suggest": { - "ext-intl": "Needed to support internationalized email addresses" + "phpunit/phpunit": "^9.5.20", + "spiral/hmvc": "^3.1", + "spiral/testing": "^2.0", + "vimeo/psalm": "^4.21" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.2-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "files": [ - "lib/swift_required.php" - ] + "psr-4": { + "Spiral\\Validator\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Chris Corbyn" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Swiftmailer, free feature-rich PHP mailer", - "homepage": "https://swiftmailer.symfony.com", - "keywords": [ - "email", - "mail", - "mailer" - ], + "description": "Nested validation, Checkers, Conditional Validation", + "homepage": "https://spiral.dev", "support": { - "issues": "https://github.com/swiftmailer/swiftmailer/issues", - "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.3.0" + "issues": "https://github.com/spiral/validator/issues", + "source": "https://github.com/spiral/validator" }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/swiftmailer/swiftmailer", - "type": "tidelift" - } - ], - "abandoned": "symfony/mailer", - "time": "2021-10-18T15:26:12+00:00" + "time": "2023-02-23T12:19:47+00:00" }, { "name": "symfony/console", @@ -7081,85 +8006,6 @@ ], "time": "2022-11-03T14:55:06+00:00" }, - { - "name": "symfony/polyfill-php73", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", - "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, { "name": "symfony/polyfill-php80", "version": "v1.27.0", @@ -7244,41 +8090,29 @@ "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.27.0", + "name": "symfony/process", + "version": "v6.2.7", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" + "url": "https://github.com/symfony/process.git", + "reference": "680e8a2ea6b3f87aecc07a6a65a203ae573d1902" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", - "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", + "url": "https://api.github.com/repos/symfony/process/zipball/680e8a2ea6b3f87aecc07a6a65a203ae573d1902", + "reference": "680e8a2ea6b3f87aecc07a6a65a203ae573d1902", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Component\\Process\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -7287,24 +8121,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" + "source": "https://github.com/symfony/process/tree/v6.2.7" }, "funding": [ { @@ -7320,7 +8148,7 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-02-24T10:42:00+00:00" }, { "name": "symfony/service-contracts", @@ -8003,6 +8831,64 @@ ], "time": "2020-12-02T01:58:49+00:00" }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, { "name": "yiisoft/friendly-exception", "version": "1.1.0", @@ -9567,6 +10453,61 @@ ], "time": "2023-03-09T06:34:10+00:00" }, + { + "name": "qossmic/deptrac-shim", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/qossmic/deptrac-shim.git", + "reference": "3179a2c4978654add865309e3c280ef3d60f0043" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/qossmic/deptrac-shim/zipball/3179a2c4978654add865309e3c280ef3d60f0043", + "reference": "3179a2c4978654add865309e3c280ef3d60f0043", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "ext-zlib": "*", + "php": "^8.1" + }, + "replace": { + "qossmic/deptrac": "self.version" + }, + "suggest": { + "ext-dom": "For using the JUnit output formatter" + }, + "bin": [ + "deptrac" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tim Glabisch" + }, + { + "name": "Simon Mönch", + "email": "simon.moench@qossmic.com" + }, + { + "name": "Denis Brumann", + "email": "denis.brumann@qossmic.com", + "role": "maintainer" + } + ], + "description": "deptrac phar distribution", + "support": { + "issues": "https://github.com/qossmic/deptrac-shim/issues", + "source": "https://github.com/qossmic/deptrac-shim/tree/1.0.2" + }, + "time": "2022-12-02T11:47:53+00:00" + }, { "name": "sebastian/cli-parser", "version": "1.0.1", @@ -10533,40 +11474,44 @@ }, { "name": "spiral/testing", - "version": "1.3.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/spiral/testing.git", - "reference": "4f178abada15995fb79ee7e717edb615ef064321" + "reference": "e923e94647fb2836370741727366a226f0d9bbfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spiral/testing/zipball/4f178abada15995fb79ee7e717edb615ef064321", - "reference": "4f178abada15995fb79ee7e717edb615ef064321", + "url": "https://api.github.com/repos/spiral/testing/zipball/e923e94647fb2836370741727366a226f0d9bbfc", + "reference": "e923e94647fb2836370741727366a226f0d9bbfc", "shasum": "" }, "require": { "ext-json": "*", "mockery/mockery": "^1.5", "nyholm/psr7": "^1.5", - "php": ">=7.4", - "phpunit/phpunit": "^8.5|^9.5", - "spiral/auth": "^2.9", - "spiral/auth-http": "^2.9", - "spiral/boot": "^2.9", - "spiral/console": "^2.9", - "spiral/http": "^2.9", - "spiral/mailer": "^2.9", - "spiral/queue": "^2.9", - "spiral/security": "^2.9", - "spiral/session": "^2.9", - "spiral/storage": "^2.11", - "spiral/tokenizer": "^2.9", - "symfony/mime": "^5.3|^6.0" - }, - "require-dev": { - "spiral/framework": "^2.9", - "spiral/roadrunner-bridge": "^1.0", + "php": ">=8.1", + "phpunit/phpunit": "^9.5", + "spiral/auth": "^3.0", + "spiral/auth-http": "^3.0", + "spiral/boot": "^3.0", + "spiral/console": "^3.0", + "spiral/events": "^3.0", + "spiral/http": "^3.0", + "spiral/mailer": "^3.0", + "spiral/queue": "^3.0", + "spiral/security": "^3.0", + "spiral/session": "^3.0", + "spiral/storage": "^3.0", + "spiral/tokenizer": "^3.0", + "spiral/translator": "^3.0", + "spiral/views": "^3.0", + "symfony/mime": "^6.0" + }, + "require-dev": { + "spiral-packages/league-event": "^1.0", + "spiral/nyholm-bridge": "^1.2", + "spiral/roadrunner-bridge": "^2.0", "vimeo/psalm": "^4.9" }, "suggest": { @@ -10604,9 +11549,9 @@ ], "support": { "issues": "https://github.com/spiral/testing/issues", - "source": "https://github.com/spiral/testing/tree/1.3.0" + "source": "https://github.com/spiral/testing/tree/2.2.0" }, - "time": "2022-07-21T06:05:12+00:00" + "time": "2022-10-26T07:41:37+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -10665,6 +11610,94 @@ }, "time": "2023-02-22T23:07:41+00:00" }, + { + "name": "symfony/var-dumper", + "version": "v6.2.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "cf8d4ca1ddc1e3cc242375deb8fc23e54f5e2a1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cf8d4ca1ddc1e3cc242375deb8fc23e54f5e2a1e", + "reference": "cf8d4ca1ddc1e3cc242375deb8fc23e54f5e2a1e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "phpunit/phpunit": "<5.4.3", + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.2.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-02-24T10:42:00+00:00" + }, { "name": "theseer/tokenizer", "version": "1.2.1", @@ -10823,64 +11856,6 @@ }, "time": "2022-11-06T20:37:08+00:00" }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, { "name": "webmozart/path-util", "version": "2.3.0", @@ -10942,7 +11917,8 @@ "php": ">=8.1", "ext-mbstring": "*", "ext-openssl": "*", - "ext-pdo": "*" + "ext-pdo": "*", + "ext-sockets": "*" }, "platform-dev": [], "plugin-api-version": "2.2.0" diff --git a/phpunit.xml b/phpunit.xml index 8c2118d..1d73a68 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,6 +1,6 @@ - - - app/src - - - - - tests/Unit - - - tests/Feature - - - - - + + + app/src + + + + + tests/Unit + + + tests/Feature + + + + + + + + + + + + + diff --git a/tests/App/TestApp.php b/tests/App/TestKernel.php similarity index 74% rename from tests/App/TestApp.php rename to tests/App/TestKernel.php index 473da98..f946fa2 100644 --- a/tests/App/TestApp.php +++ b/tests/App/TestKernel.php @@ -8,7 +8,7 @@ use Spiral\Testing\TestableKernelInterface; use Spiral\Testing\Traits\TestableKernel; -class TestApp extends App implements TestableKernelInterface +class TestKernel extends App implements TestableKernelInterface { use TestableKernel; } diff --git a/tests/Feature/Bootloader/LoggingBootloaderTest.php b/tests/Feature/Bootloader/LoggingBootloaderTest.php deleted file mode 100644 index afb3d27..0000000 --- a/tests/Feature/Bootloader/LoggingBootloaderTest.php +++ /dev/null @@ -1,25 +0,0 @@ -getMockBuilder(EnvironmentInterface::class)->getMock(); - $env->expects($this->once())->method('get')->with('DEBUG')->willReturn(true); - - ContainerScope::runScope($this->getContainer(), function () use ($env) { - $bootloader = new LoggingBootloader(); - $bootloader->boot($this->getContainer()->get(MonologBootloader::class), $env); - }); - } -} diff --git a/tests/Feature/Mail/EmailConfirmationMailTest.php b/tests/Feature/Mail/EmailConfirmationMailTest.php index 4263cae..73875f2 100644 --- a/tests/Feature/Mail/EmailConfirmationMailTest.php +++ b/tests/Feature/Mail/EmailConfirmationMailTest.php @@ -5,6 +5,7 @@ namespace Tests\Feature\Mail; use App\Mail\EmailConfirmationMail; +use Symfony\Component\Mime\Address; use Tests\Factories\UserFactory; use Tests\Fixtures; use Tests\TestCase; @@ -20,8 +21,12 @@ public function testBuild(): void $mail = $mail->build(); - $this->assertArrayHasKey($user->email, $mail->getSwiftMessage()->getTo()); - $this->assertContains($user->fullName(), $mail->getSwiftMessage()->getTo()); - $this->assertNotEmpty($mail->getSwiftMessage()->getSubject()); + $to = $mail->getEmailMessage()->getTo(); + $this->assertIsArray($to); + $this->assertCount(1, $to); + $this->assertInstanceOf(Address::class, $to[0]); + $this->assertEquals($user->email, $to[0]->getAddress()); + $this->assertEquals($user->fullName(), $to[0]->getName()); + $this->assertNotEmpty($mail->getEmailMessage()->getSubject()); } } diff --git a/tests/Feature/Mail/ForgotPasswordMailTest.php b/tests/Feature/Mail/ForgotPasswordMailTest.php index 0902237..08f597f 100644 --- a/tests/Feature/Mail/ForgotPasswordMailTest.php +++ b/tests/Feature/Mail/ForgotPasswordMailTest.php @@ -5,6 +5,7 @@ namespace Tests\Feature\Mail; use App\Mail\ForgotPasswordMail; +use Symfony\Component\Mime\Address; use Tests\Factories\UserFactory; use Tests\Fixtures; use Tests\TestCase; @@ -20,8 +21,12 @@ public function testBuild(): void $mail = $mail->build(); - $this->assertArrayHasKey($user->email, $mail->getSwiftMessage()->getTo()); - $this->assertContains($user->fullName(), $mail->getSwiftMessage()->getTo()); - $this->assertNotEmpty($mail->getSwiftMessage()->getSubject()); + $to = $mail->getEmailMessage()->getTo(); + $this->assertIsArray($to); + $this->assertCount(1, $to); + $this->assertInstanceOf(Address::class, $to[0]); + $this->assertEquals($user->email, $to[0]->getAddress()); + $this->assertEquals($user->fullName(), $to[0]->getName()); + $this->assertNotEmpty($mail->getEmailMessage()->getSubject()); } } diff --git a/tests/Feature/Mail/TestMailTest.php b/tests/Feature/Mail/TestMailTest.php index 77cee03..242f0d9 100644 --- a/tests/Feature/Mail/TestMailTest.php +++ b/tests/Feature/Mail/TestMailTest.php @@ -5,6 +5,7 @@ namespace Tests\Feature\Mail; use App\Mail\TestMail; +use Symfony\Component\Mime\Address; use Tests\Factories\UserFactory; use Tests\TestCase; @@ -18,8 +19,12 @@ public function testBuild(): void $mail = $mail->build(); - $this->assertArrayHasKey($user->email, $mail->getSwiftMessage()->getTo()); - $this->assertContains($user->fullName(), $mail->getSwiftMessage()->getTo()); - $this->assertNotEmpty($mail->getSwiftMessage()->getSubject()); + $to = $mail->getEmailMessage()->getTo(); + $this->assertIsArray($to); + $this->assertCount(1, $to); + $this->assertInstanceOf(Address::class, $to[0]); + $this->assertEquals($user->email, $to[0]->getAddress()); + $this->assertEquals($user->fullName(), $to[0]->getName()); + $this->assertNotEmpty($mail->getEmailMessage()->getSubject()); } } diff --git a/tests/Feature/Mail/WalletShareMailTest.php b/tests/Feature/Mail/WalletShareMailTest.php index 22e72e9..04a8d0d 100644 --- a/tests/Feature/Mail/WalletShareMailTest.php +++ b/tests/Feature/Mail/WalletShareMailTest.php @@ -5,6 +5,7 @@ namespace Tests\Feature\Mail; use App\Mail\WalletShareMail; +use Symfony\Component\Mime\Address; use Tests\Factories\UserFactory; use Tests\Factories\WalletFactory; use Tests\Fixtures; @@ -23,8 +24,12 @@ public function testBuild(): void $mail = $mail->build(); - $this->assertArrayHasKey($user->email, $mail->getSwiftMessage()->getTo()); - $this->assertContains($user->fullName(), $mail->getSwiftMessage()->getTo()); - $this->assertNotEmpty($mail->getSwiftMessage()->getSubject()); + $to = $mail->getEmailMessage()->getTo(); + $this->assertIsArray($to); + $this->assertCount(1, $to); + $this->assertInstanceOf(Address::class, $to[0]); + $this->assertEquals($user->email, $to[0]->getAddress()); + $this->assertEquals($user->fullName(), $to[0]->getName()); + $this->assertNotEmpty($mail->getEmailMessage()->getSubject()); } } diff --git a/tests/Feature/Service/CorsServiceTest.php b/tests/Feature/Service/CorsServiceTest.php index e3a67ac..f5c1e21 100644 --- a/tests/Feature/Service/CorsServiceTest.php +++ b/tests/Feature/Service/CorsServiceTest.php @@ -28,7 +28,7 @@ public function testPreflightRequest(): void 'Origin' => $origin, ]; - $response = $this->fakeHttp()->optionsJson('/currencies', $requestHeaders); + $response = $this->http()->optionsJson('/currencies', $requestHeaders); $response->assertHasHeader('Access-Control-Allow-Origin'); $response->assertHasHeader('Access-Control-Allow-Methods'); @@ -47,7 +47,7 @@ public function testPreflightRequestRejected(): void 'Origin' => $origin, ]; - $response = $this->fakeHttp()->optionsJson('/currencies', $requestHeaders); + $response = $this->http()->optionsJson('/currencies', $requestHeaders); $response->assertHeaderMissing('Access-Control-Allow-Origin'); $response->assertHeaderMissing('Access-Control-Allow-Methods'); @@ -63,7 +63,7 @@ public function testNormalRequest(): void 'Origin' => $origin, ]; - $response = $this->fakeHttp()->optionsJson('/currencies', $requestHeaders); + $response = $this->http()->optionsJson('/currencies', $requestHeaders); $response->assertHasHeader('Access-Control-Allow-Origin'); $response->assertHasHeader('Vary'); @@ -82,7 +82,7 @@ public function testNormalRequestAllOrigins(): void 'Origin' => Fixtures::string(), ]; - $response = $this->fakeHttp()->optionsJson('/currencies', $requestHeaders); + $response = $this->http()->optionsJson('/currencies', $requestHeaders); $response->assertHasHeader('Access-Control-Allow-Origin'); $response->assertHasHeader('Vary'); @@ -102,7 +102,7 @@ public function testNormalRequestSingleOrigin(): void 'Origin' => $origin, ]; - $response = $this->fakeHttp()->optionsJson('/currencies', $requestHeaders); + $response = $this->http()->optionsJson('/currencies', $requestHeaders); $response->assertHasHeader('Access-Control-Allow-Origin'); $response->assertHasHeader('Vary'); @@ -123,7 +123,7 @@ public function testNormalRequestAllowedCredentials(): void 'Origin' => $origin, ]; - $response = $this->fakeHttp()->optionsJson('/currencies', $requestHeaders); + $response = $this->http()->optionsJson('/currencies', $requestHeaders); $response->assertHasHeader('Access-Control-Allow-Origin'); $response->assertHasHeader('Access-Control-Allow-Credentials'); @@ -149,7 +149,7 @@ public function testPreflightRequestAllowedAllMethods(): void 'Origin' => $origin, ]; - $response = $this->fakeHttp()->optionsJson('/currencies', $requestHeaders); + $response = $this->http()->optionsJson('/currencies', $requestHeaders); $response->assertHasHeader('Access-Control-Allow-Origin'); $response->assertHasHeader('Access-Control-Allow-Methods'); @@ -177,7 +177,7 @@ public function testPreflightRequestNotAllowedAllHeaders(): void 'Origin' => $origin, ]; - $response = $this->fakeHttp()->optionsJson('/currencies', $requestHeaders); + $response = $this->http()->optionsJson('/currencies', $requestHeaders); $response->assertHasHeader('Access-Control-Allow-Origin'); $response->assertHasHeader('Access-Control-Allow-Headers'); @@ -202,7 +202,7 @@ public function testNormalRequestExposeHeaders(): void 'Origin' => $origin, ]; - $response = $this->fakeHttp()->optionsJson('/currencies', $requestHeaders); + $response = $this->http()->optionsJson('/currencies', $requestHeaders); $response->assertHasHeader('Access-Control-Allow-Origin'); $response->assertHasHeader('Access-Control-Expose-Headers'); @@ -227,7 +227,7 @@ public function testPreflightRequestNoOriginHeader(): void 'Access-Control-Request-Method' => ['GET'], ]; - $response = $this->fakeHttp()->optionsJson('/currencies', $requestHeaders); + $response = $this->http()->optionsJson('/currencies', $requestHeaders); $response->assertHeaderMissing('Access-Control-Allow-Origin'); $response->assertHeaderMissing('Access-Control-Allow-Methods'); @@ -254,7 +254,7 @@ public function testPreflightRequestAllowedOriginPattern(): void 'Origin' => $origin, ]; - $response = $this->fakeHttp()->optionsJson('/currencies', $requestHeaders); + $response = $this->http()->optionsJson('/currencies', $requestHeaders); $response->assertHasHeader('Access-Control-Allow-Origin'); $response->assertHasHeader('Access-Control-Allow-Methods'); diff --git a/tests/Feature/Service/Mailer/MailTest.php b/tests/Feature/Service/Mailer/MailTest.php index b654787..f5c0147 100644 --- a/tests/Feature/Service/Mailer/MailTest.php +++ b/tests/Feature/Service/Mailer/MailTest.php @@ -6,6 +6,8 @@ use App\Service\Mailer\Mail; use Spiral\Views\ViewsInterface; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; use Tests\Fixtures; use Tests\TestCase; @@ -15,7 +17,7 @@ public function testSend(): void { $mail = $this->getMockBuilder(Mail::class)->getMockForAbstractClass(); - $this->assertInstanceOf(\Swift_Message::class, $mail->getSwiftMessage()); + $this->assertInstanceOf(Email::class, $mail->getEmailMessage()); } public function testSubject(): void @@ -26,7 +28,7 @@ public function testSubject(): void $mail->subject($subject); - $this->assertEquals($subject, $mail->getSwiftMessage()->getSubject()); + $this->assertEquals($subject, $mail->getEmailMessage()->getSubject()); } public function testFrom(): void @@ -38,9 +40,12 @@ public function testFrom(): void $mail->from($address, $fullName); - $this->assertIsArray($mail->getSwiftMessage()->getFrom()); - $this->assertArrayHasKey($address, $mail->getSwiftMessage()->getFrom()); - $this->assertEquals($fullName, $mail->getSwiftMessage()->getFrom()[$address] ?? null); + $data = $mail->getEmailMessage()->getFrom(); + $this->assertIsArray($data); + $this->assertCount(1, $data); + $this->assertInstanceOf(Address::class, $data[0]); + $this->assertEquals($address, $data[0]->getAddress()); + $this->assertEquals($fullName, $data[0]->getName()); } public function testTo(): void @@ -52,9 +57,12 @@ public function testTo(): void $mail->to($address, $fullName); - $this->assertIsArray($mail->getSwiftMessage()->getTo()); - $this->assertArrayHasKey($address, $mail->getSwiftMessage()->getTo()); - $this->assertEquals($fullName, $mail->getSwiftMessage()->getTo()[$address] ?? null); + $data = $mail->getEmailMessage()->getTo(); + $this->assertIsArray($data); + $this->assertCount(1, $data); + $this->assertInstanceOf(Address::class, $data[0]); + $this->assertEquals($address, $data[0]->getAddress()); + $this->assertEquals($fullName, $data[0]->getName()); } public function testReplyTo(): void @@ -66,9 +74,12 @@ public function testReplyTo(): void $mail->replyTo($address, $fullName); - $this->assertIsArray($mail->getSwiftMessage()->getReplyTo()); - $this->assertArrayHasKey($address, $mail->getSwiftMessage()->getReplyTo()); - $this->assertEquals($fullName, $mail->getSwiftMessage()->getReplyTo()[$address] ?? null); + $data = $mail->getEmailMessage()->getReplyTo(); + $this->assertIsArray($data); + $this->assertCount(1, $data); + $this->assertInstanceOf(Address::class, $data[0]); + $this->assertEquals($address, $data[0]->getAddress()); + $this->assertEquals($fullName, $data[0]->getName()); } public function testCc(): void @@ -80,9 +91,12 @@ public function testCc(): void $mail->cc($address, $fullName); - $this->assertIsArray($mail->getSwiftMessage()->getCc()); - $this->assertArrayHasKey($address, $mail->getSwiftMessage()->getCc()); - $this->assertEquals($fullName, $mail->getSwiftMessage()->getCc()[$address] ?? null); + $data = $mail->getEmailMessage()->getCc(); + $this->assertIsArray($data); + $this->assertCount(1, $data); + $this->assertInstanceOf(Address::class, $data[0]); + $this->assertEquals($address, $data[0]->getAddress()); + $this->assertEquals($fullName, $data[0]->getName()); } public function testBcc(): void @@ -94,15 +108,18 @@ public function testBcc(): void $mail->bcc($address, $fullName); - $this->assertIsArray($mail->getSwiftMessage()->getBcc()); - $this->assertArrayHasKey($address, $mail->getSwiftMessage()->getBcc()); - $this->assertEquals($fullName, $mail->getSwiftMessage()->getBcc()[$address] ?? null); + $data = $mail->getEmailMessage()->getBcc(); + $this->assertIsArray($data); + $this->assertCount(1, $data); + $this->assertInstanceOf(Address::class, $data[0]); + $this->assertEquals($address, $data[0]->getAddress()); + $this->assertEquals($fullName, $data[0]->getName()); } public function testRender(): void { $viewName = Fixtures::string(); - $rendered = Fixtures::string(1024); + $rendered = Fixtures::string(64); $address = Fixtures::email(); $fullName = Fixtures::string(); @@ -122,6 +139,6 @@ public function testRender(): void $mail->render($views); - $this->assertEquals($rendered, $mail->getSwiftMessage()->getBody()); + $this->assertEquals($rendered, $mail->getEmailMessage()->getBody()->bodyToString()); } } diff --git a/tests/Feature/Service/Mailer/MailerTest.php b/tests/Feature/Service/Mailer/MailerTest.php index e91f05a..69a4b1b 100644 --- a/tests/Feature/Service/Mailer/MailerTest.php +++ b/tests/Feature/Service/Mailer/MailerTest.php @@ -7,6 +7,8 @@ use App\Service\Mailer\Mail; use App\Service\Mailer\Mailer; use Spiral\Views\ViewsInterface; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mime\Email; use Tests\Fixtures; use Tests\TestCase; @@ -14,29 +16,28 @@ class MailerTest extends TestCase { public function testSend(): void { - $swiftMessage = new \Swift_Message(); + $message = new Email(); - $swift = $this->getMockBuilder(\Swift_Mailer::class) + $symfonyMailer = $this->getMockBuilder(MailerInterface::class) ->onlyMethods(['send']) - ->disableOriginalConstructor() ->getMock(); - $swift->expects($this->once())->method('send')->with($swiftMessage); + $symfonyMailer->expects($this->once())->method('send')->with($message); $views = $this->getMockBuilder(ViewsInterface::class)->getMock(); - $mailer = new Mailer($swift, $views); + $mailer = new Mailer($symfonyMailer, $views); $mailer->setDefaultFromAddress(Fixtures::email()); $mailer->setDefaultFromName(Fixtures::string()); $mail = $this->getMockBuilder(Mail::class) - ->onlyMethods(['build', 'render', 'getSwiftMessage']) + ->onlyMethods(['build', 'render', 'getEmailMessage']) ->getMockForAbstractClass(); $mail->method('build')->willReturnSelf(); $mail->method('render')->with($views)->willReturnSelf(); - $mail->method('getSwiftMessage')->willReturn($swiftMessage); + $mail->method('getEmailMessage')->willReturn($message); $mailer->send($mail); } @@ -45,26 +46,25 @@ public function testRender(): void { $content = Fixtures::string(128); - $swiftMessage = new \Swift_Message(); - $swiftMessage->setBody($content, 'text/html', 'utf-8'); + $message = new Email(); + $message->html($content); - $swift = $this->getMockBuilder(\Swift_Mailer::class) - ->disableOriginalConstructor() + $symfonyMailer = $this->getMockBuilder(MailerInterface::class) ->getMock(); $views = $this->getMockBuilder(ViewsInterface::class)->getMock(); - $mailer = new Mailer($swift, $views); + $mailer = new Mailer($symfonyMailer, $views); $mailer->setDefaultFromAddress(Fixtures::email()); $mailer->setDefaultFromName(Fixtures::string()); $mail = $this->getMockBuilder(Mail::class) - ->onlyMethods(['build', 'render', 'getSwiftMessage']) + ->onlyMethods(['build', 'render', 'getEmailMessage']) ->getMockForAbstractClass(); $mail->method('build')->willReturnSelf(); $mail->method('render')->with($views)->willReturnSelf(); - $mail->method('getSwiftMessage')->willReturn($swiftMessage); + $mail->method('getEmailMessage')->willReturn($message); $this->assertEquals($content, $mailer->render($mail)); } diff --git a/tests/TestCase.php b/tests/TestCase.php index 8df719c..ad07936 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -12,7 +12,7 @@ use Spiral\Testing\TestableKernelInterface; use Spiral\Testing\TestCase as BaseTestCase; use Spiral\Translator\TranslatorInterface; -use Tests\App\TestApp; +use Tests\App\TestKernel; use Tests\Traits\AssertHelpers; use Tests\Traits\InteractsWithDatabase; use Tests\Traits\InteractsWithHttp; @@ -29,7 +29,7 @@ abstract class TestCase extends BaseTestCase protected function setUp(): void { - $this->beforeStarting(static function (ConfiguratorInterface $config): void { + $this->beforeBooting(static function (ConfiguratorInterface $config): void { if (! $config->exists('session')) { return; } @@ -72,11 +72,14 @@ public function defineDirectories(string $root): array ]; } - public function createAppInstance(): TestableKernelInterface + public function createAppInstance(Container $container = new Container()): TestableKernelInterface { - return new TestApp(new Container(), $this->defineDirectories( - $this->rootDirectory() - )); + return TestKernel::create( + directories: $this->defineDirectories( + $this->rootDirectory(), + ), + handleErrors: false, + container: $container, + ); } - } diff --git a/tests/Traits/InteractsWithHttp.php b/tests/Traits/InteractsWithHttp.php index 5aeade1..997437f 100644 --- a/tests/Traits/InteractsWithHttp.php +++ b/tests/Traits/InteractsWithHttp.php @@ -19,7 +19,7 @@ trait InteractsWithHttp protected array $authHeaders = []; - protected function fakeHttp(): FakeHttp + protected function http(): FakeHttp { return new FakeHttp( $this->getContainer(), @@ -41,7 +41,7 @@ public function get( array $headers = [], array $cookies = [] ): TestResponse { - return $this->fakeHttp()->getJson($uri, $query, $this->getHeaders($headers), $cookies); + return $this->http()->getJson($uri, $query, $this->getHeaders($headers), $cookies); } public function post( @@ -50,7 +50,7 @@ public function post( array $headers = [], array $cookies = [] ): TestResponse { - return $this->fakeHttp()->postJson($uri, $data, $this->getHeaders($headers), $cookies); + return $this->http()->postJson($uri, $data, $this->getHeaders($headers), $cookies); } public function put( @@ -59,7 +59,7 @@ public function put( array $headers = [], array $cookies = [] ): TestResponse { - return $this->fakeHttp()->putJson($uri, $data, $this->getHeaders($headers), $cookies); + return $this->http()->putJson($uri, $data, $this->getHeaders($headers), $cookies); } public function patch( @@ -68,7 +68,7 @@ public function patch( array $headers = [], array $cookies = [] ): TestResponse { - return $this->fakeHttp()->patchJson($uri, $data, $this->getHeaders($headers), $cookies); + return $this->http()->patchJson($uri, $data, $this->getHeaders($headers), $cookies); } public function delete( @@ -77,7 +77,7 @@ public function delete( array $headers = [], array $cookies = [] ): TestResponse { - return $this->fakeHttp()->deleteJson($uri, $data, $this->getHeaders($headers), $cookies); + return $this->http()->deleteJson($uri, $data, $this->getHeaders($headers), $cookies); } public function getResponseBody(TestResponse|ResponseInterface $response): string From 1d9ad5438a70e12f1c0b887dc09f336c0edcb632 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sat, 18 Mar 2023 13:03:12 +0200 Subject: [PATCH 02/20] Update pipeline cmd to download RR --- .github/workflows/quality.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index da10e1b..b81d94d 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -127,7 +127,7 @@ jobs: php app.php encrypt:key -m .env php app.php rsa:gen -m .env php app.php configure -vv - vendor/bin/spiral get-binary + vendor/bin/rr get --quiet php app.php migrate:init php app.php migrate From e6adea588edb645ce55774671fe1d64f7e5c6b46 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sat, 18 Mar 2023 14:43:54 +0200 Subject: [PATCH 03/20] Fix static analysis issues --- .../Bootloader/ExceptionHandlerBootloader.php | 2 +- app/src/Bootloader/FirebaseBootloader.php | 11 ++- app/src/Controller/Tags/ChargesController.php | 12 +-- app/src/Controller/Tags/CommonController.php | 2 +- app/src/Controller/Tags/TagsController.php | 4 +- .../Controller/Wallets/ActiveController.php | 8 +- .../Controller/Wallets/ArchiveController.php | 8 +- .../Wallets/Charges/ChargesController.php | 18 ++-- .../Wallets/Charges/TagsController.php | 4 +- .../Controller/Wallets/IndexController.php | 6 +- app/src/Controller/Wallets/TagsController.php | 12 +-- .../Controller/Wallets/UsersController.php | 16 ++-- .../Controller/Wallets/WalletsController.php | 6 +- app/src/Exception/ViewRenderer.php | 84 +++++++++++++++++++ app/src/Request/CheckNickNameRequest.php | 2 +- app/src/Service/Mailer/Mail.php | 20 ++--- app/src/Service/Mailer/Mailer.php | 2 +- .../Service/Pagination/PaginationFactory.php | 2 +- .../Service/Statistics/ProfileStatistics.php | 2 +- phpunit.xml | 2 +- 20 files changed, 156 insertions(+), 67 deletions(-) create mode 100644 app/src/Exception/ViewRenderer.php diff --git a/app/src/Bootloader/ExceptionHandlerBootloader.php b/app/src/Bootloader/ExceptionHandlerBootloader.php index fd57586..108c288 100644 --- a/app/src/Bootloader/ExceptionHandlerBootloader.php +++ b/app/src/Bootloader/ExceptionHandlerBootloader.php @@ -4,7 +4,7 @@ namespace App\Bootloader; -use App\Application\Exception\Renderer\ViewRenderer; +use App\Exception\ViewRenderer; use Spiral\Boot\AbstractKernel; use Spiral\Boot\Bootloader\Bootloader; use Spiral\Exceptions\ExceptionHandler; diff --git a/app/src/Bootloader/FirebaseBootloader.php b/app/src/Bootloader/FirebaseBootloader.php index ee004a8..f28401b 100644 --- a/app/src/Bootloader/FirebaseBootloader.php +++ b/app/src/Bootloader/FirebaseBootloader.php @@ -6,6 +6,7 @@ use App\Config\FirebaseConfig; use Kreait\Firebase\Factory; +use Nyholm\Psr7\Uri; use Spiral\Boot\Bootloader\Bootloader; use Spiral\Core\Container; @@ -34,11 +35,15 @@ public function boot(Container $container): void { $container->bind(Factory::class, function (): Factory { $factory = (new Factory()) - ->withDatabaseUri($this->config->getDatabaseUri()) - ->withDefaultStorageBucket($this->config->getStorageBucket()) - ->withDefaultStorageBucket($this->config->getStorageBucket()) + ->withDatabaseUri(new Uri($this->config->getDatabaseUri())) ->withServiceAccount($this->getServiceAccount()); + if (($storageBucket = $this->config->getStorageBucket()) !== '') { + $factory = $factory + ->withDefaultStorageBucket($storageBucket) + ->withDefaultStorageBucket($storageBucket); + } + return $factory; }); } diff --git a/app/src/Controller/Tags/ChargesController.php b/app/src/Controller/Tags/ChargesController.php index 383dee8..254039b 100644 --- a/app/src/Controller/Tags/ChargesController.php +++ b/app/src/Controller/Tags/ChargesController.php @@ -38,8 +38,8 @@ public function __construct( parent::__construct($auth); } - #[Route(route: '/tags//charges', name: 'tag.charges', methods: 'GET', group: 'auth')] - public function list($id, InputManager $input): ResponseInterface + #[Route(route: '/tags//charges', name: 'tag.charges', methods: 'GET', group: 'auth')] + public function list(string $id, InputManager $input): ResponseInterface { $tag = $this->tagRepository->findByPKByUsersPK((int) $id, $this->userRepository->getCommonUserIDs($this->user)); if (! $tag instanceof Tag) { @@ -55,8 +55,8 @@ public function list($id, InputManager $input): ResponseInterface ->jsonPaginated($charges, $this->chargeRepository->getPaginationState()); } - #[Route(route: '/tags//charges/total', name: 'tag.charges.total', methods: 'GET', group: 'auth')] - public function total($id, InputManager $input): ResponseInterface + #[Route(route: '/tags//charges/total', name: 'tag.charges.total', methods: 'GET', group: 'auth')] + public function total(string $id, InputManager $input): ResponseInterface { $tag = $this->tagRepository->findByPKByUsersPK((int) $id, $this->userRepository->getCommonUserIDs($this->user)); if (! $tag instanceof Tag) { @@ -78,8 +78,8 @@ public function total($id, InputManager $input): ResponseInterface ]); } - #[Route(route: '/tags//charges/graph', name: 'tag.charges.graph', methods: 'GET', group: 'auth')] - public function graph($id, InputManager $input, ChargeAmountGraph $graph): ResponseInterface + #[Route(route: '/tags//charges/graph', name: 'tag.charges.graph', methods: 'GET', group: 'auth')] + public function graph(string $id, InputManager $input, ChargeAmountGraph $graph): ResponseInterface { $tag = $this->tagRepository->findByPKByUsersPK((int) $id, $this->userRepository->getCommonUserIDs($this->user)); if (! $tag instanceof Tag) { diff --git a/app/src/Controller/Tags/CommonController.php b/app/src/Controller/Tags/CommonController.php index 9e9a5ef..12b9c7f 100644 --- a/app/src/Controller/Tags/CommonController.php +++ b/app/src/Controller/Tags/CommonController.php @@ -37,7 +37,7 @@ public function list(): ResponseInterface } #[Route(route: '/tags/common/', name: 'tag.common.index', methods: 'GET', group: 'auth')] - public function index($id): ResponseInterface + public function index(string $id): ResponseInterface { $tag = $this->tagRepository->findByPKByUsersPK((int) $id, $this->userRepository->getCommonUserIDs($this->user)); diff --git a/app/src/Controller/Tags/TagsController.php b/app/src/Controller/Tags/TagsController.php index dc640aa..96be2d0 100644 --- a/app/src/Controller/Tags/TagsController.php +++ b/app/src/Controller/Tags/TagsController.php @@ -62,7 +62,7 @@ public function create(CreateRequest $request): ResponseInterface } #[Route(route: '/tags/', name: 'tag.update', methods: 'PUT', group: 'auth')] - public function update($id, UpdateRequest $request): ResponseInterface + public function update(string $id, UpdateRequest $request): ResponseInterface { $tag = $this->tagRepository->findByPKByUserPK((int) $id, (int) $this->user->id); @@ -104,7 +104,7 @@ public function update($id, UpdateRequest $request): ResponseInterface } #[Route(route: '/tags/', name: 'tag.delete', methods: 'DELETE', group: 'auth')] - public function delete($id): ResponseInterface + public function delete(string $id): ResponseInterface { $tag = $this->tagRepository->findByPKByUserPK((int) $id, (int) $this->user->id); diff --git a/app/src/Controller/Wallets/ActiveController.php b/app/src/Controller/Wallets/ActiveController.php index 162227d..cb26724 100644 --- a/app/src/Controller/Wallets/ActiveController.php +++ b/app/src/Controller/Wallets/ActiveController.php @@ -28,10 +28,10 @@ public function __construct( /** * @Route(route="/wallets//activate", name="wallet.activate", methods="POST", group="auth") * - * @param int $id + * @param string $id * @return \Psr\Http\Message\ResponseInterface */ - public function activate($id): ResponseInterface + public function activate(string $id): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); @@ -60,10 +60,10 @@ public function activate($id): ResponseInterface /** * @Route(route="/wallets//disable", name="wallet.disable", methods="POST", group="auth") * - * @param int $id + * @param string $id * @return \Psr\Http\Message\ResponseInterface */ - public function disable($id): ResponseInterface + public function disable(string $id): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); diff --git a/app/src/Controller/Wallets/ArchiveController.php b/app/src/Controller/Wallets/ArchiveController.php index d5d072c..8c84665 100644 --- a/app/src/Controller/Wallets/ArchiveController.php +++ b/app/src/Controller/Wallets/ArchiveController.php @@ -28,10 +28,10 @@ public function __construct( /** * @Route(route="/wallets//archive", name="wallet.archive", methods="POST", group="auth") * - * @param int $id + * @param string $id * @return \Psr\Http\Message\ResponseInterface */ - public function archive($id): ResponseInterface + public function archive(string $id): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); @@ -60,10 +60,10 @@ public function archive($id): ResponseInterface /** * @Route(route="/wallets//un-archive", name="wallet.unarchive", methods="POST", group="auth") * - * @param int $id + * @param string $id * @return \Psr\Http\Message\ResponseInterface */ - public function unArchive($id): ResponseInterface + public function unArchive(string $id): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); diff --git a/app/src/Controller/Wallets/Charges/ChargesController.php b/app/src/Controller/Wallets/Charges/ChargesController.php index 4aaea9e..c0c602d 100644 --- a/app/src/Controller/Wallets/Charges/ChargesController.php +++ b/app/src/Controller/Wallets/Charges/ChargesController.php @@ -40,8 +40,8 @@ public function __construct( parent::__construct($auth); } - #[Route(route: '/wallets//charges', name: 'wallet.charge.list', methods: 'GET', group: 'auth')] - public function list($walletId, InputManager $input): ResponseInterface + #[Route(route: '/wallets//charges', name: 'wallet.charge.list', methods: 'GET', group: 'auth')] + public function list(string $walletId, InputManager $input): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); @@ -57,8 +57,8 @@ public function list($walletId, InputManager $input): ResponseInterface return $this->chargesView->jsonPaginated($charges, $this->chargeRepository->getPaginationState()); } - #[Route(route: '/wallets//charges/graph', name: 'wallet.charge.graph', methods: 'GET', group: 'auth')] - public function graph($walletId, InputManager $input, ChargeAmountGraph $graph) + #[Route(route: '/wallets//charges/graph', name: 'wallet.charge.graph', methods: 'GET', group: 'auth')] + public function graph(string $walletId, InputManager $input, ChargeAmountGraph $graph): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); @@ -75,7 +75,7 @@ public function graph($walletId, InputManager $input, ChargeAmountGraph $graph) } #[Route(route: '/wallets//charges', name: 'wallet.charge.create', methods: 'POST', group: 'auth')] - public function create($walletId, CreateRequest $request): ResponseInterface + public function create(string $walletId, CreateRequest $request): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); @@ -124,8 +124,8 @@ public function create($walletId, CreateRequest $request): ResponseInterface return $this->chargeView->withRelation(Wallet::class)->json($charge); } - #[Route(route: '/wallets//charges/', name: 'wallet.charge.update', methods: 'PUT', group: 'auth')] - public function update($walletId, string $chargeId, CreateRequest $request): ResponseInterface + #[Route(route: '/wallets//charges/', name: 'wallet.charge.update', methods: 'PUT', group: 'auth')] + public function update(string $walletId, string $chargeId, CreateRequest $request): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); @@ -180,8 +180,8 @@ public function update($walletId, string $chargeId, CreateRequest $request): Res return $this->chargeView->withRelation(Wallet::class)->json($charge); } - #[Route(route: '/wallets//charges/', name: 'wallet.charge.delete', methods: 'DELETE', group: 'auth')] - public function delete($walletId, string $chargeId): ResponseInterface + #[Route(route: '/wallets//charges/', name: 'wallet.charge.delete', methods: 'DELETE', group: 'auth')] + public function delete(string $walletId, string $chargeId): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); diff --git a/app/src/Controller/Wallets/Charges/TagsController.php b/app/src/Controller/Wallets/Charges/TagsController.php index 03455dd..c5d85ef 100644 --- a/app/src/Controller/Wallets/Charges/TagsController.php +++ b/app/src/Controller/Wallets/Charges/TagsController.php @@ -36,7 +36,7 @@ public function __construct( } #[Route(route: '/wallets//tags//charges', name: 'wallet.tag.charge.list', methods: 'GET', group: 'auth')] - public function list($walletId, $tagId, InputManager $input): ResponseInterface + public function list(string $walletId, string $tagId, InputManager $input): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); if (! $wallet instanceof Wallet) { @@ -57,7 +57,7 @@ public function list($walletId, $tagId, InputManager $input): ResponseInterface } #[Route(route: '/wallets//tags//charges/graph', name: 'wallet.tag.charge.graph', methods: 'GET', group: 'auth')] - public function graph($walletId, $tagId, InputManager $input, ChargeAmountGraph $graph): ResponseInterface + public function graph(string $walletId, string $tagId, InputManager $input, ChargeAmountGraph $graph): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); if (! $wallet instanceof Wallet) { diff --git a/app/src/Controller/Wallets/IndexController.php b/app/src/Controller/Wallets/IndexController.php index 41804f5..3f07f08 100644 --- a/app/src/Controller/Wallets/IndexController.php +++ b/app/src/Controller/Wallets/IndexController.php @@ -29,8 +29,8 @@ public function __construct( parent::__construct($auth); } - #[Route(route: '/wallets/', name: 'wallet.index', methods: 'GET', group: 'auth')] - public function index($id): ResponseInterface + #[Route(route: '/wallets/', name: 'wallet.index', methods: 'GET', group: 'auth')] + public function index(string $id): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); @@ -42,7 +42,7 @@ public function index($id): ResponseInterface } #[Route(route: '/wallets//total', name: 'wallet.index.total', methods: 'GET', group: 'auth')] - public function indexTotal($id, InputManager $input): ResponseInterface + public function indexTotal(string $id, InputManager $input): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); diff --git a/app/src/Controller/Wallets/TagsController.php b/app/src/Controller/Wallets/TagsController.php index 64e915c..7f72b08 100644 --- a/app/src/Controller/Wallets/TagsController.php +++ b/app/src/Controller/Wallets/TagsController.php @@ -32,8 +32,8 @@ public function __construct( parent::__construct($auth); } - #[Route(route: '/wallets//tags', name: 'wallet.tags.list', methods: 'GET', group: 'auth')] - public function list($id): ResponseInterface + #[Route(route: '/wallets//tags', name: 'wallet.tags.list', methods: 'GET', group: 'auth')] + public function list(string $id): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); @@ -46,8 +46,8 @@ public function list($id): ResponseInterface return $this->tagsView->json($tags); } - #[Route(route: '/wallets//tags//total', name: 'wallet.tags.total', methods: 'GET', group: 'auth')] - public function total($walletId, $tagId, InputManager $input): ResponseInterface + #[Route(route: '/wallets//tags//total', name: 'wallet.tags.total', methods: 'GET', group: 'auth')] + public function total(string $walletId, string $tagId, InputManager $input): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); @@ -75,8 +75,8 @@ public function total($walletId, $tagId, InputManager $input): ResponseInterface ]); } - #[Route(route: '/wallets//tags/find/', name: 'wallet.tags.find', methods: 'GET', group: 'auth')] - public function find($walletId, string $query = ''): ResponseInterface + #[Route(route: '/wallets//tags/find/', name: 'wallet.tags.find', methods: 'GET', group: 'auth')] + public function find(string $walletId, string $query = ''): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $walletId, (int) $this->user->id); diff --git a/app/src/Controller/Wallets/UsersController.php b/app/src/Controller/Wallets/UsersController.php index 052900e..11024c6 100644 --- a/app/src/Controller/Wallets/UsersController.php +++ b/app/src/Controller/Wallets/UsersController.php @@ -33,10 +33,10 @@ public function __construct( /** * @Route(route="/wallets//users", name="wallet.users.list", methods="GET", group="auth") * - * @param int $id + * @param string $id * @return \Psr\Http\Message\ResponseInterface */ - public function users($id): ResponseInterface + public function users(string $id): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPKWithUsers((int) $id, (int) $this->user->id); @@ -50,11 +50,11 @@ public function users($id): ResponseInterface /** * @Route(route="/wallets//users/", name="wallet.users.add", methods="PATCH", group="auth") * - * @param int $id - * @param int $userId + * @param string $id + * @param string $userId * @return \Psr\Http\Message\ResponseInterface */ - public function patch($id, $userId): ResponseInterface + public function patch(string $id, string $userId): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPKWithUsers((int) $id, (int) $this->user->id); @@ -92,11 +92,11 @@ public function patch($id, $userId): ResponseInterface /** * @Route(route="/wallets//users/", name="wallet.users.delete", methods="DELETE", group="auth") * - * @param int $id - * @param int $userId + * @param string $id + * @param string $userId * @return \Psr\Http\Message\ResponseInterface */ - public function delete($id, $userId): ResponseInterface + public function delete(string $id, string $userId): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPKWithUsers((int) $id, (int) $this->user->id); diff --git a/app/src/Controller/Wallets/WalletsController.php b/app/src/Controller/Wallets/WalletsController.php index 8912c92..03213fc 100644 --- a/app/src/Controller/Wallets/WalletsController.php +++ b/app/src/Controller/Wallets/WalletsController.php @@ -54,7 +54,7 @@ public function create(CreateRequest $request): ResponseInterface } #[Route(route: '/wallets/', name: 'wallet.update', methods: 'PUT', group: 'auth')] - public function update($id, UpdateRequest $request): ResponseInterface + public function update(string $id, UpdateRequest $request): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); @@ -112,8 +112,8 @@ public function update($id, UpdateRequest $request): ResponseInterface return $this->walletView->json($wallet); } - #[Route(route: '/wallets/', name: 'wallet.delete', methods: 'DELETE', group: 'auth')] - public function delete($id): ResponseInterface + #[Route(route: '/wallets/', name: 'wallet.delete', methods: 'DELETE', group: 'auth')] + public function delete(string $id): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); diff --git a/app/src/Exception/ViewRenderer.php b/app/src/Exception/ViewRenderer.php new file mode 100644 index 0000000..2d021d8 --- /dev/null +++ b/app/src/Exception/ViewRenderer.php @@ -0,0 +1,84 @@ +getHeaderLine('Accept'))->getAll(); + if ($acceptItems && $acceptItems[0]->getValue() === 'application/json') { + return $this->renderJson($code, $exception); + } + + return $this->renderView($code, $exception); + } + + private function renderJson(int $code, \Throwable $exception): ResponseInterface + { + $response = $this->responseFactory->createResponse($code); + + $response = $response->withHeader('Content-Type', 'application/json; charset=UTF-8'); + + $payload = [ + 'status' => $code, + 'error' => $exception->getMessage(), + ]; + + if ($this->verbosity->value > Verbosity::BASIC->value) { + $payload['stacktrace'] = $exception->getTraceAsString(); + } + + $response->getBody()->write(\json_encode($payload)); + + return $response; + } + + private function renderView(int $code, \Throwable $exception): ResponseInterface + { + $response = $this->responseFactory->createResponse($code); + + try { + // Try to find view for specific exception code + $view = $this->views->get(\sprintf(self::VIEW_PATTERN, $code)); + } catch (ViewException) { + // Fallback to default view in case if specific view not found + $view = $this->views->get(self::DEFAULT_VIEW); + } + + $content = $view->render(['code' => $code, 'exception' => $exception]); + $response->getBody()->write($content); + + return $response; + } +} diff --git a/app/src/Request/CheckNickNameRequest.php b/app/src/Request/CheckNickNameRequest.php index a535819..ef60703 100644 --- a/app/src/Request/CheckNickNameRequest.php +++ b/app/src/Request/CheckNickNameRequest.php @@ -14,7 +14,7 @@ class CheckNickNameRequest extends Filter implements HasFilterDefinition { #[Data] - public string $nickName; + public string $nickName = ''; public function filterDefinition(): FilterDefinitionInterface { diff --git a/app/src/Service/Mailer/Mail.php b/app/src/Service/Mailer/Mail.php index 51b4eaa..5ce2b6d 100644 --- a/app/src/Service/Mailer/Mail.php +++ b/app/src/Service/Mailer/Mail.php @@ -58,10 +58,10 @@ public function subject(string $subject): Mail /** * @param string $address - * @param string|null $fullName + * @param string $fullName * @return \App\Service\Mailer\Mail */ - public function from(string $address, string $fullName = null): Mail + public function from(string $address, string $fullName = ''): Mail { $this->message->from(new Address($address, $fullName)); @@ -70,10 +70,10 @@ public function from(string $address, string $fullName = null): Mail /** * @param string $address - * @param string|null $fullName + * @param string $fullName * @return \App\Service\Mailer\Mail */ - public function to(string $address, string $fullName = null): Mail + public function to(string $address, string $fullName = ''): Mail { $this->message->to(new Address($address, $fullName)); @@ -82,10 +82,10 @@ public function to(string $address, string $fullName = null): Mail /** * @param string $address - * @param string|null $fullName + * @param string $fullName * @return \App\Service\Mailer\Mail */ - public function replyTo(string $address, string $fullName = null): Mail + public function replyTo(string $address, string $fullName = ''): Mail { $this->message->replyTo(new Address($address, $fullName)); @@ -94,10 +94,10 @@ public function replyTo(string $address, string $fullName = null): Mail /** * @param string $address - * @param string|null $fullName + * @param string $fullName * @return \App\Service\Mailer\Mail */ - public function cc(string $address, string $fullName = null): Mail + public function cc(string $address, string $fullName = ''): Mail { $this->message->cc(new Address($address, $fullName)); @@ -106,10 +106,10 @@ public function cc(string $address, string $fullName = null): Mail /** * @param string $address - * @param string|null $fullName + * @param string $fullName * @return \App\Service\Mailer\Mail */ - public function bcc(string $address, string $fullName = null): Mail + public function bcc(string $address, string $fullName = ''): Mail { $this->message->bcc(new Address($address, $fullName)); diff --git a/app/src/Service/Mailer/Mailer.php b/app/src/Service/Mailer/Mailer.php index 992fb2b..bbcac26 100644 --- a/app/src/Service/Mailer/Mailer.php +++ b/app/src/Service/Mailer/Mailer.php @@ -84,7 +84,7 @@ public function send(Mail $mail): void */ public function render(Mail $mail): string { - return $this->build($mail)->getHtmlBody(); + return (string) $this->build($mail)->getHtmlBody(); } /** diff --git a/app/src/Service/Pagination/PaginationFactory.php b/app/src/Service/Pagination/PaginationFactory.php index dd6a4ea..063dfb7 100644 --- a/app/src/Service/Pagination/PaginationFactory.php +++ b/app/src/Service/Pagination/PaginationFactory.php @@ -64,7 +64,7 @@ public function createPaginator(string $parameter = 'page', int $limit = 25): Pa $paginator = $this->factory->make(Paginator::class, compact('limit', 'parameter')); - if ($paginator === null) { + if (! $paginator instanceof Paginator) { throw new \RuntimeException('Unable to resolve paginator'); } diff --git a/app/src/Service/Statistics/ProfileStatistics.php b/app/src/Service/Statistics/ProfileStatistics.php index 7e83342..0468be8 100644 --- a/app/src/Service/Statistics/ProfileStatistics.php +++ b/app/src/Service/Statistics/ProfileStatistics.php @@ -70,7 +70,7 @@ public function getChargeFlow(User $user, Currency $currency): array foreach ($metrics as $metricName => $dateFrom) { foreach ($data as $type => &$value) { - if ($dateFrom === false) { + if ($dateFrom === null) { continue; } diff --git a/phpunit.xml b/phpunit.xml index 1d73a68..b7d2a9a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -9,7 +9,7 @@ convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" - processIsolation="false" + processIsolation="true" stopOnFailure="false" stopOnError="false" stderr="true"> From edf915aa94e6e900e48cf7155ab03807402b4c72 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sun, 19 Mar 2023 17:22:41 +0200 Subject: [PATCH 04/20] Fix memory leak in S3 client --- app/src/Controller/Wallets/ListController.php | 6 +- composer.lock | 127 ++++++++++++------ phpunit.xml | 2 +- tests/TestCase.php | 38 ++++-- 4 files changed, 119 insertions(+), 54 deletions(-) diff --git a/app/src/Controller/Wallets/ListController.php b/app/src/Controller/Wallets/ListController.php index 77b8297..4ec4451 100644 --- a/app/src/Controller/Wallets/ListController.php +++ b/app/src/Controller/Wallets/ListController.php @@ -36,7 +36,7 @@ public function list(): ResponseInterface return $this->walletsView->json($this->walletRepository->findAllByUserPK((int) $this->user->id)); } - #[Route(route: '/wallets/unarchived', name: 'wallet.list.unarchived', methods: 'GET', group: 'auth', priority: 0)] + #[Route(route: '/wallets/unarchived', name: 'wallet.list.unarchived', methods: 'GET', group: 'auth', priority: -1)] public function listUnArchived(): ResponseInterface { return $this->walletsView->json( @@ -45,7 +45,7 @@ public function listUnArchived(): ResponseInterface ); } - #[Route(route: '/wallets/unarchived/sort', name: 'wallet.sort.unarchived.set', methods: 'POST', group: 'auth', priority: 0)] + #[Route(route: '/wallets/unarchived/sort', name: 'wallet.sort.unarchived.set', methods: 'POST', group: 'auth')] public function sortUnArchived(SortSetRequest $request): ResponseInterface { if (! $request->isValid()) { @@ -63,7 +63,7 @@ public function sortUnArchived(SortSetRequest $request): ResponseInterface return $this->response->create(200); } - #[Route(route: '/wallets/archived', name: 'wallet.list.archived', methods: 'GET', group: 'auth', priority: 0)] + #[Route(route: '/wallets/archived', name: 'wallet.list.archived', methods: 'GET', group: 'auth', priority: -1)] public function listArchived(): ResponseInterface { return $this->walletsView->json( diff --git a/composer.lock b/composer.lock index d2786d4..4dbf887 100644 --- a/composer.lock +++ b/composer.lock @@ -58,16 +58,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.261.9", + "version": "3.261.14", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "d91e6f89a6c41e8df859b0d313150386cfee6f6a" + "reference": "121638bb7e62ee2f71838c52e79884f4301a9400" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d91e6f89a6c41e8df859b0d313150386cfee6f6a", - "reference": "d91e6f89a6c41e8df859b0d313150386cfee6f6a", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/121638bb7e62ee2f71838c52e79884f4301a9400", + "reference": "121638bb7e62ee2f71838c52e79884f4301a9400", "shasum": "" }, "require": { @@ -146,9 +146,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.261.9" + "source": "https://github.com/aws/aws-sdk-php/tree/3.261.14" }, - "time": "2023-03-10T19:25:08+00:00" + "time": "2023-03-17T18:21:00+00:00" }, { "name": "beste/clock", @@ -3616,23 +3616,24 @@ }, { "name": "nette/php-generator", - "version": "v4.0.5", + "version": "v4.0.6", "source": { "type": "git", "url": "https://github.com/nette/php-generator.git", - "reference": "a8d6abeae5d8c7202cd69600e086a7a72877fc86" + "reference": "0f1275bb8d39b3eb92b57c22a51fe693f1f145a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/php-generator/zipball/a8d6abeae5d8c7202cd69600e086a7a72877fc86", - "reference": "a8d6abeae5d8c7202cd69600e086a7a72877fc86", + "url": "https://api.github.com/repos/nette/php-generator/zipball/0f1275bb8d39b3eb92b57c22a51fe693f1f145a5", + "reference": "0f1275bb8d39b3eb92b57c22a51fe693f1f145a5", "shasum": "" }, "require": { - "nette/utils": "^3.2.7 || ^4.0", + "nette/utils": "^3.2.9 || ^4.0", "php": ">=8.0 <8.3" }, "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", "nette/tester": "^2.4", "nikic/php-parser": "^4.15", "phpstan/phpstan": "^1.0", @@ -3678,9 +3679,9 @@ ], "support": { "issues": "https://github.com/nette/php-generator/issues", - "source": "https://github.com/nette/php-generator/tree/v4.0.5" + "source": "https://github.com/nette/php-generator/tree/v4.0.6" }, - "time": "2022-11-02T20:37:46+00:00" + "time": "2023-03-13T17:38:30+00:00" }, { "name": "nette/utils", @@ -5294,16 +5295,16 @@ }, { "name": "spiral/attributes", - "version": "v3.0.0", + "version": "v3.1.0", "source": { "type": "git", "url": "https://github.com/spiral/attributes.git", - "reference": "442712c83c703366856d7da7370a95b471f3b51e" + "reference": "b1c3f9cd8b7b0632f03e7046a38c2a98f3ed2e65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spiral/attributes/zipball/442712c83c703366856d7da7370a95b471f3b51e", - "reference": "442712c83c703366856d7da7370a95b471f3b51e", + "url": "https://api.github.com/repos/spiral/attributes/zipball/b1c3f9cd8b7b0632f03e7046a38c2a98f3ed2e65", + "reference": "b1c3f9cd8b7b0632f03e7046a38c2a98f3ed2e65", "shasum": "" }, "require": { @@ -5312,7 +5313,7 @@ "psr/simple-cache": "1 - 3" }, "require-dev": { - "doctrine/annotations": "^1.12", + "doctrine/annotations": "^1.12 || ^2.0", "jetbrains/phpstorm-attributes": "^1.0", "phpunit/phpunit": "^9.5.20", "symfony/var-dumper": "^5.2 || ^6.0", @@ -5324,7 +5325,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.14.x-dev" + "dev-master": "3.1.x-dev" } }, "autoload": { @@ -5351,7 +5352,7 @@ "issues": "https://github.com/spiral/attributes/issues", "source": "https://github.com/spiral/attributes" }, - "time": "2022-05-26T15:05:35+00:00" + "time": "2023-03-15T07:25:15+00:00" }, { "name": "spiral/composer-publish-plugin", @@ -6345,16 +6346,16 @@ }, { "name": "spiral/roadrunner-jobs", - "version": "v2.8.0", + "version": "v2.8.1", "source": { "type": "git", "url": "https://github.com/spiral/roadrunner-jobs.git", - "reference": "47f2e1a85f26df9072af67162b1b026a0835d8c6" + "reference": "948a45293365d831c7cb00f3d9803e0c7326659f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spiral/roadrunner-jobs/zipball/47f2e1a85f26df9072af67162b1b026a0835d8c6", - "reference": "47f2e1a85f26df9072af67162b1b026a0835d8c6", + "url": "https://api.github.com/repos/spiral/roadrunner-jobs/zipball/948a45293365d831c7cb00f3d9803e0c7326659f", + "reference": "948a45293365d831c7cb00f3d9803e0c7326659f", "shasum": "" }, "require": { @@ -6410,9 +6411,9 @@ "description": "RoadRunner Queues (Jobs) plugin API library", "support": { "issues": "https://github.com/spiral/roadrunner-jobs/issues", - "source": "https://github.com/spiral/roadrunner-jobs/tree/v2.8.0" + "source": "https://github.com/spiral/roadrunner-jobs/tree/v2.8.1" }, - "time": "2023-02-24T09:41:12+00:00" + "time": "2023-03-14T07:58:17+00:00" }, { "name": "spiral/roadrunner-kv", @@ -6662,16 +6663,16 @@ }, { "name": "spiral/validator", - "version": "1.2.1", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/spiral/validator.git", - "reference": "3bf210249145fc30f1db42b7a192ffe228d1720f" + "reference": "f26e6c83c98f0c302c2c64851e37defd4aa4387f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spiral/validator/zipball/3bf210249145fc30f1db42b7a192ffe228d1720f", - "reference": "3bf210249145fc30f1db42b7a192ffe228d1720f", + "url": "https://api.github.com/repos/spiral/validator/zipball/f26e6c83c98f0c302c2c64851e37defd4aa4387f", + "reference": "f26e6c83c98f0c302c2c64851e37defd4aa4387f", "shasum": "" }, "require": { @@ -6711,7 +6712,7 @@ "issues": "https://github.com/spiral/validator/issues", "source": "https://github.com/spiral/validator" }, - "time": "2023-02-23T12:19:47+00:00" + "time": "2023-03-17T11:34:10+00:00" }, { "name": "symfony/console", @@ -9980,24 +9981,27 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.2", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d" + "reference": "1534aea9bde19a5c85c5d1e1f834ab63f4c5dcf5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d", - "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/1534aea9bde19a5c85c5d1e1f834ab63f4c5dcf5", + "reference": "1534aea9bde19a5c85c5d1e1f834ab63f4c5dcf5", "shasum": "" }, "require": { + "doctrine/deprecations": "^1.0", "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" }, "require-dev": { "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.1", @@ -10029,9 +10033,54 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.0" + }, + "time": "2023-03-12T10:13:29+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.16.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571", + "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.16.1" }, - "time": "2022-10-14T12:47:21+00:00" + "time": "2023-02-07T18:11:17+00:00" }, { "name": "phpunit/php-code-coverage", @@ -11921,5 +11970,5 @@ "ext-sockets": "*" }, "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } diff --git a/phpunit.xml b/phpunit.xml index b7d2a9a..1d73a68 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -9,7 +9,7 @@ convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" - processIsolation="true" + processIsolation="false" stopOnFailure="false" stopOnError="false" stderr="true"> diff --git a/tests/TestCase.php b/tests/TestCase.php index ad07936..beaddf1 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,8 +4,9 @@ namespace Tests; +use Aws\S3\S3ClientInterface; use Cycle\Database\DatabaseInterface; -use Cycle\Database\DatabaseManager; +use Spiral\Boot\FinalizerInterface; use Spiral\Config\ConfiguratorInterface; use Spiral\Config\Patch\Set; use Spiral\Core\Container; @@ -27,6 +28,11 @@ abstract class TestCase extends BaseTestCase use InteractsWithMock; use AssertHelpers; + protected function getFakeS3(): S3ClientInterface + { + return $this->getMockBuilder(S3ClientInterface::class)->getMock(); + } + protected function setUp(): void { $this->beforeBooting(static function (ConfiguratorInterface $config): void { @@ -39,25 +45,35 @@ protected function setUp(): void parent::setUp(); + $this->getContainer()->bindSingleton(S3ClientInterface::class, $this->getFakeS3()); + $this->getContainer()->get(TranslatorInterface::class)->setLocale('en'); - if ($this instanceof DatabaseTransaction) { - $this->getContainer()->get(DatabaseInterface::class)->begin(); - } + $this->scopedDatabase(); } - protected function tearDown(): void + /** + * @return void + * @throws \Throwable + */ + protected function scopedDatabase(): void { - if ($this instanceof DatabaseTransaction) { - $this->getContainer()->get(DatabaseInterface::class)->rollback(); + if (! $this instanceof DatabaseTransaction) { + return; } + /** @var \Cycle\Database\DatabaseInterface $db */ + $db = $this->getContainer()->get(DatabaseInterface::class); + + $this->getContainer()->get(FinalizerInterface::class)->addFinalizer(static function () use ($db) { + $db->rollback(); + }); + } + + protected function tearDown(): void + { // Uncomment this line if you want to clean up runtime directory. // $this->cleanUpRuntimeDirectory(); - - foreach ($this->getContainer()->get(DatabaseManager::class)->getDrivers() as $driver) { - $driver->disconnect(); - } } public function rootDirectory(): string From b35655f6cc7c18bd1dab8eac2027ac29cbefff9b Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sun, 19 Mar 2023 19:30:31 +0200 Subject: [PATCH 05/20] Fix memory leak in container and testable app --- tests/TestCase.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/TestCase.php b/tests/TestCase.php index beaddf1..14152af 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -72,6 +72,17 @@ protected function scopedDatabase(): void protected function tearDown(): void { + parent::tearDown(); + + $container = $this->app->getContainer(); + unset($this->app); + + if ($container instanceof Container) { + $container->destruct(); + } + + unset($container); + // Uncomment this line if you want to clean up runtime directory. // $this->cleanUpRuntimeDirectory(); } From 488d5ac388fd0992d74a96cebd6335e872f143f0 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sun, 19 Mar 2023 19:54:20 +0200 Subject: [PATCH 06/20] Debug unit tests failure in CI --- phpunit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml b/phpunit.xml index 1d73a68..f0d93b7 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,7 +10,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false" + stopOnFailure="true" stopOnError="false" stderr="true"> From b455570f7a3fe3005e81d77e024e51e4c00393d3 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sun, 19 Mar 2023 20:01:32 +0200 Subject: [PATCH 07/20] Debug unit tests failure in CI --- tests/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index 14152af..9ac59b8 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -74,7 +74,7 @@ protected function tearDown(): void { parent::tearDown(); - $container = $this->app->getContainer(); + $container = $this->getContainer(); unset($this->app); if ($container instanceof Container) { From b788d472cc343729059fd0d1b7cfc8bf8bfed06b Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sun, 19 Mar 2023 20:53:34 +0200 Subject: [PATCH 08/20] Debug unit tests failure in CI --- phpunit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml b/phpunit.xml index f0d93b7..1d73a68 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,7 +10,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="true" + stopOnFailure="false" stopOnError="false" stderr="true"> From ae7e86839b6575f7788669256d2363d6483cc1fa Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sun, 19 Mar 2023 21:19:57 +0200 Subject: [PATCH 09/20] Debug unit tests failure in CI --- tests/Feature/Controller/Wallets/UsersControllerTest.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/Feature/Controller/Wallets/UsersControllerTest.php b/tests/Feature/Controller/Wallets/UsersControllerTest.php index 191fbee..f1e1104 100644 --- a/tests/Feature/Controller/Wallets/UsersControllerTest.php +++ b/tests/Feature/Controller/Wallets/UsersControllerTest.php @@ -8,7 +8,6 @@ use App\Service\WalletService; use PHPUnit\Framework\MockObject\MockObject; use Tests\DatabaseTransaction; -use Tests\Factories\ChargeFactory; use Tests\Factories\UserFactory; use Tests\Factories\WalletFactory; use Tests\Fixtures; @@ -20,8 +19,6 @@ class UsersControllerTest extends TestCase implements DatabaseTransaction protected WalletFactory $walletFactory; - protected ChargeFactory $chargeFactory; - protected function setUp(): void { parent::setUp(); @@ -132,7 +129,7 @@ public function testPatchNotExistingUserReturnNotFound(): void $auth = $this->makeAuth($user = $this->userFactory->create()); $wallet = $this->walletFactory->forUser($user)->create(); - $otherUserId = Fixtures::integer(); + $otherUserId = Fixtures::integer(100, 1000); $response = $this->withAuth($auth)->patch("/wallets/{$wallet->id}/users/{$otherUserId}"); @@ -234,7 +231,7 @@ public function testDeleteNotExistingUserReturnNotFound(): void $auth = $this->makeAuth($user = $this->userFactory->create()); $wallet = $this->walletFactory->forUser($user)->create(); - $otherUserId = Fixtures::integer(); + $otherUserId = Fixtures::integer(100, 1000); $response = $this->withAuth($auth)->delete("/wallets/{$wallet->id}/users/{$otherUserId}"); From 4b20fd8b5e6f3062c993a7a05fce8cbe3c39c74c Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sun, 19 Mar 2023 23:17:11 +0200 Subject: [PATCH 10/20] Debug unit tests failure in CI --- app/src/App.php | 1 - app/src/Controller/Auth/AuthResponses.php | 26 ++-------- .../Controller/Wallets/UsersController.php | 4 ++ app/src/Exception/ViewRenderer.php | 47 +------------------ psalm.xml | 1 + .../Wallets/UsersControllerTest.php | 4 +- tests/Feature/Exception/ViewRendererTest.php | 32 +++++++++++++ 7 files changed, 44 insertions(+), 71 deletions(-) create mode 100644 tests/Feature/Exception/ViewRendererTest.php diff --git a/app/src/App.php b/app/src/App.php index 1f27ca0..42c133d 100644 --- a/app/src/App.php +++ b/app/src/App.php @@ -72,7 +72,6 @@ class App extends Kernel Framework\Http\SessionBootloader::class, Framework\Http\CsrfBootloader::class, Framework\Http\PaginationBootloader::class, - Framework\Http\ErrorHandlerBootloader::class, Bootloader\CorsBootloader::class, Router\AnnotatedRoutesBootloader::class, diff --git a/app/src/Controller/Auth/AuthResponses.php b/app/src/Controller/Auth/AuthResponses.php index 52876aa..b70df8d 100644 --- a/app/src/Controller/Auth/AuthResponses.php +++ b/app/src/Controller/Auth/AuthResponses.php @@ -5,23 +5,11 @@ namespace App\Controller\Auth; use App\Database\User; -use App\View\UserView; use Psr\Http\Message\ResponseInterface; use Spiral\Auth\TokenInterface; -use Spiral\Http\ResponseWrapper; trait AuthResponses { - /** - * @param \Spiral\Http\ResponseWrapper $response - * @param \App\View\UserView $userView - */ - public function __construct( - protected ResponseWrapper $response, - protected UserView $userView, - ) { - } - /** * @param \Spiral\Auth\TokenInterface $accessToken * @param \Spiral\Auth\TokenInterface $refreshToken @@ -29,14 +17,11 @@ public function __construct( */ protected function responseTokens(TokenInterface $accessToken, TokenInterface $refreshToken): ResponseInterface { - $accessTokenExpiredAt = $accessToken->getExpiresAt(); - $refreshTokenExpiredAt = $refreshToken->getExpiresAt(); - return $this->response->json([ 'accessToken' => $accessToken->getID(), - 'accessTokenExpiredAt' => $accessTokenExpiredAt ? $accessTokenExpiredAt->format(DATE_RFC3339) : null, + 'accessTokenExpiredAt' => $accessToken->getExpiresAt()?->format(DATE_RFC3339), 'refreshToken' => $refreshToken->getID(), - 'refreshTokenExpiredAt' => $refreshTokenExpiredAt ? $refreshTokenExpiredAt->format(DATE_RFC3339) : null, + 'refreshTokenExpiredAt' => $refreshToken->getExpiresAt()?->format(DATE_RFC3339), ], 200); } @@ -48,15 +33,12 @@ protected function responseTokens(TokenInterface $accessToken, TokenInterface $r */ protected function responseTokensWithUser(TokenInterface $accessToken, TokenInterface $refreshToken, User $user): ResponseInterface { - $accessTokenExpiredAt = $accessToken->getExpiresAt(); - $refreshTokenExpiredAt = $refreshToken->getExpiresAt(); - return $this->response->json([ 'data' => $this->userView->head($user), 'accessToken' => $accessToken->getID(), - 'accessTokenExpiredAt' => $accessTokenExpiredAt ? $accessTokenExpiredAt->format(DATE_RFC3339) : null, + 'accessTokenExpiredAt' => $accessToken->getExpiresAt()?->format(DATE_RFC3339), 'refreshToken' => $refreshToken->getID(), - 'refreshTokenExpiredAt' => $refreshTokenExpiredAt ? $refreshTokenExpiredAt->format(DATE_RFC3339) : null, + 'refreshTokenExpiredAt' => $refreshToken->getExpiresAt()?->format(DATE_RFC3339), ], 200); } diff --git a/app/src/Controller/Wallets/UsersController.php b/app/src/Controller/Wallets/UsersController.php index 11024c6..b3403c1 100644 --- a/app/src/Controller/Wallets/UsersController.php +++ b/app/src/Controller/Wallets/UsersController.php @@ -65,6 +65,8 @@ public function patch(string $id, string $userId): ResponseInterface /** @var \App\Database\User|null $user */ $user = $this->userRepository->findByPK((int) $userId); + var_dump(gettype($user)); + if (! $user instanceof User) { return $this->response->create(404); } @@ -107,6 +109,8 @@ public function delete(string $id, string $userId): ResponseInterface /** @var \App\Database\User|null $user */ $user = $this->userRepository->findByPK((int) $userId); + var_dump(gettype($user)); + if (! $user instanceof User) { return $this->response->create(404); } diff --git a/app/src/Exception/ViewRenderer.php b/app/src/Exception/ViewRenderer.php index 2d021d8..d375b62 100644 --- a/app/src/Exception/ViewRenderer.php +++ b/app/src/Exception/ViewRenderer.php @@ -7,41 +7,18 @@ use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface as Request; -use Spiral\Exceptions\Verbosity; use Spiral\Http\ErrorHandler\RendererInterface; -use Spiral\Http\Header\AcceptHeader; -use Spiral\Views\Exception\ViewException; -use Spiral\Views\ViewsInterface; -/** - * This Renderer is to allow exceptions to be rendered as HTTP responses, either in JSON format if the request - * accepts JSON, or as a view if the request accepts HTML. - * This renderer is intended to be used exclusively by the {@see \Spiral\Http\Middleware\ErrorHandlerMiddleware} - * middleware and will only be active if the DEBUG environment variable is set to false. - * - * @link https://spiral.dev/docs/http-errors - */ final class ViewRenderer implements RendererInterface { - private const DEFAULT_VIEW = 'exception/error'; - private const VIEW_PATTERN = 'exception/%s'; - public function __construct( - private readonly ViewsInterface $views, private readonly ResponseFactoryInterface $responseFactory, - private readonly Verbosity $verbosity, ) { } public function renderException(Request $request, int $code, \Throwable $exception): ResponseInterface { - // If request accepts json, we will render as a json response - $acceptItems = AcceptHeader::fromString($request->getHeaderLine('Accept'))->getAll(); - if ($acceptItems && $acceptItems[0]->getValue() === 'application/json') { - return $this->renderJson($code, $exception); - } - - return $this->renderView($code, $exception); + return $this->renderJson($code, $exception); } private function renderJson(int $code, \Throwable $exception): ResponseInterface @@ -55,30 +32,8 @@ private function renderJson(int $code, \Throwable $exception): ResponseInterface 'error' => $exception->getMessage(), ]; - if ($this->verbosity->value > Verbosity::BASIC->value) { - $payload['stacktrace'] = $exception->getTraceAsString(); - } - $response->getBody()->write(\json_encode($payload)); return $response; } - - private function renderView(int $code, \Throwable $exception): ResponseInterface - { - $response = $this->responseFactory->createResponse($code); - - try { - // Try to find view for specific exception code - $view = $this->views->get(\sprintf(self::VIEW_PATTERN, $code)); - } catch (ViewException) { - // Fallback to default view in case if specific view not found - $view = $this->views->get(self::DEFAULT_VIEW); - } - - $content = $view->render(['code' => $code, 'exception' => $exception]); - $response->getBody()->write($content); - - return $response; - } } diff --git a/psalm.xml b/psalm.xml index 61b1a27..69a14c3 100644 --- a/psalm.xml +++ b/psalm.xml @@ -9,6 +9,7 @@ + diff --git a/tests/Feature/Controller/Wallets/UsersControllerTest.php b/tests/Feature/Controller/Wallets/UsersControllerTest.php index f1e1104..01561cd 100644 --- a/tests/Feature/Controller/Wallets/UsersControllerTest.php +++ b/tests/Feature/Controller/Wallets/UsersControllerTest.php @@ -129,7 +129,7 @@ public function testPatchNotExistingUserReturnNotFound(): void $auth = $this->makeAuth($user = $this->userFactory->create()); $wallet = $this->walletFactory->forUser($user)->create(); - $otherUserId = Fixtures::integer(100, 1000); + $otherUserId = Fixtures::integer(); $response = $this->withAuth($auth)->patch("/wallets/{$wallet->id}/users/{$otherUserId}"); @@ -231,7 +231,7 @@ public function testDeleteNotExistingUserReturnNotFound(): void $auth = $this->makeAuth($user = $this->userFactory->create()); $wallet = $this->walletFactory->forUser($user)->create(); - $otherUserId = Fixtures::integer(100, 1000); + $otherUserId = Fixtures::integer(); $response = $this->withAuth($auth)->delete("/wallets/{$wallet->id}/users/{$otherUserId}"); diff --git a/tests/Feature/Exception/ViewRendererTest.php b/tests/Feature/Exception/ViewRendererTest.php new file mode 100644 index 0000000..b8d84ba --- /dev/null +++ b/tests/Feature/Exception/ViewRendererTest.php @@ -0,0 +1,32 @@ +makeAuth($this->getContainer()->get(UserFactory::class)->create()); + + $repoMock = $this->getMockBuilder(CurrencyRepository::class)->onlyMethods(['findAll'])->disableOriginalConstructor()->getMock(); + $repoMock->expects($this->once())->method('findAll')->willThrowException(new \RuntimeException('Error')); + + $this->getContainer()->bind(CurrencyRepository::class, $repoMock); + + $response = $this->withAuth($auth)->get('/currencies'); + + $body = $this->getJsonResponseBody($response); + + $response->assertStatus(500); + $this->assertArrayHasKey('status', $body); + $this->assertArrayHasKey('error', $body); + $this->assertEquals(500, $body['status']); + $this->assertEquals('Error', $body['error']); + } +} From 1e3134b5d0d534919a9aa1ee8e26accac77a8f1d Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Mon, 20 Mar 2023 10:01:25 +0200 Subject: [PATCH 11/20] Debug unit tests failure in CI --- app/src/Controller/Wallets/UsersController.php | 4 ++-- phpunit.xml | 2 +- tests/Feature/Controller/Wallets/UsersControllerTest.php | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/Controller/Wallets/UsersController.php b/app/src/Controller/Wallets/UsersController.php index b3403c1..3671432 100644 --- a/app/src/Controller/Wallets/UsersController.php +++ b/app/src/Controller/Wallets/UsersController.php @@ -65,7 +65,7 @@ public function patch(string $id, string $userId): ResponseInterface /** @var \App\Database\User|null $user */ $user = $this->userRepository->findByPK((int) $userId); - var_dump(gettype($user)); + $this->logger->info("Debug patch user => " . print_r($user, true)); if (! $user instanceof User) { return $this->response->create(404); @@ -109,7 +109,7 @@ public function delete(string $id, string $userId): ResponseInterface /** @var \App\Database\User|null $user */ $user = $this->userRepository->findByPK((int) $userId); - var_dump(gettype($user)); + $this->logger->info("Debug delete user => " . print_r($user, true)); if (! $user instanceof User) { return $this->response->create(404); diff --git a/phpunit.xml b/phpunit.xml index 1d73a68..dcd5a29 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -33,7 +33,7 @@ - + diff --git a/tests/Feature/Controller/Wallets/UsersControllerTest.php b/tests/Feature/Controller/Wallets/UsersControllerTest.php index 01561cd..4baa840 100644 --- a/tests/Feature/Controller/Wallets/UsersControllerTest.php +++ b/tests/Feature/Controller/Wallets/UsersControllerTest.php @@ -133,6 +133,8 @@ public function testPatchNotExistingUserReturnNotFound(): void $response = $this->withAuth($auth)->patch("/wallets/{$wallet->id}/users/{$otherUserId}"); + print_r($this->getJsonResponseBody($response)); + $response->assertNotFound(); } @@ -235,6 +237,8 @@ public function testDeleteNotExistingUserReturnNotFound(): void $response = $this->withAuth($auth)->delete("/wallets/{$wallet->id}/users/{$otherUserId}"); + print_r($this->getJsonResponseBody($response)); + $response->assertNotFound(); } From 6e2b158ba9e371336d677a76a9bd57122033d6a1 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Mon, 20 Mar 2023 14:35:37 +0200 Subject: [PATCH 12/20] Debug unit tests failure in CI --- app/src/Controller/Wallets/UsersController.php | 4 ++-- .../Controller/Wallets/UsersControllerTest.php | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/Controller/Wallets/UsersController.php b/app/src/Controller/Wallets/UsersController.php index 3671432..d2170a8 100644 --- a/app/src/Controller/Wallets/UsersController.php +++ b/app/src/Controller/Wallets/UsersController.php @@ -65,7 +65,7 @@ public function patch(string $id, string $userId): ResponseInterface /** @var \App\Database\User|null $user */ $user = $this->userRepository->findByPK((int) $userId); - $this->logger->info("Debug patch user => " . print_r($user, true)); + $this->logger->info("Debug patch user {$userId} => " . print_r($user, true)); if (! $user instanceof User) { return $this->response->create(404); @@ -109,7 +109,7 @@ public function delete(string $id, string $userId): ResponseInterface /** @var \App\Database\User|null $user */ $user = $this->userRepository->findByPK((int) $userId); - $this->logger->info("Debug delete user => " . print_r($user, true)); + $this->logger->info("Debug delete user {$userId} => " . print_r($user, true)); if (! $user instanceof User) { return $this->response->create(404); diff --git a/tests/Feature/Controller/Wallets/UsersControllerTest.php b/tests/Feature/Controller/Wallets/UsersControllerTest.php index 4baa840..bb3ffa7 100644 --- a/tests/Feature/Controller/Wallets/UsersControllerTest.php +++ b/tests/Feature/Controller/Wallets/UsersControllerTest.php @@ -129,11 +129,13 @@ public function testPatchNotExistingUserReturnNotFound(): void $auth = $this->makeAuth($user = $this->userFactory->create()); $wallet = $this->walletFactory->forUser($user)->create(); - $otherUserId = Fixtures::integer(); + $otherUserId = Fixtures::integer() * -1; - $response = $this->withAuth($auth)->patch("/wallets/{$wallet->id}/users/{$otherUserId}"); + $this->mock(MailerInterface::class, ['send', 'render'], function (MockObject $mock) { + $mock->expects($this->never())->method('send'); + }); - print_r($this->getJsonResponseBody($response)); + $response = $this->withAuth($auth)->patch("/wallets/{$wallet->id}/users/{$otherUserId}"); $response->assertNotFound(); } @@ -233,12 +235,10 @@ public function testDeleteNotExistingUserReturnNotFound(): void $auth = $this->makeAuth($user = $this->userFactory->create()); $wallet = $this->walletFactory->forUser($user)->create(); - $otherUserId = Fixtures::integer(); + $otherUserId = Fixtures::integer() * -1; $response = $this->withAuth($auth)->delete("/wallets/{$wallet->id}/users/{$otherUserId}"); - print_r($this->getJsonResponseBody($response)); - $response->assertNotFound(); } From e6e05e8c345d95e65e53c107ebab2c5162f316cc Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Mon, 20 Mar 2023 16:17:19 +0200 Subject: [PATCH 13/20] Debug unit tests failure in CI --- .../{AuthResponses.php => Controller.php} | 27 +++++++------------ app/src/Controller/Auth/LoginController.php | 5 ++-- app/src/Controller/Auth/RefreshController.php | 8 +++--- .../Controller/Auth/RegisterController.php | 5 ++-- .../Controller/Wallets/UsersController.php | 4 --- phpunit.xml | 2 +- 6 files changed, 19 insertions(+), 32 deletions(-) rename app/src/Controller/Auth/{AuthResponses.php => Controller.php} (74%) diff --git a/app/src/Controller/Auth/AuthResponses.php b/app/src/Controller/Auth/Controller.php similarity index 74% rename from app/src/Controller/Auth/AuthResponses.php rename to app/src/Controller/Auth/Controller.php index b70df8d..59ca449 100644 --- a/app/src/Controller/Auth/AuthResponses.php +++ b/app/src/Controller/Auth/Controller.php @@ -5,16 +5,19 @@ namespace App\Controller\Auth; use App\Database\User; +use App\View\UserView; use Psr\Http\Message\ResponseInterface; use Spiral\Auth\TokenInterface; +use Spiral\Http\ResponseWrapper; -trait AuthResponses +abstract class Controller { - /** - * @param \Spiral\Auth\TokenInterface $accessToken - * @param \Spiral\Auth\TokenInterface $refreshToken - * @return \Psr\Http\Message\ResponseInterface - */ + public function __construct( + protected UserView $userView, + protected ResponseWrapper $response, + ) { + } + protected function responseTokens(TokenInterface $accessToken, TokenInterface $refreshToken): ResponseInterface { return $this->response->json([ @@ -25,12 +28,6 @@ protected function responseTokens(TokenInterface $accessToken, TokenInterface $r ], 200); } - /** - * @param \Spiral\Auth\TokenInterface $accessToken - * @param \Spiral\Auth\TokenInterface $refreshToken - * @param \App\Database\User $user - * @return \Psr\Http\Message\ResponseInterface - */ protected function responseTokensWithUser(TokenInterface $accessToken, TokenInterface $refreshToken, User $user): ResponseInterface { return $this->response->json([ @@ -42,9 +39,6 @@ protected function responseTokensWithUser(TokenInterface $accessToken, TokenInte ], 200); } - /** - * @return \Psr\Http\Message\ResponseInterface - */ protected function responseAuthenticationFailure(): ResponseInterface { return $this->response->json([ @@ -52,9 +46,6 @@ protected function responseAuthenticationFailure(): ResponseInterface ], 400); } - /** - * @return \Psr\Http\Message\ResponseInterface - */ protected function responseUnauthenticated(): ResponseInterface { return $this->response->json([ diff --git a/app/src/Controller/Auth/LoginController.php b/app/src/Controller/Auth/LoginController.php index e072e56..c9d5744 100644 --- a/app/src/Controller/Auth/LoginController.php +++ b/app/src/Controller/Auth/LoginController.php @@ -14,10 +14,8 @@ use Spiral\Http\ResponseWrapper; use Spiral\Router\Annotation\Route; -final class LoginController +final class LoginController extends Controller { - use AuthResponses; - /** * @param \App\View\UserView $userView * @param \App\Service\Auth\AuthService $authService @@ -32,6 +30,7 @@ public function __construct( protected UserRepository $userRepository, protected RefreshTokenService $refreshTokenService, ) { + parent::__construct($userView, $response); } #[Route(route: '/auth/login', name: 'auth.login', methods: 'POST')] diff --git a/app/src/Controller/Auth/RefreshController.php b/app/src/Controller/Auth/RefreshController.php index fa58565..1b89fdb 100644 --- a/app/src/Controller/Auth/RefreshController.php +++ b/app/src/Controller/Auth/RefreshController.php @@ -8,26 +8,28 @@ use App\Request\RefreshTokenRequest; use App\Service\Auth\AuthService; use App\Service\Auth\RefreshTokenService; +use App\View\UserView; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Spiral\Auth\TokenInterface; use Spiral\Http\ResponseWrapper; use Spiral\Router\Annotation\Route; -final class RefreshController +final class RefreshController extends Controller { - use AuthResponses; - /** + * @param \App\View\UserView $userView * @param \App\Service\Auth\AuthService $authService * @param \Spiral\Http\ResponseWrapper $response * @param \App\Service\Auth\RefreshTokenService $refreshTokenService */ public function __construct( + protected UserView $userView, protected AuthService $authService, protected ResponseWrapper $response, protected RefreshTokenService $refreshTokenService, ) { + parent::__construct($userView, $response); } /** diff --git a/app/src/Controller/Auth/RegisterController.php b/app/src/Controller/Auth/RegisterController.php index 28c41cd..73736bf 100644 --- a/app/src/Controller/Auth/RegisterController.php +++ b/app/src/Controller/Auth/RegisterController.php @@ -17,10 +17,8 @@ use Spiral\Http\ResponseWrapper; use Spiral\Router\Annotation\Route; -final class RegisterController +final class RegisterController extends Controller { - use AuthResponses; - /** * @param \App\View\UserView $userView * @param \App\Service\Auth\AuthService $authService @@ -38,6 +36,7 @@ public function __construct( protected RefreshTokenService $refreshTokenService, private CurrencyRepository $currencyRepository, ) { + parent::__construct($userView, $response); } /** diff --git a/app/src/Controller/Wallets/UsersController.php b/app/src/Controller/Wallets/UsersController.php index d2170a8..11024c6 100644 --- a/app/src/Controller/Wallets/UsersController.php +++ b/app/src/Controller/Wallets/UsersController.php @@ -65,8 +65,6 @@ public function patch(string $id, string $userId): ResponseInterface /** @var \App\Database\User|null $user */ $user = $this->userRepository->findByPK((int) $userId); - $this->logger->info("Debug patch user {$userId} => " . print_r($user, true)); - if (! $user instanceof User) { return $this->response->create(404); } @@ -109,8 +107,6 @@ public function delete(string $id, string $userId): ResponseInterface /** @var \App\Database\User|null $user */ $user = $this->userRepository->findByPK((int) $userId); - $this->logger->info("Debug delete user {$userId} => " . print_r($user, true)); - if (! $user instanceof User) { return $this->response->create(404); } diff --git a/phpunit.xml b/phpunit.xml index dcd5a29..1d73a68 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -33,7 +33,7 @@ - + From b8619dc907f8cbffbf438e1901fb36a18b2921c8 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Mon, 20 Mar 2023 22:32:34 +0200 Subject: [PATCH 14/20] Upgrade PHP version to 8.2 --- .env.actions | 11 ++++++- .env.sample | 32 +++++++++++++------ .github/workflows/quality.yml | 14 ++++---- Dockerfile | 24 +++++--------- app/config/cycle.php | 3 ++ app/config/mail.php | 1 - app/src/Config/MailConfig.php | 8 ----- composer.json | 8 ++--- composer.lock | 60 +++++++++++++++++++---------------- 9 files changed, 87 insertions(+), 74 deletions(-) diff --git a/.env.actions b/.env.actions index dfdc615..21058b7 100644 --- a/.env.actions +++ b/.env.actions @@ -1,12 +1,22 @@ DEBUG=false ENCRYPTER_KEY={encrypt-key} SAFE_MIGRATIONS=true +VERBOSITY_LEVEL=basic +MONOLOG_DEFAULT_CHANNEL=default +MONOLOG_DEFAULT_LEVEL=INFO +APP_ENV=testing APP_URL=http://localhost:8080 WEBSITE_URL=http://localhost:3000 WEB_APP_URL=http://localhost:8081 +CYCLE_SCHEMA_CACHE=true +CYCLE_SCHEMA_WARMUP=false CACHE_STORAGE=file +QUEUE_CONNECTION=sync +CACHE_STORAGE=local +STORAGE_DEFAULT=default +SCHEDULER_MUTEX_CACHE_STORAGE=file JWT_SECRET=verysecretphrase JWT_TTL=3600 @@ -32,6 +42,5 @@ MAIL_HOST= MAIL_POST= MAIL_USERNAME= MAIL_PASSWORD= -MAIL_ENCRYPTION= CORS_ALLOWED_ORIGINS="http://localhost:3000,http://localhost:8081" diff --git a/.env.sample b/.env.sample index 6c1fda4..addee67 100644 --- a/.env.sample +++ b/.env.sample @@ -7,28 +7,43 @@ ENCRYPTER_KEY={encrypt-key} # Set to TRUE to disable confirmation in `migrate` commands. SAFE_MIGRATIONS=true +# basic, verbose, debug +VERBOSITY_LEVEL=basic + +# local, testing, stage, prod +APP_ENV=local + +# Use "roadrunner" channel if you want to use RoadRunner logger +MONOLOG_DEFAULT_CHANNEL=default +# DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY +MONOLOG_DEFAULT_LEVEL=DEBUG + APP_URL=http://localhost:8080 WEBSITE_URL=http://localhost:3000 WEB_APP_URL=http://localhost:8081 -CACHE_STORAGE=file +# Store database schema in cache +CYCLE_SCHEMA_CACHE=true + +# Preload ORM providers on boot +CYCLE_SCHEMA_WARMUP=false + +QUEUE_CONNECTION=roadrunner +CACHE_STORAGE=local +STORAGE_DEFAULT=default +SCHEDULER_MUTEX_CACHE_STORAGE=file JWT_SECRET= JWT_TTL=3600 JWT_REFRESH_TTL=604800 -JWT_PUBLIC_KEY="{rsa-public-key}" -JWT_PRIVATE_KEY="{rsa-private-key}" +JWT_PUBLIC_KEY="{rsa-public-key}" # base64 encoded +JWT_PRIVATE_KEY="{rsa-private-key}" # base64 encoded DB_HOST=localhost:33060 DB_NAME=cashtrack DB_USER=cashtrack DB_PASSWORD=secret -DB_OLD_HOST=localhost:33060 -DB_OLD_NAME=production -DB_OLD_USER=root -DB_OLD_PASSWORD=secret - FIREBASE_DATABASE_URI= FIREBASE_STORAGE_BUCKET= FIREBASE_PROJECT_ID= @@ -54,6 +69,5 @@ MAIL_HOST= MAIL_POST= MAIL_USERNAME= MAIL_PASSWORD= -MAIL_ENCRYPTION= CORS_ALLOWED_ORIGINS="http://localhost:3000,http://localhost:8081" diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index b81d94d..d679250 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -20,8 +20,8 @@ jobs: env: runner: self-hosted with: - php-version: '8.1' - extensions: zip, mbstring, pdo_mysql, mysqli + php-version: '8.2' + extensions: zip xsl dom exif intl pcntl bcmath sockets mbstring pdo_mysql mysqli tools: composer - name: Get Composer Cache Directory @@ -39,7 +39,7 @@ jobs: run: composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader - name: Run Psalm - run: ./vendor/bin/psalm --php-version=8.1 --show-info=true --no-cache + run: ./vendor/bin/psalm --php-version=8.2 --show-info=true --no-cache coding-standards: name: Coding Standards @@ -54,8 +54,8 @@ jobs: env: runner: self-hosted with: - php-version: '8.1' - extensions: zip, mbstring, pdo_mysql, mysqli + php-version: '8.2' + extensions: zip xsl dom exif intl pcntl bcmath sockets mbstring pdo_mysql mysqli tools: composer - name: Get Composer Cache Directory @@ -104,9 +104,9 @@ jobs: env: runner: self-hosted with: - php-version: '8.1' + php-version: '8.2' coverage: pcov - extensions: zip, mbstring, pdo_mysql, mysqli + extensions: zip xsl dom exif intl pcntl bcmath sockets mbstring pdo_mysql mysqli tools: composer, phpunit - name: Get Composer Cache Directory diff --git a/Dockerfile b/Dockerfile index 05ed660..32755e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,10 @@ -FROM php:8.1.9-cli +FROM php:8.2.4-alpine3.17 as backend -RUN apt-get update && apt-get install -y --no-install-recommends \ - build-essential \ - nano \ - libzip-dev \ - libonig-dev \ - unzip +RUN --mount=type=bind,from=mlocati/php-extension-installer:1.5,source=/usr/bin/install-php-extensions,target=/usr/local/bin/install-php-extensions \ + install-php-extensions opcache zip xsl dom exif intl pcntl bcmath sockets mbstring pdo_mysql mysqli && \ + apk del --no-cache ${PHPIZE_DEPS} ${BUILD_DEPENDS} -# Install PHP Extensions -RUN docker-php-ext-install zip mbstring pdo_mysql mysqli - -# Clear cache -RUN apt-get clean && rm -rf /var/lib/apt/lists/* - -COPY --from=ghcr.io/roadrunner-server/roadrunner:2.11.1 /usr/bin/rr /usr/bin/rr +COPY --from=ghcr.io/roadrunner-server/roadrunner:2.12.3 /usr/bin/rr /usr/bin/rr COPY --from=composer /usr/bin/composer /usr/bin/composer @@ -22,10 +13,11 @@ WORKDIR /app COPY composer.json /app COPY composer.lock /app -RUN composer install --ignore-platform-reqs --no-scripts +ENV COMPOSER_ALLOW_SUPERUSER=1 +RUN composer install --ignore-platform-reqs --optimize-autoloader --no-dev --no-scripts COPY . /app -EXPOSE 8080 +EXPOSE 8080/tcp ENTRYPOINT [ "rr", "serve", "-c", "/app/.rr.yaml" ] diff --git a/app/config/cycle.php b/app/config/cycle.php index cd5ab78..1deae50 100644 --- a/app/config/cycle.php +++ b/app/config/cycle.php @@ -58,6 +58,9 @@ // ], ], + /** + * Prepare all internal ORM services (mappers, repositories, typecasters...) + */ 'warmup' => env('CYCLE_SCHEMA_WARMUP', false), /** diff --git a/app/config/mail.php b/app/config/mail.php index 5656de4..2a8a294 100644 --- a/app/config/mail.php +++ b/app/config/mail.php @@ -16,7 +16,6 @@ 'port' => env('MAIL_PORT'), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), - 'encryption' => env('MAIL_ENCRYPTION') ], ], ]; diff --git a/app/src/Config/MailConfig.php b/app/src/Config/MailConfig.php index fc745b8..da79c96 100644 --- a/app/src/Config/MailConfig.php +++ b/app/src/Config/MailConfig.php @@ -86,12 +86,4 @@ public function getSmtpPassword(): string { return (string) $this->config['drivers'][self::DRIVER_SMTP]['password']; } - - /** - * @return string - */ - public function getSmtpEncryption(): string - { - return (string) $this->config['drivers'][self::DRIVER_SMTP]['encryption']; - } } diff --git a/composer.json b/composer.json index e780a11..6f3c575 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "source": "https://github.com/cash-track/api" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "ext-mbstring": "*", "ext-openssl": "*", "ext-pdo": "*", @@ -35,7 +35,7 @@ "spiral/stempler-bridge": "^3.2", "spiral/validator": "^1.1", "symfony/mailer": "^6.2", - "voku/portable-utf8": "^5.4" + "voku/portable-utf8": "^6.0" }, "require-dev": { "phpunit/phpunit": "^9.5", @@ -55,7 +55,7 @@ "checks": [ "./vendor/bin/phpunit", "./vendor/bin/phpcs -p -n --standard=PSR12 --colors --report=code ./app/src", - "./vendor/bin/psalm --php-version=8.0 --show-info=true --no-cache" + "./vendor/bin/psalm --php-version=8.2 --show-info=true --no-cache" ], "phpunit": [ "./vendor/bin/phpunit" @@ -64,7 +64,7 @@ "./vendor/bin/phpcs -p -n --standard=PSR12 --colors --report=code ./app/src" ], "psalm": [ - "./vendor/bin/psalm --php-version=8.1 --show-info=true --no-cache" + "./vendor/bin/psalm --php-version=8.2 --show-info=true --no-cache" ] }, "autoload": { diff --git a/composer.lock b/composer.lock index 4dbf887..0164b93 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bb48f6c4e1b92292a01d8079a5953ee4", + "content-hash": "dfc6ae9bb2743f108139b713a6279ef9", "packages": [ { "name": "aws/aws-crt-php", @@ -58,16 +58,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.261.14", + "version": "3.261.15", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "121638bb7e62ee2f71838c52e79884f4301a9400" + "reference": "7d17f3187a4d5bebc41ca5d363570ed632b698c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/121638bb7e62ee2f71838c52e79884f4301a9400", - "reference": "121638bb7e62ee2f71838c52e79884f4301a9400", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7d17f3187a4d5bebc41ca5d363570ed632b698c0", + "reference": "7d17f3187a4d5bebc41ca5d363570ed632b698c0", "shasum": "" }, "require": { @@ -146,9 +146,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.261.14" + "source": "https://github.com/aws/aws-sdk-php/tree/3.261.15" }, - "time": "2023-03-17T18:21:00+00:00" + "time": "2023-03-20T18:19:46+00:00" }, { "name": "beste/clock", @@ -6346,16 +6346,16 @@ }, { "name": "spiral/roadrunner-jobs", - "version": "v2.8.1", + "version": "v2.9.0", "source": { "type": "git", "url": "https://github.com/spiral/roadrunner-jobs.git", - "reference": "948a45293365d831c7cb00f3d9803e0c7326659f" + "reference": "5d7b9d252c9453f7ec3f98c684a6051da3b5e8d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spiral/roadrunner-jobs/zipball/948a45293365d831c7cb00f3d9803e0c7326659f", - "reference": "948a45293365d831c7cb00f3d9803e0c7326659f", + "url": "https://api.github.com/repos/spiral/roadrunner-jobs/zipball/5d7b9d252c9453f7ec3f98c684a6051da3b5e8d2", + "reference": "5d7b9d252c9453f7ec3f98c684a6051da3b5e8d2", "shasum": "" }, "require": { @@ -6411,9 +6411,9 @@ "description": "RoadRunner Queues (Jobs) plugin API library", "support": { "issues": "https://github.com/spiral/roadrunner-jobs/issues", - "source": "https://github.com/spiral/roadrunner-jobs/tree/v2.8.1" + "source": "https://github.com/spiral/roadrunner-jobs/tree/v2.9.0" }, - "time": "2023-03-14T07:58:17+00:00" + "time": "2023-03-20T08:02:16+00:00" }, { "name": "spiral/roadrunner-kv", @@ -8661,16 +8661,16 @@ }, { "name": "voku/portable-ascii", - "version": "1.5.6", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "80953678b19901e5165c56752d087fc11526017c" + "reference": "b56450eed252f6801410d810c8e1727224ae0743" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/80953678b19901e5165c56752d087fc11526017c", - "reference": "80953678b19901e5165c56752d087fc11526017c", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", + "reference": "b56450eed252f6801410d810c8e1727224ae0743", "shasum": "" }, "require": { @@ -8707,7 +8707,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/1.5.6" + "source": "https://github.com/voku/portable-ascii/tree/2.0.1" }, "funding": [ { @@ -8731,20 +8731,20 @@ "type": "tidelift" } ], - "time": "2020-11-12T00:07:28+00:00" + "time": "2022-03-08T17:03:00+00:00" }, { "name": "voku/portable-utf8", - "version": "5.4.51", + "version": "6.0.13", "source": { "type": "git", "url": "https://github.com/voku/portable-utf8.git", - "reference": "578f5266725dc9880483d24ad0cfb39f8ce170f7" + "reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-utf8/zipball/578f5266725dc9880483d24ad0cfb39f8ce170f7", - "reference": "578f5266725dc9880483d24ad0cfb39f8ce170f7", + "url": "https://api.github.com/repos/voku/portable-utf8/zipball/b8ce36bf26593e5c2e81b1850ef0ffb299d2043f", + "reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f", "shasum": "" }, "require": { @@ -8754,10 +8754,14 @@ "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php72": "~1.0", - "voku/portable-ascii": "~1.5.6" + "voku/portable-ascii": "~2.0.0" }, "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + "phpstan/phpstan": "1.9.*@dev", + "phpstan/phpstan-strict-rules": "1.4.*@dev", + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0", + "thecodingmachine/phpstan-strict-rules": "1.0.*@dev", + "voku/phpstan-rules": "3.1.*@dev" }, "suggest": { "ext-ctype": "Use Ctype for e.g. hexadecimal digit detection", @@ -8806,7 +8810,7 @@ ], "support": { "issues": "https://github.com/voku/portable-utf8/issues", - "source": "https://github.com/voku/portable-utf8/tree/5.4.51" + "source": "https://github.com/voku/portable-utf8/tree/6.0.13" }, "funding": [ { @@ -8830,7 +8834,7 @@ "type": "tidelift" } ], - "time": "2020-12-02T01:58:49+00:00" + "time": "2023-03-08T08:35:38+00:00" }, { "name": "webmozart/assert", @@ -11963,7 +11967,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=8.1", + "php": ">=8.2", "ext-mbstring": "*", "ext-openssl": "*", "ext-pdo": "*", From 0be81a8d651c96fb1d57cc3f53a2827106cbb684 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Fri, 24 Mar 2023 23:23:26 +0200 Subject: [PATCH 15/20] Update codebase according to new framework features --- Makefile | 2 +- app/src/Auth/AuthMiddleware.php | 8 +- .../Auth/EmailConfirmationsController.php | 26 +---- .../Auth/ForgotPasswordController.php | 38 ++----- app/src/Controller/Auth/LoginController.php | 10 +- app/src/Controller/Auth/LogoutController.php | 13 +-- app/src/Controller/Auth/RefreshController.php | 14 +-- .../Controller/Auth/RegisterController.php | 23 +--- .../Controller/Profile/PasswordController.php | 24 +---- .../Controller/Profile/PhotoController.php | 23 +--- .../Controller/Profile/ProfileController.php | 58 ++-------- app/src/Controller/Tags/TagsController.php | 25 +---- .../Controller/Wallets/ActiveController.php | 14 +-- .../Wallets/Charges/ChargesController.php | 32 ++---- app/src/Controller/Wallets/ListController.php | 8 +- .../Controller/Wallets/WalletsController.php | 20 +--- app/src/Request/Charge/CreateRequest.php | 96 ++++++++--------- app/src/Request/CheckNickNameRequest.php | 7 +- .../Request/ForgotPasswordCreateRequest.php | 31 +++--- .../Request/ForgotPasswordResetRequest.php | 54 +++++----- app/src/Request/JsonErrorsRenderer.php | 4 +- app/src/Request/LoginRequest.php | 28 +++-- app/src/Request/LogoutRequest.php | 16 +-- .../Request/Profile/UpdateBasicRequest.php | 89 ++++++++------- .../Request/Profile/UpdatePasswordRequest.php | 61 ++++++----- .../Request/Profile/UpdatePhotoRequest.php | 45 ++++---- app/src/Request/RefreshTokenRequest.php | 26 +++-- app/src/Request/RegisterRequest.php | 102 ++++++++++-------- app/src/Request/Tag/CreateRequest.php | 74 +++++++------ app/src/Request/Tag/UpdateRequest.php | 81 +++++++------- app/src/Request/Wallet/CreateRequest.php | 89 ++++++++------- app/src/Request/Wallet/SortSetRequest.php | 38 ++++--- app/src/Request/Wallet/UpdateRequest.php | 59 +++++----- app/src/Security/PasswordChecker.php | 38 +++---- .../Profile/PhotoControllerTest.php | 27 +---- .../Profile/ProfileControllerTest.php | 10 +- .../Controller/Tags/TagsControllerTest.php | 8 +- .../Wallets/WalletsControllerTest.php | 11 +- .../Profile/RefreshTokenRequestTest.php | 26 ----- .../Profile/UpdatePhotoRequestTest.php | 14 +-- .../Feature/Security/PasswordCheckerTest.php | 40 +++++-- 41 files changed, 599 insertions(+), 813 deletions(-) delete mode 100644 tests/Feature/Request/Profile/RefreshTokenRequestTest.php diff --git a/Makefile b/Makefile index 1aff482..56cba9a 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ phpcs: ./vendor/bin/phpcs -p -n --standard=PSR12 --colors --report=code ./app/src psalm: - ./vendor/bin/psalm --php-version=8.0 --show-info=true + ./vendor/bin/psalm --php-version=8.2 --show-info=true --no-cache test-env-start: cd ./tests && docker-compose up -d diff --git a/app/src/Auth/AuthMiddleware.php b/app/src/Auth/AuthMiddleware.php index 1f89577..a2544a6 100644 --- a/app/src/Auth/AuthMiddleware.php +++ b/app/src/Auth/AuthMiddleware.php @@ -15,6 +15,8 @@ class AuthMiddleware implements MiddlewareInterface { + const HEADER_USER_ID = 'X-Internal-UserId'; + /** * {@inheritdoc} */ @@ -26,11 +28,13 @@ public function process(Request $request, RequestHandlerInterface $handler): Res return $this->unauthenticated(); } - if (! $authContext->getActor() instanceof User) { + $actor = $authContext->getActor(); + + if (! $actor instanceof User) { return $this->unauthenticated(); } - return $handler->handle($request); + return $handler->handle($request->withAddedHeader(self::HEADER_USER_ID, (string) $actor->id)); } /** diff --git a/app/src/Controller/Auth/EmailConfirmationsController.php b/app/src/Controller/Auth/EmailConfirmationsController.php index 78de39d..a19b8c4 100644 --- a/app/src/Controller/Auth/EmailConfirmationsController.php +++ b/app/src/Controller/Auth/EmailConfirmationsController.php @@ -16,13 +16,6 @@ final class EmailConfirmationsController extends AuthAwareController { - /** - * @param \Spiral\Auth\AuthScope $auth - * @param \Spiral\Http\ResponseWrapper $response - * @param \App\View\EmailConfirmationView $emailConfirmationView - * @param \App\Service\Auth\EmailConfirmationService $emailConfirmationService - * @param \App\Repository\EmailConfirmationRepository $emailConfirmationRepository - */ public function __construct( protected AuthScope $auth, protected ResponseWrapper $response, @@ -33,11 +26,7 @@ public function __construct( parent::__construct($auth); } - /** - * @Route(route="/auth/email/confirmation", name="auth.email.confirmation", methods="GET", group="auth") - * - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/auth/email/confirmation', name: 'auth.email.confirmation', methods: 'GET', group: 'auth')] public function index(): ResponseInterface { /** @var \App\Database\EmailConfirmation|null $confirmation */ @@ -52,12 +41,7 @@ public function index(): ResponseInterface return $this->emailConfirmationView->json($confirmation); } - /** - * @Route(route="/auth/email/confirmation/confirm/", name="auth.email.confirm") - * - * @param string $token - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/auth/email/confirmation/confirm/', name: 'auth.email.confirm')] public function confirm(string $token): ResponseInterface { try { @@ -74,11 +58,7 @@ public function confirm(string $token): ResponseInterface ]); } - /** - * @Route(route="/auth/email/confirmation/resend", name="auth.email.resend", methods="POST", group="auth") - * - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/auth/email/confirmation/resend', name: 'auth.email.resend', methods: 'POST', group: 'auth')] public function reSend(): ResponseInterface { try { diff --git a/app/src/Controller/Auth/ForgotPasswordController.php b/app/src/Controller/Auth/ForgotPasswordController.php index 6ccce83..06b9ffd 100644 --- a/app/src/Controller/Auth/ForgotPasswordController.php +++ b/app/src/Controller/Auth/ForgotPasswordController.php @@ -14,32 +14,17 @@ final class ForgotPasswordController { - /** - * @param \Spiral\Http\ResponseWrapper $response - * @param \App\Service\Auth\ForgotPasswordService $forgotPasswordService - */ public function __construct( - protected ResponseWrapper $response, - protected ForgotPasswordService $forgotPasswordService, + protected readonly ResponseWrapper $response, + protected readonly ForgotPasswordService $forgotPasswordService, ) { } - /** - * @Route(route="/auth/password/forgot", name="auth.password.forgot", methods="POST") - * - * @param \App\Request\ForgotPasswordCreateRequest $request - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/auth/password/forgot', name: 'auth.password.forgot', methods: 'POST')] public function create(ForgotPasswordCreateRequest $request): ResponseInterface { - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - try { - $this->forgotPasswordService->create($request->getEmail()); + $this->forgotPasswordService->create($request->email); } catch (ForgotPasswordThrottledException $exception) { return $this->response->json([ 'message' => $exception->getMessage(), @@ -56,22 +41,11 @@ public function create(ForgotPasswordCreateRequest $request): ResponseInterface ], 200); } - /** - * @Route(route="/auth/password/reset", name="auth.password.reset", methods="POST") - * - * @param \App\Request\ForgotPasswordResetRequest $request - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/auth/password/reset', name: 'auth.password.reset', methods: 'POST')] public function reset(ForgotPasswordResetRequest $request): ResponseInterface { - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - try { - $this->forgotPasswordService->reset($request->getCode(), $request->getPassword()); + $this->forgotPasswordService->reset($request->code, $request->password); } catch (\Throwable $exception) { return $this->response->json([ 'message' => 'Unable to reset your password.', diff --git a/app/src/Controller/Auth/LoginController.php b/app/src/Controller/Auth/LoginController.php index c9d5744..47dcc8f 100644 --- a/app/src/Controller/Auth/LoginController.php +++ b/app/src/Controller/Auth/LoginController.php @@ -36,14 +36,8 @@ public function __construct( #[Route(route: '/auth/login', name: 'auth.login', methods: 'POST')] public function login(LoginRequest $request): ResponseInterface { - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - try { - $user = $this->userRepository->findByEmail($request->getField('email')); + $user = $this->userRepository->findByEmail($request->email); } catch (\Throwable $exception) { return $this->response->json([ 'error' => $exception->getMessage(), @@ -55,7 +49,7 @@ public function login(LoginRequest $request): ResponseInterface return $this->responseAuthenticationFailure(); } - if (! $this->authService->verifyPassword($user, $request->getField('password'))) { + if (! $this->authService->verifyPassword($user, $request->password)) { return $this->responseAuthenticationFailure(); } diff --git a/app/src/Controller/Auth/LogoutController.php b/app/src/Controller/Auth/LogoutController.php index e01e26b..2a8c1c8 100644 --- a/app/src/Controller/Auth/LogoutController.php +++ b/app/src/Controller/Auth/LogoutController.php @@ -14,11 +14,6 @@ final class LogoutController { - /** - * @param \Spiral\Auth\AuthScope $auth - * @param \Spiral\Http\ResponseWrapper $response - * @param \App\Service\Auth\RefreshTokenService $refreshTokenService - */ public function __construct( protected AuthScope $auth, protected ResponseWrapper $response, @@ -26,16 +21,12 @@ public function __construct( ) { } - /** - * @Route(route="/auth/logout", name="auth.logout", methods="POST", group="auth") - * - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/auth/logout', name: 'auth.logout', methods: 'POST', group: 'auth')] public function logout(LogoutRequest $request): ResponseInterface { $this->auth->close(); - $refreshToken = $this->refreshTokenService->getContextByToken($request->getRefreshToken())->getToken(); + $refreshToken = $this->refreshTokenService->getContextByToken($request->refreshToken)->getToken(); if ($refreshToken instanceof TokenInterface) { $this->refreshTokenService->close($refreshToken); diff --git a/app/src/Controller/Auth/RefreshController.php b/app/src/Controller/Auth/RefreshController.php index 1b89fdb..fb91cf7 100644 --- a/app/src/Controller/Auth/RefreshController.php +++ b/app/src/Controller/Auth/RefreshController.php @@ -17,12 +17,6 @@ final class RefreshController extends Controller { - /** - * @param \App\View\UserView $userView - * @param \App\Service\Auth\AuthService $authService - * @param \Spiral\Http\ResponseWrapper $response - * @param \App\Service\Auth\RefreshTokenService $refreshTokenService - */ public function __construct( protected UserView $userView, protected AuthService $authService, @@ -32,13 +26,7 @@ public function __construct( parent::__construct($userView, $response); } - /** - * @Route(route="/auth/refresh", name="auth.refresh", methods="POST") - * - * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \App\Request\RefreshTokenRequest $refreshTokenRequest - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/auth/refresh', name: 'auth.refresh', methods: 'POST')] public function refresh(ServerRequestInterface $request, RefreshTokenRequest $refreshTokenRequest): ResponseInterface { $authContext = $this->refreshTokenService->getContextByRequest($request); diff --git a/app/src/Controller/Auth/RegisterController.php b/app/src/Controller/Auth/RegisterController.php index 73736bf..0e591e6 100644 --- a/app/src/Controller/Auth/RegisterController.php +++ b/app/src/Controller/Auth/RegisterController.php @@ -19,14 +19,6 @@ final class RegisterController extends Controller { - /** - * @param \App\View\UserView $userView - * @param \App\Service\Auth\AuthService $authService - * @param \App\Service\UserService $userService - * @param \Spiral\Http\ResponseWrapper $response - * @param \App\Service\Auth\EmailConfirmationService $emailConfirmationService - * @param \App\Service\Auth\RefreshTokenService $refreshTokenService - */ public function __construct( protected UserView $userView, protected AuthService $authService, @@ -39,20 +31,9 @@ public function __construct( parent::__construct($userView, $response); } - /** - * @Route(route="/auth/register", name="auth.register", methods="POST") - * - * @param \App\Request\RegisterRequest $request - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/auth/register', name: 'auth.register', methods: 'POST')] public function register(RegisterRequest $request): ResponseInterface { - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - $user = $request->createUser(); $currency = $this->currencyRepository->getDefault(); @@ -61,7 +42,7 @@ public function register(RegisterRequest $request): ResponseInterface $user->setDefaultCurrency($currency); } - $this->authService->hashPassword($user, $request->getField('password')); + $this->authService->hashPassword($user, $request->password); try { $user = $this->userService->store($user); diff --git a/app/src/Controller/Profile/PasswordController.php b/app/src/Controller/Profile/PasswordController.php index 5a5d440..9e2ba0d 100644 --- a/app/src/Controller/Profile/PasswordController.php +++ b/app/src/Controller/Profile/PasswordController.php @@ -16,13 +16,6 @@ final class PasswordController extends AuthAwareController { - /** - * @param \Spiral\Auth\AuthScope $auth - * @param \Spiral\Http\ResponseWrapper $response - * @param \App\Service\Auth\AuthService $authService - * @param \App\Service\UserService $userService - * @param \Psr\Log\LoggerInterface $logger - */ public function __construct( AuthScope $auth, protected ResponseWrapper $response, @@ -33,23 +26,10 @@ public function __construct( parent::__construct($auth); } - /** - * @Route(route="/profile/password", name="profile.update.password", methods="PUT", group="auth") - * - * @param \App\Request\Profile\UpdatePasswordRequest $request - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/profile/password', name: 'profile.update.password', methods: 'PUT', group: 'auth')] public function updatePassword(UpdatePasswordRequest $request): ResponseInterface { - $request->setContext($this->user); - - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - - $this->authService->hashPassword($this->user, $request->getNewPassword()); + $this->authService->hashPassword($this->user, $request->newPassword); try { $this->userService->store($this->user); diff --git a/app/src/Controller/Profile/PhotoController.php b/app/src/Controller/Profile/PhotoController.php index 98709e0..85c5ad0 100644 --- a/app/src/Controller/Profile/PhotoController.php +++ b/app/src/Controller/Profile/PhotoController.php @@ -19,13 +19,6 @@ final class PhotoController extends AuthAwareController { use PrototypeTrait; - /** - * @param \Spiral\Auth\AuthScope $auth - * @param \Psr\Log\LoggerInterface $logger - * @param \App\Service\UserService $userService - * @param \Spiral\Http\ResponseWrapper $response - * @param \App\Service\PhotoStorageService $photoStorageService - */ public function __construct( AuthScope $auth, protected LoggerInterface $logger, @@ -36,23 +29,9 @@ public function __construct( parent::__construct($auth); } - - /** - * @Route(route="/profile/photo", name="profile.update.photo", methods="PUT", group="auth") - * - * @param \App\Request\Profile\UpdatePhotoRequest $request - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/profile/photo', name: 'profile.update.photo', methods: 'PUT', group: 'auth')] public function updatePhoto(UpdatePhotoRequest $request): ResponseInterface { - $request->setContext($this->user); - - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - $file = $request->getPhoto(); $fileName = $this->photoStorageService->storeUploadedProfilePhoto($file); diff --git a/app/src/Controller/Profile/ProfileController.php b/app/src/Controller/Profile/ProfileController.php index e0dbfad..7c6384c 100644 --- a/app/src/Controller/Profile/ProfileController.php +++ b/app/src/Controller/Profile/ProfileController.php @@ -22,14 +22,6 @@ class ProfileController extends AuthAwareController { - /** - * @param \Spiral\Auth\AuthScope $auth - * @param \App\View\UserView $userView - * @param \Psr\Log\LoggerInterface $logger - * @param \App\Service\UserService $userService - * @param \Spiral\Http\ResponseWrapper $response - * @param \App\Repository\CurrencyRepository $currencyRepository - */ public function __construct( AuthScope $auth, protected UserView $userView, @@ -41,63 +33,31 @@ public function __construct( parent::__construct($auth); } - /** - * @Route(route="/profile", name="profile.index", methods="GET", group="auth") - * - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/profile', name: 'profile.index', methods: 'GET', group: 'auth')] public function index(): ResponseInterface { return $this->userView->json($this->user); } - /** - * @Route(route="/profile/check/nick-name", name="profile.check.nickname", methods="POST", group="auth") - * - * @param \App\Request\CheckNickNameRequest $request - * @return \Psr\Http\Message\ResponseInterface - */ - public function checkNickName(InputManager $input, ValidationProviderInterface $provider): ResponseInterface + #[Route(route: '/profile/check/nick-name', name: 'profile.check.nickname', methods: 'POST', group: 'auth')] + public function checkNickName(CheckNickNameRequest $_): ResponseInterface { - $validator = $provider->getValidation(FilterDefinition::class)->validate([ - 'nickName' => $input->data('nickName'), - ], CheckNickNameRequest::validationRules())->withContext($this->user); - - if (! $validator->isValid()) { - return $this->response->json([ - 'errors' => $validator->getErrors(), - ], 422); - } - return $this->response->json([ 'message' => 'Nick name are free to use' ]); } - /** - * @Route(route="/profile", name="profile.update", methods="PUT", group="auth") - * - * @param \App\Request\Profile\UpdateBasicRequest $request - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/profile', name: 'profile.update', methods: 'PUT', group: 'auth')] public function update(UpdateBasicRequest $request): ResponseInterface { - $request->setContext($this->user); - - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - - $this->user->name = $request->getName(); - $this->user->lastName = $request->getLastName(); - $this->user->nickName = $request->getNickName(); - $this->user->defaultCurrencyCode = $request->getDefaultCurrencyCode(); + $this->user->name = $request->name; + $this->user->lastName = $request->lastName; + $this->user->nickName = $request->nickName; + $this->user->defaultCurrencyCode = $request->defaultCurrencyCode; try { /** @var \App\Database\Currency|null $defaultCurrency */ - $defaultCurrency = $this->currencyRepository->findByPK($request->getDefaultCurrencyCode()); + $defaultCurrency = $this->currencyRepository->findByPK($request->defaultCurrencyCode); if (! $defaultCurrency instanceof Currency) { throw new \RuntimeException('Unable to load default currency'); diff --git a/app/src/Controller/Tags/TagsController.php b/app/src/Controller/Tags/TagsController.php index 96be2d0..8ea9781 100644 --- a/app/src/Controller/Tags/TagsController.php +++ b/app/src/Controller/Tags/TagsController.php @@ -41,14 +41,6 @@ public function list(): ResponseInterface #[Route(route: '/tags', name: 'tag.create', methods: 'POST', group: 'auth')] public function create(CreateRequest $request): ResponseInterface { - $request->setFields(['user_id' => $this->user->id]); - - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - try { $tag = $this->tagService->create($request->createTag(), $this->user); } catch (\Throwable $exception) { @@ -70,20 +62,9 @@ public function update(string $id, UpdateRequest $request): ResponseInterface return $this->response->create(404); } - $request->setValue([ - 'id' => $tag->id, - 'user_id' => $this->user->id - ]); - - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - - $tag->name = $request->getName(); - $tag->icon = $request->getIcon(); - $tag->color = $request->getColor(); + $tag->name = $request->name; + $tag->icon = $request->icon; + $tag->color = $request->color; try { $tag = $this->tagService->store($tag); diff --git a/app/src/Controller/Wallets/ActiveController.php b/app/src/Controller/Wallets/ActiveController.php index cb26724..005f73c 100644 --- a/app/src/Controller/Wallets/ActiveController.php +++ b/app/src/Controller/Wallets/ActiveController.php @@ -25,12 +25,7 @@ public function __construct( parent::__construct($auth); } - /** - * @Route(route="/wallets//activate", name="wallet.activate", methods="POST", group="auth") - * - * @param string $id - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/wallets//activate', name: 'wallet.activate', methods: 'POST', group: 'auth')] public function activate(string $id): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); @@ -57,12 +52,7 @@ public function activate(string $id): ResponseInterface return $this->response->create(200); } - /** - * @Route(route="/wallets//disable", name="wallet.disable", methods="POST", group="auth") - * - * @param string $id - * @return \Psr\Http\Message\ResponseInterface - */ + #[Route(route: '/wallets//disable', name: 'wallet.disable', methods: 'POST', group: 'auth')] public function disable(string $id): ResponseInterface { $wallet = $this->walletRepository->findByPKByUserPK((int) $id, (int) $this->user->id); diff --git a/app/src/Controller/Wallets/Charges/ChargesController.php b/app/src/Controller/Wallets/Charges/ChargesController.php index c0c602d..4441aff 100644 --- a/app/src/Controller/Wallets/Charges/ChargesController.php +++ b/app/src/Controller/Wallets/Charges/ChargesController.php @@ -83,21 +83,15 @@ public function create(string $walletId, CreateRequest $request): ResponseInterf return $this->response->create(404); } - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - $charge = new Charge(); - $charge->type = $request->getType(); - $charge->amount = $request->getAmount(); - $charge->title = $request->getTitle(); - $charge->description = $request->getDescription(); + $charge->type = $request->type; + $charge->amount = $request->amount; + $charge->title = $request->title; + $charge->description = $request->description; $charge->setWallet($wallet); $charge->setUser($this->user); - $tags = $this->tagRepository->findAllByPKsAndUserPKs($request->getTags(), $wallet->getUserIDs()); + $tags = $this->tagRepository->findAllByPKsAndUserPKs($request->tags, $wallet->getUserIDs()); foreach ($tags as $tag) { $charge->tags->add($tag); @@ -139,21 +133,15 @@ public function update(string $walletId, string $chargeId, CreateRequest $reques return $this->response->create(404); } - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - $oldCharge = clone $charge; - $charge->type = $request->getType(); - $charge->amount = $request->getAmount(); - $charge->title = $request->getTitle(); - $charge->description = $request->getDescription(); + $charge->type = $request->type; + $charge->amount = $request->amount; + $charge->title = $request->title; + $charge->description = $request->description; $charge->tags->clear(); - $tags = $this->tagRepository->findAllByPKsAndUserPKs($request->getTags(), $wallet->getUserIDs()); + $tags = $this->tagRepository->findAllByPKsAndUserPKs($request->tags, $wallet->getUserIDs()); foreach ($tags as $tag) { $charge->tags->add($tag); diff --git a/app/src/Controller/Wallets/ListController.php b/app/src/Controller/Wallets/ListController.php index 4ec4451..ad46de9 100644 --- a/app/src/Controller/Wallets/ListController.php +++ b/app/src/Controller/Wallets/ListController.php @@ -48,14 +48,8 @@ public function listUnArchived(): ResponseInterface #[Route(route: '/wallets/unarchived/sort', name: 'wallet.sort.unarchived.set', methods: 'POST', group: 'auth')] public function sortUnArchived(SortSetRequest $request): ResponseInterface { - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - try { - $this->sortService->set($this->user, SortType::Wallets, $request->getSort()); + $this->sortService->set($this->user, SortType::Wallets, $request->sort); $this->userService->store($this->user); } catch (\Throwable) { } diff --git a/app/src/Controller/Wallets/WalletsController.php b/app/src/Controller/Wallets/WalletsController.php index 03213fc..046c101 100644 --- a/app/src/Controller/Wallets/WalletsController.php +++ b/app/src/Controller/Wallets/WalletsController.php @@ -35,12 +35,6 @@ public function __construct( #[Route(route: '/wallets', name: 'wallet.create', methods: 'POST', group: 'auth')] public function create(CreateRequest $request): ResponseInterface { - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - try { $wallet = $this->walletService->create($request->createWallet(), $this->user); } catch (\Throwable $exception) { @@ -62,19 +56,13 @@ public function update(string $id, UpdateRequest $request): ResponseInterface return $this->response->create(404); } - if (! $request->isValid()) { - return $this->response->json([ - 'errors' => $request->getErrors(), - ], 422); - } - - $wallet->name = $request->getName(); - $wallet->isPublic = $request->getIsPublic(); - $wallet->defaultCurrencyCode = $request->getDefaultCurrencyCode(); + $wallet->name = $request->name; + $wallet->isPublic = $request->isPublic; + $wallet->defaultCurrencyCode = $request->defaultCurrencyCode; try { /** @var \App\Database\Currency|null $defaultCurrency */ - $defaultCurrency = $this->currencyRepository->findByPK($request->getDefaultCurrencyCode()); + $defaultCurrency = $this->currencyRepository->findByPK($request->defaultCurrencyCode); if (! $defaultCurrency instanceof Currency) { throw new \RuntimeException('Unable to load default currency'); diff --git a/app/src/Request/Charge/CreateRequest.php b/app/src/Request/Charge/CreateRequest.php index 85bf90e..1a6f7e7 100644 --- a/app/src/Request/Charge/CreateRequest.php +++ b/app/src/Request/Charge/CreateRequest.php @@ -6,68 +6,58 @@ use App\Database\Charge; use App\Database\Tag; -use Spiral\Filters\Filter; - -class CreateRequest extends Filter +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Attribute\Setter; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; + +class CreateRequest extends Filter implements HasFilterDefinition { // TODO. Support custom currency with custom rate - protected const SCHEMA = [ - 'type' => 'data:type', - 'amount' => 'data:amount', - 'title' => 'data:title', - 'description' => 'data:description', - 'tags' => 'data:tags', - ]; - - protected const VALIDATES = [ - 'type' => [ - 'type::notEmpty', - ['in_array', [Charge::TYPE_EXPENSE, Charge::TYPE_INCOME], true], - ], - 'amount' => [ - 'is_numeric', - 'type::notEmpty', - ['number::higher', 0] - ], - 'title' => [ - 'is_string', - 'type::notEmpty', - ], - 'description' => [ - 'is_string', - ], - 'tags' => [ - ['array::of', ['entity:exists', Tag::class, 'id']], - ], - ]; - - public function getType(): string - { - return (string) $this->getField('type', Charge::TYPE_EXPENSE); - } + #[Data] + public string $type = ''; - public function getAmount(): float - { - return floatval($this->getField('amount', 0)); - } + #[Data] + #[Setter(filter: 'floatval')] + public float $amount = 0.0; - public function getTitle(): string - { - return (string) $this->getField('title'); - } + #[Data] + public string $title = ''; - public function getDescription(): string - { - return (string) $this->getField('description'); - } + #[Data] + public string $description = ''; /** - * @return array - * @throws \Spiral\Models\Exception\EntityExceptionInterface + * @var array */ - public function getTags(): array + #[Data] + public array $tags = []; + + public function filterDefinition(): FilterDefinitionInterface { - return (array) $this->getField('tags'); + return new FilterDefinition(validationRules: [ + 'type' => [ + 'type::notEmpty', + ['in_array', [Charge::TYPE_EXPENSE, Charge::TYPE_INCOME], true], + ], + 'amount' => [ + 'is_numeric', + 'type::notEmpty', + ['number::higher', 0] + ], + 'title' => [ + 'is_string', + 'type::notEmpty', + ], + 'description' => [ + 'is_string', + ], + 'tags' => [ + ['array::of', ['entity:exists', Tag::class, 'id']], + ], + ]); } } diff --git a/app/src/Request/CheckNickNameRequest.php b/app/src/Request/CheckNickNameRequest.php index ef60703..eed2f9c 100644 --- a/app/src/Request/CheckNickNameRequest.php +++ b/app/src/Request/CheckNickNameRequest.php @@ -4,8 +4,10 @@ namespace App\Request; +use App\Auth\AuthMiddleware; use App\Database\User; use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Attribute\Input\Header; use Spiral\Filters\Model\Filter; use Spiral\Filters\Model\FilterDefinitionInterface; use Spiral\Filters\Model\HasFilterDefinition; @@ -13,6 +15,9 @@ class CheckNickNameRequest extends Filter implements HasFilterDefinition { + #[Header(key: AuthMiddleware::HEADER_USER_ID)] + public int $id = 0; + #[Data] public string $nickName = ''; @@ -29,7 +34,7 @@ public static function validationRules(): array 'type::notEmpty', ['string::longer', 3], ['string::regexp', '/^[a-zA-Z0-9_]*$/'], - ['entity::unique', User::class, 'nickName', 'error' => 'Nick name already claimed'], + ['unique::verify', User::class, 'nickName', [], ['id'], 'error' => 'Nick name already claimed'], ], ]; } diff --git a/app/src/Request/ForgotPasswordCreateRequest.php b/app/src/Request/ForgotPasswordCreateRequest.php index 4b0c8e7..27f9591 100644 --- a/app/src/Request/ForgotPasswordCreateRequest.php +++ b/app/src/Request/ForgotPasswordCreateRequest.php @@ -5,24 +5,25 @@ namespace App\Request; use App\Database\User; -use Spiral\Filters\Filter; +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; -class ForgotPasswordCreateRequest extends Filter +class ForgotPasswordCreateRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'email' => 'data:email', - ]; + #[Data] + public string $email = ''; - protected const VALIDATES = [ - 'email' => [ - 'address::email', - 'type::notEmpty', - ['entity::exists', User::class, 'email'], - ], - ]; - - public function getEmail(): string + public function filterDefinition(): FilterDefinitionInterface { - return (string) $this->getField('email'); + return new FilterDefinition(validationRules: [ + 'email' => [ + 'address::email', + 'type::notEmpty', + ['entity::exists', User::class, 'email'], + ], + ]); } } diff --git a/app/src/Request/ForgotPasswordResetRequest.php b/app/src/Request/ForgotPasswordResetRequest.php index ad5a93f..010925d 100644 --- a/app/src/Request/ForgotPasswordResetRequest.php +++ b/app/src/Request/ForgotPasswordResetRequest.php @@ -5,38 +5,38 @@ namespace App\Request; use App\Database\ForgotPasswordRequest; -use Spiral\Filters\Filter; +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; -class ForgotPasswordResetRequest extends Filter +class ForgotPasswordResetRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'code' => 'data:code', - 'password' => 'data:password', - 'passwordConfirmation' => 'data:passwordConfirmation', - ]; + #[Data] + public string $code = ''; - protected const VALIDATES = [ - 'code' => [ - 'type::notEmpty', - ['entity::exists', ForgotPasswordRequest::class, 'code'], - ], - 'password' => [ - 'type::notEmpty', - ['string::longer', 6], - ], - 'passwordConfirmation' => [ - ['notEmpty', 'if' => ['withAll' => ['password']]], - ['match', 'password', 'error' => 'Password confirmation does not match'] - ], - ]; + #[Data] + public string $password = ''; - public function getCode(): string - { - return $this->getField('code'); - } + #[Data] + public string $passwordConfirmation = ''; - public function getPassword(): string + public function filterDefinition(): FilterDefinitionInterface { - return $this->getField('password'); + return new FilterDefinition(validationRules: [ + 'code' => [ + 'type::notEmpty', + ['entity::exists', ForgotPasswordRequest::class, 'code'], + ], + 'password' => [ + 'type::notEmpty', + ['string::longer', 6], + ], + 'passwordConfirmation' => [ + ['notEmpty', 'if' => ['withAll' => ['password']]], + ['match', 'password', 'error' => 'Password confirmation does not match'] + ], + ]); } } diff --git a/app/src/Request/JsonErrorsRenderer.php b/app/src/Request/JsonErrorsRenderer.php index b93f12c..e03c4c0 100644 --- a/app/src/Request/JsonErrorsRenderer.php +++ b/app/src/Request/JsonErrorsRenderer.php @@ -6,10 +6,10 @@ use Spiral\Filters\ErrorsRendererInterface; use Spiral\Http\ResponseWrapper; -class JsonErrorsRenderer implements ErrorsRendererInterface +readonly class JsonErrorsRenderer implements ErrorsRendererInterface { public function __construct( - private readonly ResponseWrapper $wrapper + private ResponseWrapper $wrapper ) { } diff --git a/app/src/Request/LoginRequest.php b/app/src/Request/LoginRequest.php index dc89fe7..62363c0 100644 --- a/app/src/Request/LoginRequest.php +++ b/app/src/Request/LoginRequest.php @@ -4,17 +4,25 @@ namespace App\Request; -use Spiral\Filters\Filter; +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; -class LoginRequest extends Filter +class LoginRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'email' => 'data:email', - 'password' => 'data:password', - ]; + #[Data] + public string $email = ''; - protected const VALIDATES = [ - 'email' => ['type::notEmpty'], - 'password' => ['type::notEmpty'], - ]; + #[Data] + public string $password = ''; + + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition(validationRules: [ + 'email' => ['type::notEmpty'], + 'password' => ['type::notEmpty'], + ]); + } } diff --git a/app/src/Request/LogoutRequest.php b/app/src/Request/LogoutRequest.php index f3ab365..1a40c5c 100644 --- a/app/src/Request/LogoutRequest.php +++ b/app/src/Request/LogoutRequest.php @@ -4,19 +4,11 @@ namespace App\Request; -use Spiral\Filters\Filter; +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Model\Filter; class LogoutRequest extends Filter { - protected const SCHEMA = [ - 'refreshToken' => 'data:refreshToken', - ]; - - /** - * @return string - */ - public function getRefreshToken(): string - { - return (string) $this->getField('refreshToken'); - } + #[Data] + public string $refreshToken = ''; } diff --git a/app/src/Request/Profile/UpdateBasicRequest.php b/app/src/Request/Profile/UpdateBasicRequest.php index a17be38..65ae718 100644 --- a/app/src/Request/Profile/UpdateBasicRequest.php +++ b/app/src/Request/Profile/UpdateBasicRequest.php @@ -4,58 +4,55 @@ namespace App\Request\Profile; +use App\Auth\AuthMiddleware; use App\Database\Currency; use App\Database\User; -use Spiral\Filters\Filter; - -class UpdateBasicRequest extends Filter +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Attribute\Input\Header; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; + +class UpdateBasicRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'name' => 'data:name', - 'lastName' => 'data:lastName', - 'nickName' => 'data:nickName', - 'defaultCurrencyCode' => 'data:defaultCurrencyCode' - ]; - - protected const VALIDATES = [ - 'name' => [ - 'is_string', - 'type::notEmpty', - ], - 'lastName' => [ - 'is_string', - ], - 'nickName' => [ - 'is_string', - 'type::notEmpty', - ['string::longer', 3], - ['string::regexp', '/^[a-zA-Z0-9_]*$/'], - ['entity::unique', User::class, 'nickName'], - ], - 'defaultCurrencyCode' => [ - 'is_string', - 'type::notEmpty', - ['entity::exists', Currency::class, 'code'], - ], - ]; - - public function getName(): string - { - return $this->getField('name'); - } + #[Header(key: AuthMiddleware::HEADER_USER_ID)] + public int $id = 0; - public function getLastName(): string - { - return $this->getField('lastName'); - } + #[Data] + public string $name = ''; - public function getNickName(): string - { - return $this->getField('nickName'); - } + #[Data] + public string $lastName = ''; + + #[Data] + public string $nickName = ''; + + #[Data] + public string $defaultCurrencyCode = ''; - public function getDefaultCurrencyCode(): string + public function filterDefinition(): FilterDefinitionInterface { - return $this->getField('defaultCurrencyCode'); + return new FilterDefinition(validationRules: [ + 'name' => [ + 'is_string', + 'type::notEmpty', + ], + 'lastName' => [ + 'is_string', + ], + 'nickName' => [ + 'is_string', + 'type::notEmpty', + ['string::longer', 3], + ['string::regexp', '/^[a-zA-Z0-9_]*$/'], + ['unique::verify', User::class, 'nickName', [], ['id']], + ], + 'defaultCurrencyCode' => [ + 'is_string', + 'type::notEmpty', + ['entity::exists', Currency::class, 'code'], + ], + ]); } } diff --git a/app/src/Request/Profile/UpdatePasswordRequest.php b/app/src/Request/Profile/UpdatePasswordRequest.php index b76dc03..8be7859 100644 --- a/app/src/Request/Profile/UpdatePasswordRequest.php +++ b/app/src/Request/Profile/UpdatePasswordRequest.php @@ -4,33 +4,44 @@ namespace App\Request\Profile; -use Spiral\Filters\Filter; +use App\Auth\AuthMiddleware; +use App\Database\User; +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Attribute\Input\Header; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; -class UpdatePasswordRequest extends Filter +class UpdatePasswordRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'currentPassword' => 'data:currentPassword', - 'newPassword' => 'data:newPassword', - 'newPasswordConfirmation' => 'data:newPasswordConfirmation', - ]; - - protected const VALIDATES = [ - 'currentPassword' => [ - 'type::notEmpty', - 'password::verify', - ], - 'newPassword' => [ - 'type::notEmpty', - ['string::longer', 6], - ], - 'newPasswordConfirmation' => [ - ['notEmpty', 'if' => ['withAll' => ['newPassword']]], - ['match', 'newPassword', 'error' => 'New password confirmation does not match'], - ], - ]; - - public function getNewPassword(): string + #[Header(key: AuthMiddleware::HEADER_USER_ID)] + public int $id = 0; + + #[Data] + public string $currentPassword = ''; + + #[Data] + public string $newPassword = ''; + + #[Data] + public string $newPasswordConfirmation = ''; + + public function filterDefinition(): FilterDefinitionInterface { - return $this->getField('newPassword'); + return new FilterDefinition(validationRules: [ + 'currentPassword' => [ + 'type::notEmpty', + ['password::verify', User::class], + ], + 'newPassword' => [ + 'type::notEmpty', + ['string::longer', 6], + ], + 'newPasswordConfirmation' => [ + ['notEmpty', 'if' => ['withAll' => ['newPassword']]], + ['match', 'newPassword', 'error' => 'New password confirmation does not match'], + ], + ]); } } diff --git a/app/src/Request/Profile/UpdatePhotoRequest.php b/app/src/Request/Profile/UpdatePhotoRequest.php index e5e0e8e..a7e025d 100644 --- a/app/src/Request/Profile/UpdatePhotoRequest.php +++ b/app/src/Request/Profile/UpdatePhotoRequest.php @@ -6,35 +6,34 @@ use Laminas\Diactoros\Exception\UploadedFileErrorException; use Psr\Http\Message\UploadedFileInterface; -use Spiral\Filters\Filter; +use Spiral\Filters\Attribute\Input\File; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; -class UpdatePhotoRequest extends Filter +class UpdatePhotoRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'photo' => 'file:photo', - ]; + #[File] + public ?UploadedFileInterface $photo = null; - protected const VALIDATES = [ - 'photo' => [ - 'file::uploaded', - 'image::valid', - ['file::size', 5120], - ['image::smaller', 5000, 5000], - ['image::bigger', 50, 50], - ], - ]; + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition(validationRules: [ + 'photo' => [ + 'file::uploaded', + 'image::valid', + ['file::size', 5120], + ['image::smaller', 5000, 5000], + ['image::bigger', 50, 50], + ], + ]); + } - /** - * @return \Psr\Http\Message\UploadedFileInterface - * @throws \Laminas\Diactoros\Exception\UploadedFileErrorException - * @throws \Spiral\Models\Exception\EntityExceptionInterface - */ public function getPhoto(): UploadedFileInterface { - $file = $this->getField('photo'); - - if ($file instanceof UploadedFileInterface) { - return $file; + if ($this->photo instanceof UploadedFileInterface) { + return $this->photo; } throw new UploadedFileErrorException('Empty uploaded file'); diff --git a/app/src/Request/RefreshTokenRequest.php b/app/src/Request/RefreshTokenRequest.php index 62a5a6d..19e8b51 100644 --- a/app/src/Request/RefreshTokenRequest.php +++ b/app/src/Request/RefreshTokenRequest.php @@ -4,23 +4,21 @@ namespace App\Request; -use Spiral\Filters\Filter; +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; -class RefreshTokenRequest extends Filter +class RefreshTokenRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'accessToken' => 'data:accessToken', - ]; + #[Data] + public string $accessToken = ''; - protected const VALIDATES = [ - 'accessToken' => ['type::notEmpty'], - ]; - - /** - * @return string - */ - public function getAccessToken(): string + public function filterDefinition(): FilterDefinitionInterface { - return (string) $this->getField('accessToken'); + return new FilterDefinition(validationRules: [ + 'accessToken' => ['is_string'], + ]); } } diff --git a/app/src/Request/RegisterRequest.php b/app/src/Request/RegisterRequest.php index f654f9b..8c022f3 100644 --- a/app/src/Request/RegisterRequest.php +++ b/app/src/Request/RegisterRequest.php @@ -6,57 +6,73 @@ use App\Database\Currency; use App\Database\User; -use Spiral\Filters\Filter; +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; -class RegisterRequest extends Filter +class RegisterRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'name' => 'data:name', - 'lastName' => 'data:lastName', - 'nickName' => 'data:nickName', - 'email' => 'data:email', - 'password' => 'data:password', - 'passwordConfirmation' => 'data:passwordConfirmation' - ]; + #[Data] + public string $name = ''; - protected const VALIDATES = [ - 'name' => [ - 'is_string', - 'type::notEmpty', - ], - 'lastName' => [ - 'is_string', - ], - 'nickName' => [ - 'is_string', - 'type::notEmpty', - ['string::longer', 3], - ['string::regexp', '/^[a-zA-Z0-9_]*$/'], - ['entity::unique', User::class, 'nickName'], - ], - 'email' => [ - 'address::email', - 'type::notEmpty', - ['entity::unique', User::class, 'email'], - ], - 'password' => [ - 'type::notEmpty', - ['string::longer', 6], - ], - 'passwordConfirmation' => [ - ['notEmpty', 'if' => ['withAll' => ['password']]], - ['match', 'password', 'error' => 'Password confirmation does not match'] - ], - ]; + #[Data] + public string $lastName = ''; + + #[Data] + public string $nickName = ''; + + #[Data] + public string $email = ''; + + #[Data] + public string $password = ''; + + #[Data] + public string $passwordConfirmation = ''; + + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition(validationRules: [ + 'name' => [ + 'is_string', + 'type::notEmpty', + ], + 'lastName' => [ + 'is_string', + ], + 'nickName' => [ + 'is_string', + 'type::notEmpty', + ['string::longer', 3], + ['string::regexp', '/^[a-zA-Z0-9_]*$/'], + ['entity::unique', User::class, 'nickName'], + ], + 'email' => [ + 'address::email', + 'type::notEmpty', + ['entity::unique', User::class, 'email'], + ], + 'password' => [ + 'type::notEmpty', + ['string::longer', 6], + ], + 'passwordConfirmation' => [ + ['notEmpty', 'if' => ['withAll' => ['password']]], + ['match', 'password', 'error' => 'Password confirmation does not match'] + ], + ]); + } public function createUser(): User { $user = new User(); - $user->name = $this->getField('name'); - $user->lastName = $this->getField('lastName'); - $user->nickName = $this->getField('nickName'); - $user->email = $this->getField('email'); + $user->name = $this->name; + $user->lastName = $this->lastName; + $user->nickName = $this->nickName; + $user->email = $this->email; $user->defaultCurrencyCode = Currency::DEFAULT_CURRENCY_CODE; return $user; diff --git a/app/src/Request/Tag/CreateRequest.php b/app/src/Request/Tag/CreateRequest.php index 0e45505..1021e77 100644 --- a/app/src/Request/Tag/CreateRequest.php +++ b/app/src/Request/Tag/CreateRequest.php @@ -4,43 +4,57 @@ namespace App\Request\Tag; +use App\Auth\AuthMiddleware; use App\Database\Tag; -use Spiral\Filters\Filter; - -class CreateRequest extends Filter +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Attribute\Input\Header; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; + +class CreateRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'name' => 'data:name', - 'icon' => 'data:icon', - 'color' => 'data:color', - 'user_id' => 'data:user_id', - ]; - - protected const VALIDATES = [ - 'name' => [ - ['is_string'], - ['string::range', 3, 255], - ['type::notEmpty'], - ['string::regexp', '/^[^\s]*$/'], - ['entity::unique', Tag::class, 'name', ['user_id']], - ], - 'icon' => [ - ['is_string'], - ['string::range', 1, 7], - ], - 'color' => [ - ['is_string'], - ['string::regexp', '/^\#[0-9a-fA-F]{6}$/'], - ], - ]; + #[Header(key: AuthMiddleware::HEADER_USER_ID)] + public int $user_id = 0; + + #[Data] + public string $name = ''; + + #[Data] + public string $icon = ''; + + #[Data] + public string $color = ''; + + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition(validationRules: [ + 'name' => [ + ['is_string'], + ['string::range', 3, 255], + ['type::notEmpty'], + ['string::regexp', '/^[^\s]*$/'], + ['entity::unique', Tag::class, 'name', ['user_id']], + ], + 'icon' => [ + ['is_string'], + ['string::range', 1, 7], + ], + 'color' => [ + ['is_string'], + ['string::regexp', '/^\#[0-9a-fA-F]{6}$/'], + ], + ]); + } public function createTag(): Tag { $tag = new Tag(); - $tag->name = $this->getField('name'); - $tag->icon = $this->getField('icon'); - $tag->color = $this->getField('color'); + $tag->name = $this->name; + $tag->icon = $this->icon; + $tag->color = $this->color; return $tag; } diff --git a/app/src/Request/Tag/UpdateRequest.php b/app/src/Request/Tag/UpdateRequest.php index 1cc2ea2..88e44bd 100644 --- a/app/src/Request/Tag/UpdateRequest.php +++ b/app/src/Request/Tag/UpdateRequest.php @@ -4,52 +4,51 @@ namespace App\Request\Tag; +use App\Auth\AuthMiddleware; use App\Database\Tag; -use Spiral\Filters\Filter; - -class UpdateRequest extends Filter +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Attribute\Input\Header; +use Spiral\Filters\Attribute\Input\Route; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; + +class UpdateRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'name' => 'data:name', - 'icon' => 'data:icon', - 'color' => 'data:color', - 'user_id' => 'data:user_id', - ]; - - protected const VALIDATES = [ - 'name' => [ - ['is_string'], - ['string::range', 3, 255], - ['type::notEmpty'], - ['string::regexp', '/^[^\s]*$/'], - ['unique::verify', Tag::class, 'name', ['user_id'], ['id']], - ], - 'icon' => [ - ['is_string'], - ['string::range', 1, 7] - ], - 'color' => [ - ['is_string'], - ['string::regexp', '/^\#[0-9a-fA-F]{6}$/'], - ], - ]; - - public function getName(): string - { - return (string) $this->getField('name'); - } + #[Header(key: AuthMiddleware::HEADER_USER_ID)] + public int $user_id = 0; - public function getIcon(): ?string - { - $value = $this->getField('icon'); + #[Route] + public int $id = 0; - return is_string($value) ? $value : null; - } + #[Data] + public string $name = ''; - public function getColor(): ?string - { - $value = $this->getField('color'); + #[Data] + public ?string $icon = ''; - return is_string($value) ? $value : null; + #[Data] + public ?string $color = ''; + + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition(validationRules: [ + 'name' => [ + ['is_string'], + ['string::range', 3, 255], + ['type::notEmpty'], + ['string::regexp', '/^[^\s]*$/'], + ['unique::verify', Tag::class, 'name', ['user_id'], ['id']], + ], + 'icon' => [ + ['is_string'], + ['string::range', 1, 7] + ], + 'color' => [ + ['is_string'], + ['string::regexp', '/^\#[0-9a-fA-F]{6}$/'], + ], + ]); } } diff --git a/app/src/Request/Wallet/CreateRequest.php b/app/src/Request/Wallet/CreateRequest.php index af2a28a..13f24c7 100644 --- a/app/src/Request/Wallet/CreateRequest.php +++ b/app/src/Request/Wallet/CreateRequest.php @@ -6,48 +6,65 @@ use App\Database\Currency; use App\Database\Wallet; -use Spiral\Filters\Filter; +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; -class CreateRequest extends Filter +class CreateRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'name' => 'data:name', - 'slug' => 'data:slug', - 'isPublic' => 'data:isPublic', - 'defaultCurrencyCode' => 'data:defaultCurrencyCode', - ]; - - protected const VALIDATES = [ - 'name' => [ - 'is_string', - 'type::notEmpty', - ], - 'slug' => [ - 'is_string', - ['string::regexp', '/^[a-zA-Z0-9\-_]*$/'], - ['entity::unique', Wallet::class, 'slug'], - ], - 'isPublic' => [ - 'type::boolean', - ], - 'defaultCurrencyCode' => [ - 'is_string', - ['entity::exists', Currency::class, 'code', 'if' => ['withAll' => ['defaultCurrencyCode']]], - ], - ]; - - /** - * @return \App\Database\Wallet - */ + #[Data] + public string $name = ''; + + #[Data] + public string $slug = ''; + + #[Data] + public bool $isPublic = false; + + #[Data] + public string $defaultCurrencyCode = ''; + + public function filterDefinition(): FilterDefinitionInterface + { + return new FilterDefinition(validationRules: [ + 'name' => [ + 'is_string', + 'type::notEmpty', + ], + 'slug' => [ + 'is_string', + ['string::regexp', '/^[a-zA-Z0-9\-_]*$/'], + ['entity::unique', Wallet::class, 'slug'], + ], + 'isPublic' => [ + 'type::boolean', + ], + 'defaultCurrencyCode' => [ + 'is_string', + ['entity::exists', Currency::class, 'code', 'if' => ['withAll' => ['defaultCurrencyCode']]], + ], + ]); + } + public function createWallet(): Wallet { $wallet = new Wallet(); - $wallet->name = $this->getField('name'); - $wallet->slug = $this->getField('slug', str_slug($wallet->name)); - $wallet->isPublic = $this->getField('isPublic', false); - $wallet->defaultCurrencyCode = $this->getField('defaultCurrencyCode', Currency::DEFAULT_CURRENCY_CODE); - $wallet->totalAmount = 0; + $wallet->name = $this->name; + $wallet->slug = $this->slug; + $wallet->isPublic = $this->isPublic; + $wallet->defaultCurrencyCode = $this->defaultCurrencyCode; + $wallet->totalAmount = 0; + + if (! $wallet->slug) { + $wallet->slug = str_slug($wallet->name); + } + + if (! $wallet->defaultCurrencyCode) { + $wallet->defaultCurrencyCode = Currency::DEFAULT_CURRENCY_CODE; + } return $wallet; } diff --git a/app/src/Request/Wallet/SortSetRequest.php b/app/src/Request/Wallet/SortSetRequest.php index 98f811d..a2d6f94 100644 --- a/app/src/Request/Wallet/SortSetRequest.php +++ b/app/src/Request/Wallet/SortSetRequest.php @@ -5,29 +5,27 @@ namespace App\Request\Wallet; use App\Database\Wallet; -use Spiral\Filters\Filter; +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; -class SortSetRequest extends Filter +class SortSetRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'sort' => 'data:sort', - ]; + #[Data] + public array $sort = []; - protected const VALIDATES = [ - 'sort' => [ - 'notEmpty', - [ - 'arrayOf', ['entity:exists', Wallet::class], - 'error' => 'One or more wallets does not exists', - ], - ], - ]; - - /** - * @throws \Spiral\Models\Exception\EntityExceptionInterface - */ - public function getSort(): array + public function filterDefinition(): FilterDefinitionInterface { - return (array) $this->getField('sort'); + return new FilterDefinition(validationRules: [ + 'sort' => [ + 'notEmpty', + [ + 'arrayOf', ['entity:exists', Wallet::class], + 'error' => 'One or more wallets does not exists', + ], + ], + ]); } } diff --git a/app/src/Request/Wallet/UpdateRequest.php b/app/src/Request/Wallet/UpdateRequest.php index c719d2d..f8734ec 100644 --- a/app/src/Request/Wallet/UpdateRequest.php +++ b/app/src/Request/Wallet/UpdateRequest.php @@ -5,43 +5,38 @@ namespace App\Request\Wallet; use App\Database\Currency; -use Spiral\Filters\Filter; +use Spiral\Filters\Attribute\Input\Data; +use Spiral\Filters\Model\Filter; +use Spiral\Filters\Model\FilterDefinitionInterface; +use Spiral\Filters\Model\HasFilterDefinition; +use Spiral\Validator\FilterDefinition; -class UpdateRequest extends Filter +class UpdateRequest extends Filter implements HasFilterDefinition { - protected const SCHEMA = [ - 'name' => 'data:name', - 'isPublic' => 'data:isPublic', - 'defaultCurrencyCode' => 'data:defaultCurrencyCode', - ]; + #[Data] + public string $name = ''; - protected const VALIDATES = [ - 'name' => [ - 'is_string', - 'type::notEmpty', - ], - 'isPublic' => [ - 'type::boolean', - ], - 'defaultCurrencyCode' => [ - 'is_string', - 'type::notEmpty', - ['entity::exists', Currency::class, 'code',], - ], - ]; + #[Data] + public bool $isPublic = false; - public function getName(): string - { - return (string) $this->getField('name'); - } - - public function getIsPublic(): bool - { - return (bool) $this->getField('isPublic', false); - } + #[Data] + public string $defaultCurrencyCode = ''; - public function getDefaultCurrencyCode(): string + public function filterDefinition(): FilterDefinitionInterface { - return (string) $this->getField('defaultCurrencyCode'); + return new FilterDefinition(validationRules: [ + 'name' => [ + 'is_string', + 'type::notEmpty', + ], + 'isPublic' => [ + 'type::boolean', + ], + 'defaultCurrencyCode' => [ + 'is_string', + 'type::notEmpty', + ['entity::exists', Currency::class, 'code',], + ], + ]); } } diff --git a/app/src/Security/PasswordChecker.php b/app/src/Security/PasswordChecker.php index 1ee132f..63dfcfd 100644 --- a/app/src/Security/PasswordChecker.php +++ b/app/src/Security/PasswordChecker.php @@ -5,6 +5,7 @@ namespace App\Security; use App\Service\Auth\AuthService; +use Cycle\ORM\ORMInterface; use Spiral\Validator\AbstractChecker; class PasswordChecker extends AbstractChecker @@ -13,30 +14,25 @@ class PasswordChecker extends AbstractChecker 'verify' => 'Wrong password.' ]; - /** - * @var \App\Service\Auth\AuthService - */ - private $auth; - - /** - * PasswordChecker constructor. - * - * @param \App\Service\Auth\AuthService $auth - */ - public function __construct(AuthService $auth) - { - $this->auth = $auth; + public function __construct( + private AuthService $auth, + private ORMInterface $orm + ) { } - /** - * Expect an instance of PasswordContainerInterface in request context. - * - * @param mixed $value - * @return bool - */ - public function verify($value): bool + public function verify(mixed $value, string $role, string $field = 'id'): bool { - $entity = $this->getValidator()->getContext(); + if (!$this->getValidator()->hasValue($field) || !class_exists($role)) { + return false; + } + + /** @var \Cycle\ORM\Select\Repository $repository */ + $repository = $this->orm->getRepository($role); + + $select = $repository->select(); + $select->where($field, $this->getValidator()->getValue($field)); + $entity = $select->fetchOne(); + if (! $entity instanceof PasswordContainerInterface) { return false; } diff --git a/tests/Feature/Controller/Profile/PhotoControllerTest.php b/tests/Feature/Controller/Profile/PhotoControllerTest.php index fe4aee2..8b61e6d 100644 --- a/tests/Feature/Controller/Profile/PhotoControllerTest.php +++ b/tests/Feature/Controller/Profile/PhotoControllerTest.php @@ -78,27 +78,6 @@ public function testUpdatePhoto(): void ]); } - public function testUpdatePhotoValidationFails(): void - { - $auth = $this->makeAuth($this->userFactory->create()); - - $fileMock = $this->getMockUploadedFile(); - $requestMock = $this->getMockUpdatePhotoRequest($fileMock, true); - $storageMock = $this->getMockStorageService(); - - $this->getContainer()->bind(UpdatePhotoRequest::class, fn () => $requestMock); - $this->getContainer()->bind(PhotoStorageService::class, fn () => $storageMock); - - $response = $this->withAuth($auth)->put('/profile/photo'); - - $response->assertUnprocessable(); - - $body = $this->getJsonResponseBody($response); - - $this->assertArrayHasKey('errors', $body); - $this->assertArrayHasKey('photo', $body['errors']); - } - public function testUpdatePhotoStoreFails(): void { $auth = $this->makeAuth($this->userFactory->create()); @@ -175,14 +154,10 @@ private function getMockUpdatePhotoRequest(UploadedFileInterface $file, bool $is { $mock = $this->getMockBuilder(UpdatePhotoRequest::class) ->disableOriginalConstructor() - ->onlyMethods(['isValid', 'setContext', 'getPhoto', 'getErrors']) ->getMock(); - $mock->method('isValid')->willReturn(!$isInvalid); + $mock->photo = $file; $mock->method('getPhoto')->willReturn($file); - $mock->method('getErrors')->willReturn( - $isInvalid ? ['photo' => ['Validation error']] : [], - ); return $mock; } diff --git a/tests/Feature/Controller/Profile/ProfileControllerTest.php b/tests/Feature/Controller/Profile/ProfileControllerTest.php index 142d887..5935bad 100644 --- a/tests/Feature/Controller/Profile/ProfileControllerTest.php +++ b/tests/Feature/Controller/Profile/ProfileControllerTest.php @@ -294,14 +294,12 @@ public function testUpdateFailsWithMissingCurrency(): void $requestMock = $this->getMockBuilder(UpdateBasicRequest::class) ->disableOriginalConstructor() - ->onlyMethods(['setContext', 'isValid', 'getName', 'getLastName', 'getNickName', 'getDefaultCurrencyCode']) ->getMock(); - $requestMock->method('isValid')->willReturn(true); - $requestMock->method('getName')->willReturn($newProfile->name); - $requestMock->method('getLastName')->willReturn($newProfile->lastName); - $requestMock->method('getNickName')->willReturn($newProfile->nickName); - $requestMock->method('getDefaultCurrencyCode')->willReturn($missingCurrencyCode); + $requestMock->name = $newProfile->name; + $requestMock->lastName = $newProfile->lastName; + $requestMock->nickName = $newProfile->nickName; + $requestMock->defaultCurrencyCode = $missingCurrencyCode; $repositoryMock = $this->getMockBuilder(CurrencyRepository::class) ->disableOriginalConstructor() diff --git a/tests/Feature/Controller/Tags/TagsControllerTest.php b/tests/Feature/Controller/Tags/TagsControllerTest.php index 8fc81dd..df0e201 100644 --- a/tests/Feature/Controller/Tags/TagsControllerTest.php +++ b/tests/Feature/Controller/Tags/TagsControllerTest.php @@ -227,7 +227,9 @@ public function testUpdateMissingTagReturnNotFound(): void $tagId = Fixtures::integer(); - $response = $this->withAuth($auth)->put("/tags/{$tagId}"); + $response = $this->withAuth($auth)->put("/tags/{$tagId}", [ + 'name' => Fixtures::string() + ]); $response->assertNotFound(); } @@ -238,7 +240,9 @@ public function testUpdateForeignTagReturnNotFound(): void $tag = $this->tagFactory->forUser($this->userFactory->create())->create(); - $response = $this->withAuth($auth)->put("/tags/{$tag->id}"); + $response = $this->withAuth($auth)->put("/tags/{$tag->id}", [ + 'name' => Fixtures::string() + ]); $response->assertNotFound(); } diff --git a/tests/Feature/Controller/Wallets/WalletsControllerTest.php b/tests/Feature/Controller/Wallets/WalletsControllerTest.php index 25e3b85..683273f 100644 --- a/tests/Feature/Controller/Wallets/WalletsControllerTest.php +++ b/tests/Feature/Controller/Wallets/WalletsControllerTest.php @@ -8,6 +8,7 @@ use App\Service\WalletService; use PHPUnit\Framework\MockObject\MockObject; use Tests\DatabaseTransaction; +use Tests\Factories\CurrencyFactory; use Tests\Factories\UserFactory; use Tests\Factories\WalletFactory; use Tests\Fixtures; @@ -184,7 +185,10 @@ public function testUpdateMissingWalletReturnNotFound(): void $walletId = Fixtures::integer(); - $response = $this->withAuth($auth)->put("/wallets/{$walletId}"); + $response = $this->withAuth($auth)->put("/wallets/{$walletId}", [ + 'name' => Fixtures::string(), + 'defaultCurrencyCode' => CurrencyFactory::code(), + ]); $response->assertNotFound(); } @@ -195,7 +199,10 @@ public function testUpdateNonMemberWalletReturnNotFound(): void $wallet = $this->walletFactory->create(); - $response = $this->withAuth($auth)->put("/wallets/{$wallet->id}"); + $response = $this->withAuth($auth)->put("/wallets/{$wallet->id}", [ + 'name' => Fixtures::string(), + 'defaultCurrencyCode' => CurrencyFactory::code(), + ]); $response->assertNotFound(); } diff --git a/tests/Feature/Request/Profile/RefreshTokenRequestTest.php b/tests/Feature/Request/Profile/RefreshTokenRequestTest.php deleted file mode 100644 index e0a2dca..0000000 --- a/tests/Feature/Request/Profile/RefreshTokenRequestTest.php +++ /dev/null @@ -1,26 +0,0 @@ -getMockBuilder(RefreshTokenRequest::class) - ->disableOriginalConstructor() - ->onlyMethods(['getField']) - ->getMock(); - - $request->method('getField')->with('accessToken')->willReturn($token); - - $this->assertEquals($token, $request->getAccessToken()); - } -} diff --git a/tests/Feature/Request/Profile/UpdatePhotoRequestTest.php b/tests/Feature/Request/Profile/UpdatePhotoRequestTest.php index d1aa354..4745086 100644 --- a/tests/Feature/Request/Profile/UpdatePhotoRequestTest.php +++ b/tests/Feature/Request/Profile/UpdatePhotoRequestTest.php @@ -13,26 +13,18 @@ class UpdatePhotoRequestTest extends TestCase { public function testGetPhoto(): void { - $request = $this->getMockBuilder(UpdatePhotoRequest::class) - ->disableOriginalConstructor() - ->onlyMethods(['getField']) - ->getMock(); + $request = new UpdatePhotoRequest(); $file = $this->getMockBuilder(UploadedFileInterface::class)->getMock(); - $request->method('getField')->with('photo')->willReturn($file); + $request->photo = $file; $this->assertEquals($file, $request->getPhoto()); } public function testGetPhotoThrownException(): void { - $request = $this->getMockBuilder(UpdatePhotoRequest::class) - ->disableOriginalConstructor() - ->onlyMethods(['getField']) - ->getMock(); - - $request->method('getField')->with('photo')->willReturn(null); + $request = new UpdatePhotoRequest(); $this->expectException(UploadedFileErrorException::class); diff --git a/tests/Feature/Security/PasswordCheckerTest.php b/tests/Feature/Security/PasswordCheckerTest.php index a5b91ef..e4cf968 100644 --- a/tests/Feature/Security/PasswordCheckerTest.php +++ b/tests/Feature/Security/PasswordCheckerTest.php @@ -4,24 +4,52 @@ namespace Tests\Feature\Security; +use App\Database\User; use App\Security\PasswordChecker; use App\Service\Auth\AuthService; +use Cycle\ORM\ORMInterface; +use Cycle\ORM\Select; use Spiral\Validation\ValidatorInterface; use Tests\Fixtures; use Tests\TestCase; class PasswordCheckerTest extends TestCase { - public function testVerifyEmptyContext(): void + public function testVerifyEmptyValue(): void { $authService = $this->getMockBuilder(AuthService::class)->disableOriginalConstructor()->getMock(); - + + $orm = $this->getMockBuilder(ORMInterface::class)->getMock(); + $validator = $this->getMockBuilder(ValidatorInterface::class)->getMock(); - $validator->method('getContext')->willReturn(null); + $validator->method('hasValue')->willReturn(false); + + $checker = new PasswordChecker($authService, $orm); + + $this->assertFalse($checker->check($validator, 'verify', '', Fixtures::string(), [User::class])); + } + + public function testVerifyEmptyEntity(): void + { + $select = $this->getMockBuilder(Select::class)->disableOriginalConstructor()->getMock(); + $select->method('fetchOne')->willReturn(null); + + $repository = $this->getMockBuilder(Select\Repository::class)->disableOriginalConstructor()->getMock(); + $repository->method('select')->willReturn($select); + + $orm = $this->getMockBuilder(ORMInterface::class)->getMock(); + $orm->method('getRepository')->willReturn($repository); + + $validator = $this->getMockBuilder(ValidatorInterface::class)->getMock(); + + $validator->method('hasValue')->willReturn(true); + $validator->method('getValue')->willReturn(1); + + $authService = $this->getMockBuilder(AuthService::class)->disableOriginalConstructor()->getMock(); + + $checker = new PasswordChecker($authService, $orm); - $checker = new PasswordChecker($authService); - - $this->assertFalse($checker->check($validator, 'verify', '', Fixtures::string())); + $this->assertFalse($checker->check($validator, 'verify', '', Fixtures::string(), [User::class])); } } From da1c32b260d8b3655666fe7fb9d6bbaac23224e2 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sat, 25 Mar 2023 15:52:02 +0200 Subject: [PATCH 16/20] Improve tests coverage --- app/src/Bootloader/LoggingBootloader.php | 11 +------ .../Bootloader/LoggingBootloaderTest.php | 32 +++++++++++++++++++ .../Profile/UpdatePhotoRequestTest.php | 9 ++++++ .../Request/Wallet/CreateRequestTest.php | 23 +++++++++++++ 4 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 tests/Feature/Bootloader/LoggingBootloaderTest.php create mode 100644 tests/Feature/Request/Wallet/CreateRequestTest.php diff --git a/app/src/Bootloader/LoggingBootloader.php b/app/src/Bootloader/LoggingBootloader.php index 2a46ffa..3d04fb3 100644 --- a/app/src/Bootloader/LoggingBootloader.php +++ b/app/src/Bootloader/LoggingBootloader.php @@ -21,10 +21,7 @@ class LoggingBootloader extends Bootloader */ const DEFAULT_CHANNEL = 'default'; - /** - * @param \Spiral\Monolog\Bootloader\MonologBootloader $monolog - */ - public function boot(MonologBootloader $monolog, EnvironmentInterface $env): void + public function init(MonologBootloader $monolog, EnvironmentInterface $env): void { $this->configureCommonHandlers($monolog); @@ -33,9 +30,6 @@ public function boot(MonologBootloader $monolog, EnvironmentInterface $env): voi } } - /** - * @param \Spiral\Monolog\Bootloader\MonologBootloader $monolog - */ private function configureCommonHandlers(MonologBootloader $monolog): void { // app level errors @@ -58,9 +52,6 @@ private function configureCommonHandlers(MonologBootloader $monolog): void ); } - /** - * @param \Spiral\Monolog\Bootloader\MonologBootloader $monolog - */ private function configureDebugHandlers(MonologBootloader $monolog): void { // debug and info messages via global LoggerInterface diff --git a/tests/Feature/Bootloader/LoggingBootloaderTest.php b/tests/Feature/Bootloader/LoggingBootloaderTest.php new file mode 100644 index 0000000..91a501c --- /dev/null +++ b/tests/Feature/Bootloader/LoggingBootloaderTest.php @@ -0,0 +1,32 @@ +getMockBuilder(EnvironmentInterface::class)->getMock(); + $env->expects($this->once())->method('get')->with('DEBUG')->willReturn(true); + + $config = $this->getMockBuilder(ConfiguratorInterface::class)->getMock(); + + $monolog = new MonologBootloader($config); + + $this->getContainer()->scope(function (MonologBootloader $monologBootloader, EnvironmentInterface $environment) { + $bootloader = new LoggingBootloader(); + $bootloader->init($monologBootloader, $environment); + }, [ + EnvironmentInterface::class => fn () => $env, + MonologBootloader::class => fn () => $monolog, + ]); + } +} diff --git a/tests/Feature/Request/Profile/UpdatePhotoRequestTest.php b/tests/Feature/Request/Profile/UpdatePhotoRequestTest.php index 4745086..b916b84 100644 --- a/tests/Feature/Request/Profile/UpdatePhotoRequestTest.php +++ b/tests/Feature/Request/Profile/UpdatePhotoRequestTest.php @@ -7,10 +7,19 @@ use App\Request\Profile\UpdatePhotoRequest; use Laminas\Diactoros\Exception\UploadedFileErrorException; use Psr\Http\Message\UploadedFileInterface; +use Spiral\Validator\FilterDefinition; use Tests\TestCase; class UpdatePhotoRequestTest extends TestCase { + public function testFilterDefinition(): void + { + $request = new UpdatePhotoRequest(); + + $filter = $request->filterDefinition(); + $this->assertInstanceOf(FilterDefinition::class, $filter); + } + public function testGetPhoto(): void { $request = new UpdatePhotoRequest(); diff --git a/tests/Feature/Request/Wallet/CreateRequestTest.php b/tests/Feature/Request/Wallet/CreateRequestTest.php new file mode 100644 index 0000000..4ccea47 --- /dev/null +++ b/tests/Feature/Request/Wallet/CreateRequestTest.php @@ -0,0 +1,23 @@ +name = 'Test wallet'; + + $wallet = $request->createWallet(); + + $this->assertEquals('test-wallet', $wallet->slug); + $this->assertEquals(Currency::DEFAULT_CURRENCY_CODE, $wallet->defaultCurrencyCode); + } +} From abff80dbcade8da7e50577d4cd01ee8c922121e0 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sat, 25 Mar 2023 17:03:46 +0200 Subject: [PATCH 17/20] Improve tests coverage --- tests/Feature/Config/S3ConfigTest.php | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/Feature/Config/S3ConfigTest.php diff --git a/tests/Feature/Config/S3ConfigTest.php b/tests/Feature/Config/S3ConfigTest.php new file mode 100644 index 0000000..21c1c55 --- /dev/null +++ b/tests/Feature/Config/S3ConfigTest.php @@ -0,0 +1,30 @@ +getContainer()->get(S3Config::class); + + $class = new \ReflectionClass($config); + $class->getProperty('config')->setValue($config, [ + 'region' => Fixtures::string(), + 'endpoint' => Fixtures::url(), + 'key' => Fixtures::string(), + 'secret' => Fixtures::string(), + ]); + + $this->assertNotEmpty($config->getRegion()); + $this->assertNotEmpty($config->getEndpoint()); + $this->assertNotEmpty($config->getKey()); + $this->assertNotEmpty($config->getSecret()); + } +} From 99f01e2f0e980bb0166193df5a66cce4f79650e0 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sat, 25 Mar 2023 18:03:55 +0200 Subject: [PATCH 18/20] Improve tests coverage --- app/src/Bootloader/ExceptionHandlerBootloader.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/Bootloader/ExceptionHandlerBootloader.php b/app/src/Bootloader/ExceptionHandlerBootloader.php index 108c288..3800ba0 100644 --- a/app/src/Bootloader/ExceptionHandlerBootloader.php +++ b/app/src/Bootloader/ExceptionHandlerBootloader.php @@ -38,11 +38,7 @@ public function init(AbstractKernel $kernel): void // is running in the console. $this->handler->addRenderer(new ConsoleRenderer()); - $kernel->running(function (): void { - // Register the JSON renderer, that will be used when the application is - // running in the HTTP context and a JSON response is expected. - $this->handler->addRenderer(new JsonRenderer()); - }); + $kernel->running(\Closure::fromCallable([$this, 'addRenderer'])); } public function boot(LoggerReporter $logger, FileReporter $files): void @@ -55,4 +51,11 @@ public function boot(LoggerReporter $logger, FileReporter $files): void // known as snapshot. $this->handler->addReporter($files); } + + public function addRenderer(): void + { + // Register the JSON renderer, that will be used when the application is + // running in the HTTP context and a JSON response is expected. + $this->handler->addRenderer(new JsonRenderer()); + } } From 95f32dd67fcae0447b91370b0977774548507ae2 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sat, 25 Mar 2023 18:56:19 +0200 Subject: [PATCH 19/20] Improve tests coverage --- .../ExceptionHandlerBootloaderTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/Feature/Bootloader/ExceptionHandlerBootloaderTest.php diff --git a/tests/Feature/Bootloader/ExceptionHandlerBootloaderTest.php b/tests/Feature/Bootloader/ExceptionHandlerBootloaderTest.php new file mode 100644 index 0000000..9399036 --- /dev/null +++ b/tests/Feature/Bootloader/ExceptionHandlerBootloaderTest.php @@ -0,0 +1,17 @@ +getContainer()->get(ExceptionHandlerBootloader::class); + $this->assertEmpty($bootloader->addRenderer()); + } +} From 87a6d4d353113ee66ba723cb133ac17912d98d46 Mon Sep 17 00:00:00 2001 From: Volodymyr Komarov Date: Sat, 25 Mar 2023 20:04:24 +0200 Subject: [PATCH 20/20] Improve tests coverage --- app/src/Bootloader/S3Bootloader.php | 3 +++ tests/TestCase.php | 7 ------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/src/Bootloader/S3Bootloader.php b/app/src/Bootloader/S3Bootloader.php index 20ff1f9..324ec96 100644 --- a/app/src/Bootloader/S3Bootloader.php +++ b/app/src/Bootloader/S3Bootloader.php @@ -10,6 +10,9 @@ use Spiral\Boot\Bootloader\Bootloader; use Spiral\Core\Container; +/** + * Note: It uses ~10MB of memory + */ class S3Bootloader extends Bootloader { /** diff --git a/tests/TestCase.php b/tests/TestCase.php index 9ac59b8..6c1a87c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -28,11 +28,6 @@ abstract class TestCase extends BaseTestCase use InteractsWithMock; use AssertHelpers; - protected function getFakeS3(): S3ClientInterface - { - return $this->getMockBuilder(S3ClientInterface::class)->getMock(); - } - protected function setUp(): void { $this->beforeBooting(static function (ConfiguratorInterface $config): void { @@ -45,8 +40,6 @@ protected function setUp(): void parent::setUp(); - $this->getContainer()->bindSingleton(S3ClientInterface::class, $this->getFakeS3()); - $this->getContainer()->get(TranslatorInterface::class)->setLocale('en'); $this->scopedDatabase();