1
0
mirror of synced 2024-11-21 20:36:08 +03:00

Message handlers for symfony messenger

This commit is contained in:
Akolzin Dmitry 2021-03-31 11:00:48 +03:00 committed by GitHub
parent 7516ec60cb
commit 1e99455b65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 905 additions and 150 deletions

View File

@ -53,6 +53,12 @@ class Configuration implements ConfigurationInterface
->end() ->end()
->end() ->end()
->end() ->end()
->arrayNode('messenger')
->children()
->scalarNode('message_handler')->isRequired()->defaultValue('simple_console_runner')->end()
->scalarNode('process_timeout')->end()
->end()
->end()
->end(); ->end();
return $treeBuilder; return $treeBuilder;

View File

@ -4,6 +4,7 @@ namespace RetailCrm\ServiceBundle\DependencyInjection;
use RetailCrm\ServiceBundle\ArgumentResolver\CallbackValueResolver; use RetailCrm\ServiceBundle\ArgumentResolver\CallbackValueResolver;
use RetailCrm\ServiceBundle\ArgumentResolver\ClientValueResolver; use RetailCrm\ServiceBundle\ArgumentResolver\ClientValueResolver;
use RetailCrm\ServiceBundle\Messenger\MessageHandler;
use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory; use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory;
use RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator; use RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator;
use RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator; use RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator;
@ -49,6 +50,18 @@ class RetailCrmServiceExtension extends Extension
$config['request_schema']['client']['serializer'] $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 $container
->register(SymfonySerializerAdapter::class) ->register(SymfonySerializerAdapter::class)
->setAutowired(true); ->setAutowired(true);
@ -90,5 +103,28 @@ class RetailCrmServiceExtension extends Extension
$container $container
->register(FrontApiClientAuthenticator::class) ->register(FrontApiClientAuthenticator::class)
->setAutowired(true); ->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);
} }
} }

View File

