From 1e99455b652c0320e266353bdd148d6cd002afda Mon Sep 17 00:00:00 2001 From: Akolzin Dmitry Date: Wed, 31 Mar 2021 11:00:48 +0300 Subject: [PATCH] Message handlers for symfony messenger --- DependencyInjection/Configuration.php | 6 + .../RetailCrmServiceExtension.php | 36 +++++ Messenger/CommandMessage.php | 99 ++++++++++++ Messenger/MessageHandler.php | 40 +++++ .../MessageHandler/InNewProcessRunner.php | 109 +++++++++++++ Messenger/MessageHandler/JobRunner.php | 18 +++ .../MessageHandler/SimpleConsoleRunner.php | 66 ++++++++ Resources/doc/Messenger.md | 60 +++++++ Resources/doc/Requests.md | 71 +++++++++ Resources/doc/Security.md | 70 ++++++++ Resources/doc/index.md | 149 +----------------- Tests/DataFixtures/TestMessage.php | 15 ++ .../DependencyInjection/ConfigurationTest.php | 7 + .../RetailCrmServiceExtensionTest.php | 3 + Tests/Fixtures/App/Kernel.php | 33 ++++ Tests/Fixtures/App/TestCommand.php | 36 +++++ Tests/Fixtures/App/TestCommandMessage.php | 15 ++ Tests/Fixtures/bin/console | 9 ++ Tests/Messenger/CommandMessageTest.php | 38 +++++ .../MessageHandler/InNewProcessRunnerTest.php | 32 ++++ .../SimpleConsoleRunnerTest.php | 32 ++++ Tests/Messenger/MessageHandlerTest.php | 26 +++ .../CallbackClientAuthenticatorTest.php | 35 +++- .../FrontApiClientAuthenticatorTest.php | 41 ++++- composer.json | 6 +- phpunit.xml.dist | 3 + 26 files changed, 905 insertions(+), 150 deletions(-) create mode 100644 Messenger/CommandMessage.php create mode 100644 Messenger/MessageHandler.php create mode 100644 Messenger/MessageHandler/InNewProcessRunner.php create mode 100644 Messenger/MessageHandler/JobRunner.php create mode 100644 Messenger/MessageHandler/SimpleConsoleRunner.php create mode 100644 Resources/doc/Messenger.md create mode 100644 Resources/doc/Requests.md create mode 100644 Resources/doc/Security.md create mode 100644 Tests/DataFixtures/TestMessage.php create mode 100644 Tests/Fixtures/App/Kernel.php create mode 100644 Tests/Fixtures/App/TestCommand.php create mode 100644 Tests/Fixtures/App/TestCommandMessage.php create mode 100644 Tests/Fixtures/bin/console create mode 100644 Tests/Messenger/CommandMessageTest.php create mode 100644 Tests/Messenger/MessageHandler/InNewProcessRunnerTest.php create mode 100644 Tests/Messenger/MessageHandler/SimpleConsoleRunnerTest.php create mode 100644 Tests/Messenger/MessageHandlerTest.php diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 557d96d..96d7c6b 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -53,6 +53,12 @@ class Configuration implements ConfigurationInterface ->end() ->end() ->end() + ->arrayNode('messenger') + ->children() + ->scalarNode('message_handler')->isRequired()->defaultValue('simple_console_runner')->end() + ->scalarNode('process_timeout')->end() + ->end() + ->end() ->end(); return $treeBuilder; diff --git a/DependencyInjection/RetailCrmServiceExtension.php b/DependencyInjection/RetailCrmServiceExtension.php index d32f08e..4e27c45 100644 --- a/DependencyInjection/RetailCrmServiceExtension.php +++ b/DependencyInjection/RetailCrmServiceExtension.php @@ -4,6 +4,7 @@ namespace RetailCrm\ServiceBundle\DependencyInjection; use RetailCrm\ServiceBundle\ArgumentResolver\CallbackValueResolver; use RetailCrm\ServiceBundle\ArgumentResolver\ClientValueResolver; +use RetailCrm\ServiceBundle\Messenger\MessageHandler; use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory; use RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator; use RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator; @@ -49,6 +50,18 @@ class RetailCrmServiceExtension extends Extension $config['request_schema']['client']['serializer'] ); + $container->setParameter( + 'retail_crm_service.messenger.message_handler', + $config['messenger']['message_handler'] + ); + + if (isset($config['messenger']['process_timeout'])) { + $container->setParameter( + 'retail_crm_service.messenger.process_timeout', + $config['messenger']['process_timeout'] + ); + } + $container ->register(SymfonySerializerAdapter::class) ->setAutowired(true); @@ -90,5 +103,28 @@ class RetailCrmServiceExtension extends Extension $container ->register(FrontApiClientAuthenticator::class) ->setAutowired(true); + + $container + ->register(MessageHandler\SimpleConsoleRunner::class) + ->setAutowired(true); + $container->setAlias('simple_console_runner', MessageHandler\SimpleConsoleRunner::class); + + $timeout = $container->hasParameter('retail_crm_service.messenger.process_timeout') + ? $container->getParameter('retail_crm_service.messenger.process_timeout') + : null; + + $container + ->register(MessageHandler\InNewProcessRunner::class) + ->setArgument('$timeout', $timeout) + ->setAutowired(true); + $container->setAlias('in_new_process_runner', MessageHandler\InNewProcessRunner::class); + + $container + ->register(MessageHandler::class) + ->addTag('messenger.message_handler') + ->setArguments([ + new Reference($container->getParameter('retail_crm_service.messenger.message_handler')) + ]) + ->setAutowired(true); } } diff --git a/Messenger/CommandMessage.php b/Messenger/CommandMessage.php new file mode 100644 index 0000000..9ea7cde --- /dev/null +++ b/Messenger/CommandMessage.php @@ -0,0 +1,99 @@ +commandName; + } + + /** + * @param string $commandName + */ + public function setCommandName(string $commandName): void + { + $this->commandName = $commandName; + } + + /** + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * @param array $options + */ + public function setOptions(array $options): void + { + $this->options = $options; + } + + /** + * @return array + */ + public function getArguments(): array + { + return $this->arguments; + } + + /** + * @param array $arguments + */ + public function setArguments(array $arguments): void + { + $this->arguments = $arguments; + } + + /** + * @param string $key + * @param string $value + */ + public function addOption(string $key, string $value): void + { + $this->options[$key] = $value; + } + + /** + * @param string $key + * @param string $value + */ + public function addArgument(string $key, string $value): void + { + $this->arguments[$key] = $value; + } + + /** + * @return array + */ + public function getFormattedOptions(): array + { + $options = []; + foreach ($this->options as $option => $value) { + $options['--' . $option] = $value; + } + + return $options; + } +} diff --git a/Messenger/MessageHandler.php b/Messenger/MessageHandler.php new file mode 100644 index 0000000..6f38fa3 --- /dev/null +++ b/Messenger/MessageHandler.php @@ -0,0 +1,40 @@ +runner = $runner; + } + + /** + * @param CommandMessage $message + * + * @throws Exception + */ + public function __invoke(CommandMessage $message): void + { + $this->runner->run($message); + } +} diff --git a/Messenger/MessageHandler/InNewProcessRunner.php b/Messenger/MessageHandler/InNewProcessRunner.php new file mode 100644 index 0000000..da0abff --- /dev/null +++ b/Messenger/MessageHandler/InNewProcessRunner.php @@ -0,0 +1,109 @@ +logger = $logger; + $this->kernel = $kernel; + + if (null !== $timeout) { + $this->timeout = $timeout; + } + } + + /** + * {@inheritdoc} + */ + public function run(CommandMessage $message): void + { + $phpBinaryPath = (new PhpExecutableFinder)->find(); + $consoleCommand = [ + 'php' => $phpBinaryPath ?: 'php', + 'console' => sprintf('%s/bin/console', $this->kernel->getContainer()->getParameter('kernel.project_dir')), + 'command' => $message->getCommandName() + ]; + + $process = new Process( + array_merge( + array_values($consoleCommand), + array_values($message->getArguments()), + array_values($this->getOptions($message)), + ) + ); + + try { + $process + ->setTimeout($this->timeout) + ->run(static function(string $type, string $buffer) { + echo $buffer; + }) + ; + } catch (ProcessTimedOutException $processTimedOutException) { + $this->logger->error( + sprintf( + 'Process "%s" killed after %d seconds of execution', + $processTimedOutException->getProcess()->getCommandLine(), + $processTimedOutException->getProcess()->getTimeout() + ) + ); + } + } + + /** + * @param CommandMessage $message + * + * @return array + */ + private function getOptions(CommandMessage $message): array + { + $options = []; + foreach ($message->getFormattedOptions() as $option => $value) { + $options[] = $option . '=' . $value; + } + + return $options; + } +} diff --git a/Messenger/MessageHandler/JobRunner.php b/Messenger/MessageHandler/JobRunner.php new file mode 100644 index 0000000..350e847 --- /dev/null +++ b/Messenger/MessageHandler/JobRunner.php @@ -0,0 +1,18 @@ +logger = $logger; + $this->kernel = $kernel; + } + + /** + * {@inheritdoc} + */ + public function run(CommandMessage $message): void + { + $application = new Application($this->kernel); + $application->setAutoExit(false); + + $input = new ArrayInput( + array_merge( + ['command' => $message->getCommandName()], + $message->getFormattedOptions(), + $message->getArguments() + ) + ); + + $output = new BufferedOutput(); + if ($application->run($input, $output) > 0) { + $this->logger->error($output->fetch()); + + return; + } + + echo $output->fetch(); + } +} diff --git a/Resources/doc/Messenger.md b/Resources/doc/Messenger.md new file mode 100644 index 0000000..d68f900 --- /dev/null +++ b/Resources/doc/Messenger.md @@ -0,0 +1,60 @@ +### Messenger + +#### Messages + +The library provides a basic message for executing console commands as message handlers - `RetailCrm\ServiceBundle\Messenger\CommandMessage`. +This makes it easier to create new message types. For example: + +* Create your message + +```php + +namespace App\Message; + +use RetailCrm\ServiceBundle\Messenger\CommandMessage; + +class MyMessage extends CommandMessage +{ + public function __construct() + { + $this->commandName = \App\Command\MyCommand::getDefaultName(); + $this->options = ['optionName' => 'optionValue']; + $this->arguments = ['argumentName' => 'argumentValue']; + } +} + +``` + +* Add a message to a routing + +```yaml +# config/packages/messenger.yaml +framework: + messenger: + transports: + async: "%env(MESSENGER_TRANSPORT_DSN)%" + + routing: + 'App\Message\MyMessage': async +``` + +Now when sending this message through messenger (```$messageBus->dispatch(new MyMessage())```) the message handler will run the associated command + +#### Message handlers + +Two messages handlers are is supported: + +* all messages handling in one worker process +* each message is a processed in a separate process + +By default, messages will be processed in one process. To set up a handler to run in a separate process, add to the bundle config: + +```yaml +retail_crm_service: + messenger: + message_handler: in_new_process_runner + process_timeout: 60 +``` + +`process_timeout` - an optional parameter that only makes sense when `message_handler` is equal `in_new_process_runner` and determines the lifetime of the process. +By default, process timeout - 3600 (in seconds). diff --git a/Resources/doc/Requests.md b/Resources/doc/Requests.md new file mode 100644 index 0000000..a70866d --- /dev/null +++ b/Resources/doc/Requests.md @@ -0,0 +1,71 @@ +### Deserialize incoming requests + +#### Callbacks (form data) + +To automatically get the callback request parameter + +```php + +class AppController extends AbstractController +{ + public function activityAction(\App\Dto\Callback\Activity $activity): Response + { + // handle activity + } +} + +``` + +add to the config: + +```yaml +retail_crm_service: + request_schema: + callback: + supports: + - { type: App\Dto\Callback\Activity, params: ["activity"] } +``` + +request automatically will be deserialization to $activity. + +#### Body json content + +```php + +class AppController extends AbstractController +{ + public function someAction(\App\Dto\Body $activity): Response + { + // handle activity + } +} + +``` + +add to the config: + +```yaml +retail_crm_service: + request_schema: + client: + supports: + - App\Dto\Body +``` + +#### Serializers +At this time supported [Symfony serializer](https://symfony.com/doc/current/components/serializer.html) and [JMS serializer](https://jmsyst.com/libs/serializer). +By default, the library using a Symfony serializer. For use JMS install JMS serializer bundle - `composer require jms/serializer-bundle` +You can explicitly specify the type of serializer used for request schema: + +```yaml +retail_crm_service: + request_schema: + client: + supports: + # types + serializer: retail_crm_service.symfony_serializer.adapter # or retail_crm_service.jms_serializer.adapter + callback: + supports: + # types + serializer: retail_crm_service.jms_serializer.adapter # or retail_crm_service.symfony_serializer.adapter +``` diff --git a/Resources/doc/Security.md b/Resources/doc/Security.md new file mode 100644 index 0000000..9de58c1 --- /dev/null +++ b/Resources/doc/Security.md @@ -0,0 +1,70 @@ +### Authentication + +Example security configuration: + +```yaml +security: + providers: + client: + entity: + class: 'App\Entity\Connection' # must implements UserInterface + property: 'clientId' + firewalls: + api: + pattern: ^/api + provider: client + anonymous: ~ + lazy: true + stateless: false + guard: + authenticators: + - RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator + callback: + pattern: ^/callback + provider: client + anonymous: ~ + lazy: true + stateless: true + guard: + authenticators: + - RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator + main: + anonymous: true + lazy: true + + access_control: + - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } # login for programmatically authentication user + - { path: ^/api, roles: ROLE_USER } + - { path: ^/callback, roles: ROLE_USER } +``` + +To authenticate the user after creating it, you can use the following code + +```php + +use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; +use RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class AppController extends AbstractController +{ + public function someAction( + Request $request, + GuardAuthenticatorHandler $guardAuthenticatorHandler, + FrontApiClientAuthenticator $frontApiClientAuthenticator, + ConnectionManager $manager + ): Response { + $user = $manager->getUser(); // getting user + + $guardAuthenticatorHandler->authenticateUserAndHandleSuccess( + $user, + $request, + $frontApiClientAuthenticator, + 'api' + ); + // ... + } +} + +``` diff --git a/Resources/doc/index.md b/Resources/doc/index.md index 443faab..53c0418 100644 --- a/Resources/doc/index.md +++ b/Resources/doc/index.md @@ -2,8 +2,6 @@ `composer require retailcrm/service-bundle` -## Usage - Enable bundle in `config/bundles.php`: ```php @@ -23,147 +21,10 @@ retail_crm_service: request_schema: callback: ~ client: ~ + messenger: ~ ``` -### Deserializing incoming requests - -#### Callbacks (form data) - -To automatically get the callback request parameter - -```php - -class AppController extends AbstractController -{ - public function activityAction(\App\Dto\Callback\Activity $activity): Response - { - // handle activity - } -} - -``` - -add to the config: - -```yaml -retail_crm_service: - request_schema: - callback: - supports: - - { type: App\Dto\Callback\Activity, params: ["activity"] } -``` - -request automatically will be deserialization to $activity. - -#### Body json content - -```php - -class AppController extends AbstractController -{ - public function someAction(\App\Dto\Body $activity): Response - { - // handle activity - } -} - -``` - -add to the config: - -```yaml -retail_crm_service: - request_schema: - client: - supports: - - App\Dto\Body -``` - -#### Serializers -At this time supported [Symfony serializer](https://symfony.com/doc/current/components/serializer.html) and [JMS serializer](https://jmsyst.com/libs/serializer). -By default, the library using a Symfony serializer. For use JMS install JMS serializer bundle - `composer require jms/serializer-bundle` -You can explicitly specify the type of serializer used for request schema: - -```yaml -retail_crm_service: - request_schema: - client: - supports: - # types - serializer: retail_crm_service.symfony_serializer.adapter # or retail_crm_service.jms_serializer.adapter - callback: - supports: - # types - serializer: retail_crm_service.jms_serializer.adapter # or retail_crm_service.symfony_serializer.adapter -``` - -### Authentication - -Example security configuration: - -```yaml -security: - providers: - client: - entity: - class: 'App\Entity\Connection' # must implements UserInterface - property: 'clientId' - firewalls: - api: - pattern: ^/api - provider: client - anonymous: ~ - lazy: true - stateless: false - guard: - authenticators: - - RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator - callback: - pattern: ^/callback - provider: client - anonymous: ~ - lazy: true - stateless: true - guard: - authenticators: - - RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator - main: - anonymous: true - lazy: true - - access_control: - - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } # login for programmatically authentication user - - { path: ^/api, roles: ROLE_USER } - - { path: ^/callback, roles: ROLE_USER } -``` - -To authenticate the user after creating it, you can use the following code - -```php - -use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; -use RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; - -class AppController extends AbstractController -{ - public function someAction( - Request $request, - GuardAuthenticatorHandler $guardAuthenticatorHandler, - FrontApiClientAuthenticator $frontApiClientAuthenticator, - ConnectionManager $manager - ): Response { - $user = $manager->getUser(); // getting user - - $guardAuthenticatorHandler->authenticateUserAndHandleSuccess( - $user, - $request, - $frontApiClientAuthenticator, - 'api' - ); - // ... - } -} - -``` +## Usage +* [Handling incoming requests data](./Requests.md) +* [Security](./Security.md) +* [Messenger](./Messenger.md) diff --git a/Tests/DataFixtures/TestMessage.php b/Tests/DataFixtures/TestMessage.php new file mode 100644 index 0000000..b69877e --- /dev/null +++ b/Tests/DataFixtures/TestMessage.php @@ -0,0 +1,15 @@ +commandName = 'test'; + $this->arguments = ['argument' => 'argument']; + $this->options = ['option' => 'option']; + } +} diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 5af77cf..1468dde 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -34,6 +34,10 @@ class ConfigurationTest extends TestCase 'type2' ] ] + ], + 'messenger' => [ + 'message_handler' => 'simple_console_runner', + 'process_timeout' => 60 ] ] ]; @@ -71,6 +75,9 @@ class ConfigurationTest extends TestCase 'type', ] ] + ], + 'messenger' => [ + 'message_handler' => 'simple_console_runner' ] ] ]; diff --git a/Tests/DependencyInjection/RetailCrmServiceExtensionTest.php b/Tests/DependencyInjection/RetailCrmServiceExtensionTest.php index ba0dcd0..75141e7 100644 --- a/Tests/DependencyInjection/RetailCrmServiceExtensionTest.php +++ b/Tests/DependencyInjection/RetailCrmServiceExtensionTest.php @@ -34,6 +34,9 @@ class RetailCrmServiceExtensionTest extends TestCase 'request_schema' => [ 'callback' => [], 'client' => [] + ], + 'messenger' => [ + 'message_handler' => 'simple_console_runner' ] ] ], diff --git a/Tests/Fixtures/App/Kernel.php b/Tests/Fixtures/App/Kernel.php new file mode 100644 index 0000000..d10c683 --- /dev/null +++ b/Tests/Fixtures/App/Kernel.php @@ -0,0 +1,33 @@ +register(TestCommand::class, TestCommand::class) + ->addTag('console.command', ['command' => TestCommand::getDefaultName()]) + ; + + $container->setParameter('kernel.project_dir', __DIR__ . '/..'); + } + +// public function registerContainerConfiguration(LoaderInterface $loader) +// { +// } +} diff --git a/Tests/Fixtures/App/TestCommand.php b/Tests/Fixtures/App/TestCommand.php new file mode 100644 index 0000000..a47c371 --- /dev/null +++ b/Tests/Fixtures/App/TestCommand.php @@ -0,0 +1,36 @@ +addArgument( + 'argument', + InputArgument::REQUIRED + ) + ->addOption( + 'option', + null, + InputOption::VALUE_REQUIRED + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + echo self::$defaultName . ' ' . $input->getArgument('argument') . ' ' . $input->getOption('option'); + + return Command::SUCCESS; + } +} diff --git a/Tests/Fixtures/App/TestCommandMessage.php b/Tests/Fixtures/App/TestCommandMessage.php new file mode 100644 index 0000000..e03e282 --- /dev/null +++ b/Tests/Fixtures/App/TestCommandMessage.php @@ -0,0 +1,15 @@ +commandName = 'test'; + $this->arguments = ['argument' => 'test']; + $this->options = ['option' => 'test']; + } +} diff --git a/Tests/Fixtures/bin/console b/Tests/Fixtures/bin/console new file mode 100644 index 0000000..b5a1057 --- /dev/null +++ b/Tests/Fixtures/bin/console @@ -0,0 +1,9 @@ +#!/usr/bin/env php +run($input); diff --git a/Tests/Messenger/CommandMessageTest.php b/Tests/Messenger/CommandMessageTest.php new file mode 100644 index 0000000..fdd13b1 --- /dev/null +++ b/Tests/Messenger/CommandMessageTest.php @@ -0,0 +1,38 @@ +getCommandName()); + static::assertEquals(['argument' => 'argument'], $message->getArguments()); + static::assertEquals(['option' => 'option'], $message->getOptions()); + static::assertEquals(['--option' => 'option'], $message->getFormattedOptions()); + + $message->addOption('option2', 'option2'); + $message->addArgument('argument2', 'argument2'); + + static::assertEquals(['argument' => 'argument', 'argument2' => 'argument2'], $message->getArguments()); + static::assertEquals(['option' => 'option', 'option2' => 'option2'], $message->getOptions()); + static::assertEquals(['--option' => 'option', '--option2' => 'option2'], $message->getFormattedOptions()); + + $message->setOptions(['option' => 'option']); + $message->setArguments(['argument' => 'argument']); + + static::assertEquals(['argument' => 'argument'], $message->getArguments()); + static::assertEquals(['option' => 'option'], $message->getOptions()); + static::assertEquals(['--option' => 'option'], $message->getFormattedOptions()); + } +} diff --git a/Tests/Messenger/MessageHandler/InNewProcessRunnerTest.php b/Tests/Messenger/MessageHandler/InNewProcessRunnerTest.php new file mode 100644 index 0000000..cada1a3 --- /dev/null +++ b/Tests/Messenger/MessageHandler/InNewProcessRunnerTest.php @@ -0,0 +1,32 @@ + 'test']); + } + + public function testRun(): void + { + $runner = new InNewProcessRunner(new NullLogger, self::$kernel); + + ob_clean(); + ob_start(); + $runner->run(new TestCommandMessage()); + + static::assertEquals('test test test', ob_get_clean()); + } +} diff --git a/Tests/Messenger/MessageHandler/SimpleConsoleRunnerTest.php b/Tests/Messenger/MessageHandler/SimpleConsoleRunnerTest.php new file mode 100644 index 0000000..d5256b7 --- /dev/null +++ b/Tests/Messenger/MessageHandler/SimpleConsoleRunnerTest.php @@ -0,0 +1,32 @@ + 'test']); + } + + public function testRun(): void + { + $runner = new SimpleConsoleRunner(new NullLogger, self::$kernel); + + ob_clean(); + ob_start(); + $runner->run(new TestCommandMessage()); + + static::assertEquals('test test test', ob_get_clean()); + } +} diff --git a/Tests/Messenger/MessageHandlerTest.php b/Tests/Messenger/MessageHandlerTest.php new file mode 100644 index 0000000..1f66d97 --- /dev/null +++ b/Tests/Messenger/MessageHandlerTest.php @@ -0,0 +1,26 @@ +createMock(JobRunner::class); + $runner->expects(static::once())->method('run'); + $message = $this->createMock(CommandMessage::class); + + $handler = new MessageHandler($runner); + $handler->__invoke($message); + } +} diff --git a/Tests/Security/CallbackClientAuthenticatorTest.php b/Tests/Security/CallbackClientAuthenticatorTest.php index 12befe7..bc2f532 100644 --- a/Tests/Security/CallbackClientAuthenticatorTest.php +++ b/Tests/Security/CallbackClientAuthenticatorTest.php @@ -5,11 +5,14 @@ namespace RetailCrm\ServiceBundle\Tests\Security; use PHPUnit\Framework\TestCase; use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory; use RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator; +use RetailCrm\ServiceBundle\Tests\DataFixtures\User; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; /** * Class CallbackClientAuthenticatorTest @@ -99,7 +102,7 @@ class CallbackClientAuthenticatorTest extends TestCase ); $auth = new CallbackClientAuthenticator($errorResponseFactory); - $result = $auth->start(new Request(), new AuthenticationException()); + $result = $auth->onAuthenticationFailure(new Request(), new AuthenticationException()); static::assertInstanceOf(JsonResponse::class, $result); static::assertEquals(Response::HTTP_FORBIDDEN, $result->getStatusCode()); @@ -132,4 +135,34 @@ class CallbackClientAuthenticatorTest extends TestCase static::assertFalse($result); } + + public function testGetUser(): void + { + $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); + $user = new User(); + $auth = new CallbackClientAuthenticator($errorResponseFactory); + + $userProvider = $this->createMock(UserProviderInterface::class); + $userProvider + ->expects(static::once()) + ->method('loadUserByUsername') + ->with('clientId') + ->willReturn($user) + ; + + $result = $auth->getUser('clientId', $userProvider); + static::assertEquals($user, $result); + } + + public function testOnAuthenticationSuccess(): void + { + $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); + $request = $this->createMock(Request::class); + $token = $this->createMock(TokenInterface::class); + $auth = new CallbackClientAuthenticator($errorResponseFactory); + + $result = $auth->onAuthenticationSuccess($request, $token, 'key'); + + static::assertNull($result); + } } diff --git a/Tests/Security/FrontApiClientAuthenticatorTest.php b/Tests/Security/FrontApiClientAuthenticatorTest.php index 6ee8a4e..c57bf6d 100644 --- a/Tests/Security/FrontApiClientAuthenticatorTest.php +++ b/Tests/Security/FrontApiClientAuthenticatorTest.php @@ -4,15 +4,15 @@ namespace RetailCrm\ServiceBundle\Tests\Security; use PHPUnit\Framework\TestCase; use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory; -use RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator; use RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator; use RetailCrm\ServiceBundle\Tests\DataFixtures\User; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Security; -use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; /** * Class FrontApiClientAuthenticatorTest @@ -45,11 +45,11 @@ class FrontApiClientAuthenticatorTest extends TestCase $security = $this->createMock(Security::class); $auth = new FrontApiClientAuthenticator($errorResponseFactory, $security); - $result = $auth->getCredentials(new Request([], [CallbackClientAuthenticator::AUTH_FIELD => '123'])); + $result = $auth->getCredentials(new Request([], [FrontApiClientAuthenticator::AUTH_FIELD => '123'])); static::assertEquals('123', $result); - $result = $auth->getCredentials(new Request([CallbackClientAuthenticator::AUTH_FIELD => '123'])); + $result = $auth->getCredentials(new Request([FrontApiClientAuthenticator::AUTH_FIELD => '123'])); static::assertEquals('123', $result); } @@ -120,4 +120,37 @@ class FrontApiClientAuthenticatorTest extends TestCase static::assertTrue($result); } + + public function testGetUser(): void + { + $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); + $security = $this->createMock(Security::class); + + $user = new User(); + $auth = new FrontApiClientAuthenticator($errorResponseFactory, $security); + + $userProvider = $this->createMock(UserProviderInterface::class); + $userProvider + ->expects(static::once()) + ->method('loadUserByUsername') + ->with('clientId') + ->willReturn($user) + ; + + $result = $auth->getUser('clientId', $userProvider); + static::assertEquals($user, $result); + } + + public function testOnAuthenticationSuccess(): void + { + $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); + $security = $this->createMock(Security::class); + $request = $this->createMock(Request::class); + $token = $this->createMock(TokenInterface::class); + $auth = new FrontApiClientAuthenticator($errorResponseFactory, $security); + + $result = $auth->onAuthenticationSuccess($request, $token, 'key'); + + static::assertNull($result); + } } diff --git a/composer.json b/composer.json index cdfb4a4..c90c1ab 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,11 @@ "symfony/serializer": "^5.2", "symfony/http-kernel": "^4.0|^5.0", "symfony/validator": "^4.0|^5.0", - "symfony/security-guard": "^4.0|^5.0" + "symfony/security-guard": "^4.0|^5.0", + "symfony/console": "^5.2", + "symfony/messenger": "^5.2", + "symfony/process": "^5.2", + "symfony/event-dispatcher": "^5.2" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ada38ee..56d86c0 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,6 +10,9 @@ stopOnFailure="false" bootstrap="vendor/autoload.php" > + + + ./