@ -0,0 +1,99 @@
<?php
namespace RetailCrm\ServiceBundle\Messenger;
/**
* Class Message
*
* @package RetailCrm\ServiceBundle\Messenger
*/
abstract class CommandMessage
{
/** @var string */
protected $commandName;
/** @var array */
protected $options = [];
/** @var array */
protected $arguments = [];
/**
* @return string
*/
public function getCommandName(): string
{
return $this->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;
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace RetailCrm\ServiceBundle\Messenger;
use RetailCrm\ServiceBundle\Messenger\MessageHandler\JobRunner;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Exception;
/**
* Class MessageHandler
*
* @package RetailCrm\ServiceBundle\Messenger
*/
class MessageHandler implements MessageHandlerInterface
{
/**
* @var JobRunner
*/
private $runner;
/**
* CommandQueueHandler constructor.
*
* @param JobRunner $runner
*/
public function __construct(JobRunner $runner)
{
$this->runner = $runner;
}
/**
* @param CommandMessage $message
*
* @throws Exception
*/
public function __invoke(CommandMessage $message): void
{
$this->runner->run($message);
}
}

View File

@ -0,0 +1,109 @@
<?php
namespace RetailCrm\ServiceBundle\Messenger\MessageHandler;
use Psr\Log\LoggerInterface;
use RetailCrm\ServiceBundle\Messenger\CommandMessage;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;
/**
* Class InNewProcessRunner
*
* @package RetailCrm\ServiceBundle\Messenger\MessageHandler
*/
class InNewProcessRunner implements JobRunner
{
/** @var int Default timeout for process */
public const DEFAULT_TIMEOUT = 3600;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var KernelInterface
*/
private $kernel;
/**
* @var int
*/
private $timeout = self::DEFAULT_TIMEOUT;
/**
* CommandQueueHandler constructor.
*
* @param LoggerInterface $logger
* @param KernelInterface $kernel
* @param int|null $timeout
*/
public function __construct(
LoggerInterface $logger,
KernelInterface $kernel,
?int $timeout = null
) {
$this->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;
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace RetailCrm\ServiceBundle\Messenger\MessageHandler;
use RetailCrm\ServiceBundle\Messenger\CommandMessage;
/**
* Interface JobRunner
*
* @package RetailCrm\ServiceBundle\Messenger\MessageHandler
*/
interface JobRunner
{
/**
* @param CommandMessage $message
*/
public function run(CommandMessage $message): void;
}

View File

@ -0,0 +1,66 @@
<?php
namespace RetailCrm\ServiceBundle\Messenger\MessageHandler;
use Psr\Log\LoggerInterface;
use RetailCrm\ServiceBundle\Messenger\CommandMessage;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* Class SimpleConsoleRunner
*
* @package RetailCrm\ServiceBundle\Messenger\MessageHandler
*/
class SimpleConsoleRunner implements JobRunner
{
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var KernelInterface
*/
private $kernel;
/**
* CommandQueueHandler constructor.
*
* @param LoggerInterface $logger
* @param KernelInterface $kernel
*/
public function __construct(LoggerInterface $logger, KernelInterface $kernel)
{
$this->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();
}
}

View File

@ -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).

71
Resources/doc/Requests.md Normal file
View File

@ -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
```

70
Resources/doc/Security.md Normal file
View File

@ -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'
);
// ...
}
}
```

View File

@ -2,8 +2,6 @@
`composer require retailcrm/service-bundle` `composer require retailcrm/service-bundle`
## Usage
Enable bundle in `config/bundles.php`: Enable bundle in `config/bundles.php`:
```php ```php
@ -23,147 +21,10 @@ retail_crm_service:
request_schema: request_schema:
callback: ~ callback: ~
client: ~ client: ~
messenger: ~
``` ```
### Deserializing incoming requests ## Usage
* [Handling incoming requests data](./Requests.md)
#### Callbacks (form data) * [Security](./Security.md)
* [Messenger](./Messenger.md)
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'
);
// ...
}
}
```

View File

@ -0,0 +1,15 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\DataFixtures;
use RetailCrm\ServiceBundle\Messenger\CommandMessage;
class TestMessage extends CommandMessage
{
public function __construct()
{
$this->commandName = 'test';
$this->arguments = ['argument' => 'argument'];
$this->options = ['option' => 'option'];
}
}

View File

@ -34,6 +34,10 @@ class ConfigurationTest extends TestCase
'type2' 'type2'
] ]
] ]
],
'messenger' => [
'message_handler' => 'simple_console_runner',
'process_timeout' => 60
] ]
] ]
]; ];
@ -71,6 +75,9 @@ class ConfigurationTest extends TestCase
'type', 'type',
] ]
] ]
],
'messenger' => [
'message_handler' => 'simple_console_runner'
] ]
] ]
]; ];

View File

@ -34,6 +34,9 @@ class RetailCrmServiceExtensionTest extends TestCase
'request_schema' => [ 'request_schema' => [
'callback' => [], 'callback' => [],
'client' => [] 'client' => []
],
'messenger' => [
'message_handler' => 'simple_console_runner'
] ]
] ]
], ],

View File

@ -0,0 +1,33 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\Fixtures\App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class Kernel extends \Symfony\Component\HttpKernel\Kernel
{
use MicroKernelTrait;
public function registerBundles()
{
return [
new \Symfony\Bundle\FrameworkBundle\FrameworkBundle()
];
}
protected function configureContainer(ContainerBuilder $container/*, LoaderInterface $loader*/): void
{
$container
->register(TestCommand::class, TestCommand::class)
->addTag('console.command', ['command' => TestCommand::getDefaultName()])
;
$container->setParameter('kernel.project_dir', __DIR__ . '/..');
}
// public function registerContainerConfiguration(LoaderInterface $loader)
// {
// }
}

View File

@ -0,0 +1,36 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\Fixtures\App;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class TestCommand extends Command
{
protected static $defaultName = 'test';
protected function configure(): void
{
$this
->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;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\Fixtures\App;
use RetailCrm\ServiceBundle\Messenger\CommandMessage;
class TestCommandMessage extends CommandMessage
{
public function __construct()
{
$this->commandName = 'test';
$this->arguments = ['argument' => 'test'];
$this->options = ['option' => 'test'];
}
}

View File

@ -0,0 +1,9 @@
#!/usr/bin/env php
<?php
require_once __DIR__ . '/../../../vendor/autoload.php';
$input = new Symfony\Component\Console\Input\ArgvInput();
$kernel = new RetailCrm\ServiceBundle\Tests\Fixtures\App\Kernel('test', true);
$application = new Symfony\Bundle\FrameworkBundle\Console\Application($kernel);
$application->run($input);

View File

@ -0,0 +1,38 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\Messenger;
use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\Tests\DataFixtures\TestMessage;
/**
* Class CommandMessageTest
*
* @package RetailCrm\ServiceBundle\Tests\Messenger
*/
class CommandMessageTest extends TestCase
{
public function testMessage(): void
{
$message = new TestMessage();
static::assertEquals('test', $message->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());
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\Messenger\MessageHandler;
use Psr\Log\NullLogger;
use RetailCrm\ServiceBundle\Messenger\MessageHandler\InNewProcessRunner;
use RetailCrm\ServiceBundle\Tests\Fixtures\App\TestCommandMessage;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
/**
* Class InNewProcessRunnerTest
*
* @package RetailCrm\ServiceBundle\Tests\Messenger\MessageHandler
*/
class InNewProcessRunnerTest extends KernelTestCase
{
protected function setUp(): void
{
self::bootKernel(['environment' => '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());
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\Messenger\MessageHandler;
use Psr\Log\NullLogger;
use RetailCrm\ServiceBundle\Messenger\MessageHandler\SimpleConsoleRunner;
use RetailCrm\ServiceBundle\Tests\Fixtures\App\TestCommandMessage;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
/**
* Class SimpleConsoleRunnerTest
*
* @package RetailCrm\ServiceBundle\Tests\Messenger\MessageHandler
*/
class SimpleConsoleRunnerTest extends KernelTestCase
{
protected function setUp(): void
{
self::bootKernel(['environment' => '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());
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\Messenger;
use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\Messenger\CommandMessage;
use RetailCrm\ServiceBundle\Messenger\MessageHandler;
use RetailCrm\ServiceBundle\Messenger\MessageHandler\JobRunner;
/**
* Class MessageHandlerTest
*
* @package RetailCrm\ServiceBundle\Tests\Messenger
*/
class MessageHandlerTest extends TestCase
{
public function testRun(): void
{
$runner = $this->createMock(JobRunner::class);
$runner->expects(static::once())->method('run');
$message = $this->createMock(CommandMessage::class);
$handler = new MessageHandler($runner);
$handler->__invoke($message);
}
}

View File

@ -5,11 +5,14 @@ namespace RetailCrm\ServiceBundle\Tests\Security;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory; use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory;
use RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator; use RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator;
use RetailCrm\ServiceBundle\Tests\DataFixtures\User;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; 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\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
/** /**
* Class CallbackClientAuthenticatorTest * Class CallbackClientAuthenticatorTest
@ -99,7 +102,7 @@ class CallbackClientAuthenticatorTest extends TestCase
); );
$auth = new CallbackClientAuthenticator($errorResponseFactory); $auth = new CallbackClientAuthenticator($errorResponseFactory);
$result = $auth->start(new Request(), new AuthenticationException()); $result = $auth->onAuthenticationFailure(new Request(), new AuthenticationException());
static::assertInstanceOf(JsonResponse::class, $result); static::assertInstanceOf(JsonResponse::class, $result);
static::assertEquals(Response::HTTP_FORBIDDEN, $result->getStatusCode()); static::assertEquals(Response::HTTP_FORBIDDEN, $result->getStatusCode());
@ -132,4 +135,34 @@ class CallbackClientAuthenticatorTest extends TestCase
static::assertFalse($result); 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);
}
} }

View File

@ -4,15 +4,15 @@ namespace RetailCrm\ServiceBundle\Tests\Security;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory; use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory;
use RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator;
use RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator; use RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator;
use RetailCrm\ServiceBundle\Tests\DataFixtures\User; use RetailCrm\ServiceBundle\Tests\DataFixtures\User;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; 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\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface;
/** /**
* Class FrontApiClientAuthenticatorTest * Class FrontApiClientAuthenticatorTest
@ -45,11 +45,11 @@ class FrontApiClientAuthenticatorTest extends TestCase
$security = $this->createMock(Security::class); $security = $this->createMock(Security::class);
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security); $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); 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); static::assertEquals('123', $result);
} }
@ -120,4 +120,37 @@ class FrontApiClientAuthenticatorTest extends TestCase
static::assertTrue($result); 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);
}
} }

View File

@ -16,7 +16,11 @@
"symfony/serializer": "^5.2", "symfony/serializer": "^5.2",
"symfony/http-kernel": "^4.0|^5.0", "symfony/http-kernel": "^4.0|^5.0",
"symfony/validator": "^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": { "autoload": {
"psr-4": { "psr-4": {

View File

@ -10,6 +10,9 @@
stopOnFailure="false" stopOnFailure="false"
bootstrap="vendor/autoload.php" bootstrap="vendor/autoload.php"
> >
<php>
<server name="KERNEL_CLASS" value="RetailCrm\ServiceBundle\Tests\Fixtures\App\Kernel" />
</php>
<coverage> <coverage>
<include> <include>
<directory>./</directory> <directory>./</directory>