1
0
Fork 0
mirror of synced 2025-04-04 21:53:37 +03:00

Compare commits

...

9 commits

Author SHA1 Message Date
Alex Lushpai
a06ec22341
Merge pull request #9 from curse89/modify-middleware
add-ttl-for-middleware
2023-10-18 14:28:42 +03:00
Сергей Кривич
f15d9a2a86 add-ttl-for-middleware 2023-10-18 14:15:32 +03:00
Alex Lushpai
a104eebf2a
Bump symfony validator to 5.3 2021-10-18 11:08:08 +03:00
Sergey Chazov
3f87618424 bump symfony validator to 5.3 2021-08-17 17:53:44 +03:00
Alex Lushpai
5520c4ca68
Fix serialization for transport php serializer 2021-04-22 14:57:08 +03:00
Akolzin Dmitry
ac0a1ae95d fix serialization for transport php serializer 2021-04-22 14:55:30 +03:00
Akolzin Dmitry
d9dca7b78b
lockable middleware (#5) 2021-04-16 13:37:47 +03:00
Akolzin Dmitry
1e99455b65
Message handlers for symfony messenger 2021-03-31 11:00:48 +03:00
Akolzin Dmitry
7516ec60cb
JMS serializer supports (#3)
* add callback argument value resolver
* add jms serializer support
* add phpdoc
* fix FrontApiClientAuthenticator
2021-02-17 09:31:36 +03:00
39 changed files with 1441 additions and 178 deletions

View file

@ -2,10 +2,10 @@
namespace RetailCrm\ServiceBundle\ArgumentResolver;
use RetailCrm\ServiceBundle\Serializer\Adapter;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\Serializer\SerializerInterface;
use Generator;
use Symfony\Component\Validator\Validator\ValidatorInterface;
@ -22,12 +22,12 @@ class CallbackValueResolver extends AbstractValueResolver implements ArgumentVal
/**
* CallbackValueResolver constructor.
*
* @param SerializerInterface $serializer
* @param Adapter $serializer
* @param ValidatorInterface $validator
* @param array $requestSchema
*/
public function __construct(
SerializerInterface $serializer,
Adapter $serializer,
ValidatorInterface $validator,
array $requestSchema
) {
@ -55,7 +55,7 @@ class CallbackValueResolver extends AbstractValueResolver implements ArgumentVal
public function resolve(Request $request, ArgumentMetadata $argument): Generator
{
$parameter = $this->search($request, $argument);
$data = $this->serializer->deserialize($request->request->get($parameter), $argument->getType(), 'json');
$data = $this->serializer->deserialize($request->request->get($parameter), $argument->getType());
$this->validate($data);
yield $data;

View file

@ -2,12 +2,10 @@
namespace RetailCrm\ServiceBundle\ArgumentResolver;
use RetailCrm\ServiceBundle\Serializer\Adapter;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Generator;
@ -19,27 +17,24 @@ use Generator;
class ClientValueResolver extends AbstractValueResolver implements ArgumentValueResolverInterface
{
private $serializer;
private $denormalizer;
private $requestSchema;
/**
* ClientValueResolver constructor.
*
*
* @param Adapter $serializer
* @param ValidatorInterface $validator
* @param SerializerInterface $serializer
* @param DenormalizerInterface $denormalizer
* @param array $requestSchema
*/
public function __construct(
Adapter $serializer,
ValidatorInterface $validator,
SerializerInterface $serializer,
DenormalizerInterface $denormalizer,
array $requestSchema
) {
parent::__construct($validator);
$this->serializer = $serializer;
$this->denormalizer = $denormalizer;
$this->requestSchema = $requestSchema;
}
@ -72,12 +67,10 @@ class ClientValueResolver extends AbstractValueResolver implements ArgumentValue
* @param string $type
*
* @return object
*
* @throws ExceptionInterface
*/
private function handleGetData(array $data, string $type): object
{
return $this->denormalizer->denormalize($data, $type);
return $this->serializer->arrayToObject($data, $type);
}
/**
@ -88,6 +81,6 @@ class ClientValueResolver extends AbstractValueResolver implements ArgumentValue
*/
private function handlePostData(string $data, string $type): object
{
return $this->serializer->deserialize($data, $type, 'json');
return $this->serializer->deserialize($data, $type);
}
}

View file

@ -25,18 +25,38 @@ class Configuration implements ConfigurationInterface
->arrayNode('request_schema')
->children()
->arrayNode('callback')
->arrayPrototype()
->children()
->scalarNode('type')->isRequired()->end()
->arrayNode('params')
->isRequired()->scalarPrototype()->end()
->children()
->arrayNode('supports')
->arrayPrototype()
->children()
->scalarNode('type')->isRequired()->end()
->arrayNode('params')
->isRequired()->scalarPrototype()->end()
->end()
->end()
->end()
->end()
->scalarNode('serializer')
->defaultValue('retail_crm_service.symfony_serializer.adapter')
->end()
->end()
->arrayNode('client')
->scalarPrototype()->end()
->end()
->arrayNode('client')
->children()
->arrayNode('supports')
->scalarPrototype()->end()
->end()
->scalarNode('serializer')
->defaultValue('retail_crm_service.symfony_serializer.adapter')
->end()
->end()
->end()
->end()
->end()
->arrayNode('messenger')
->children()
->scalarNode('message_handler')->isRequired()->defaultValue('simple_console_runner')->end()
->scalarNode('process_timeout')->end()
->end()
->end()
->end();

View file

@ -4,11 +4,15 @@ 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;
use RetailCrm\ServiceBundle\Serializer\JMSSerializerAdapter;
use RetailCrm\ServiceBundle\Serializer\SymfonySerializerAdapter;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Reference;
/**
* Class RetailCrmServiceExtension
@ -27,24 +31,64 @@ class RetailCrmServiceExtension extends Extension
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter(
'retail_crm_service.request_schema.callback',
$config['request_schema']['callback']
'retail_crm_service.request_schema.callback.supports',
$config['request_schema']['callback']['supports']
);
$container->setParameter(
'retail_crm_service.request_schema.client',
$config['request_schema']['client']
'retail_crm_service.request_schema.client.supports',
$config['request_schema']['client']['supports']
);
$container->setParameter(
'retail_crm_service.request_schema.callback.serializer',
$config['request_schema']['callback']['serializer']
);
$container->setParameter(
'retail_crm_service.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
->register(SymfonySerializerAdapter::class)
->setAutowired(true);
$container->setAlias('retail_crm_service.symfony_serializer.adapter', SymfonySerializerAdapter::class);
$container
->register(JMSSerializerAdapter::class)
->setAutowired(true);
$container->setAlias('retail_crm_service.jms_serializer.adapter', JMSSerializerAdapter::class);
$container
->register(CallbackValueResolver::class)
->setArgument('$requestSchema', '%retail_crm_service.request_schema.callback%')
->setArguments([
new Reference($container->getParameter('retail_crm_service.request_schema.callback.serializer')),
new Reference('validator'),
$container->getParameter('retail_crm_service.request_schema.callback.supports')
])
->addTag('controller.argument_value_resolver', ['priority' => 50])
->setAutowired(true);
$container
->register(ClientValueResolver::class)
->setArgument('$requestSchema', '%retail_crm_service.request_schema.client%')
->setArguments([
new Reference($container->getParameter('retail_crm_service.request_schema.client.serializer')),
new Reference('validator'),
$container->getParameter('retail_crm_service.request_schema.client.supports')
])
->addTag('controller.argument_value_resolver', ['priority' => 50])
->setAutowired(true);
@ -59,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);
}
}

View file

@ -0,0 +1,113 @@
<?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;
}
/**
* For lockable message
*
* @return array
*/
public function __serialize(): array
{
return [
'commandName' => $this->getCommandName(),
'arguments' => $this->getArguments(),
'options' => $this->getOptions()
];
}
}

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,13 @@
<?php
namespace RetailCrm\ServiceBundle\Messenger\Middleware;
/**
* Interface LockableMessage
*
* @package RetailCrm\ServiceBundle\Messenger\Middleware
*/
interface LockableMessage
{
public function __serialize(): array;
}

View file

@ -0,0 +1,74 @@
<?php
namespace RetailCrm\ServiceBundle\Messenger\Middleware;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
use Throwable;
/**
* Class LockableMessageMiddleware
*
* @package RetailCrm\ServiceBundle\Messenger\Middleware
*/
class LockableMessageMiddleware implements MiddlewareInterface
{
/**
* @var LockFactory
*/
private $lockFactory;
/**
* @var int|null
*/
private $ttl;
public function __construct(LockFactory $lockFactory, int $ttl = null)
{
$this->lockFactory = $lockFactory;
$this->ttl = $ttl;
}
/**
* @param Envelope $envelope
* @param StackInterface $stack
*
* @return Envelope
*
* @throws Throwable
*/
public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
$message = $envelope->getMessage();
if ($envelope->all(ReceivedStamp::class) && $message instanceof LockableMessage) {
$lock = $this->lockFactory->createLock($this->objectHash($message), $this->ttl);
if (!$lock->acquire()) {
return $envelope;
}
try {
return $stack->next()->handle($envelope, $stack);
} catch (Throwable $exception) {
throw $exception;
} finally {
$lock->release();
}
}
return $stack->next()->handle($envelope, $stack);
}
/**
* @param LockableMessage $message
*
* @return string
*/
private function objectHash(LockableMessage $message): string
{
return hash('crc32', serialize($message));
}
}

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`
## Usage
Enable bundle in `config/bundles.php`:
```php
@ -18,131 +16,15 @@ return [
Create bundle config file in `config/packages/retail_crm_service.yaml`:
```yaml
retail_crm_service:
request_schema: ~
```
### 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:
- 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:
- App\Dto\Body
callback: ~
client: ~
messenger: ~
```
### 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)

29
Serializer/Adapter.php Normal file
View file

@ -0,0 +1,29 @@
<?php
namespace RetailCrm\ServiceBundle\Serializer;
/**
* Interface Adapter
*
* @package RetailCrm\ServiceBundle\Serializer
*/
interface Adapter
{
/**
* @param string $data
* @param string $type
* @param string $format
*
* @return object
*/
public function deserialize(string $data, string $type, string $format = 'json'): object;
/**
* @param array $data
* @param string $type
* @param string|null $format
*
* @return object
*/
public function arrayToObject(array $data, string $type, ?string $format = null): object;
}

View file

@ -0,0 +1,57 @@
<?php
namespace RetailCrm\ServiceBundle\Serializer;
use JMS\Serializer\ArrayTransformerInterface;
use JMS\Serializer\Context;
use JMS\Serializer\SerializerInterface;
/**
* Class JMSSerializerAdapter
*
* @package RetailCrm\ServiceBundle\Serializer
*/
class JMSSerializerAdapter implements Adapter
{
private $serializer;
private $transformer;
private $context;
/**
* JMSSerializerAdapter constructor.
*
* @param SerializerInterface $serializer
* @param ArrayTransformerInterface $transformer
*/
public function __construct(
SerializerInterface $serializer,
ArrayTransformerInterface $transformer
) {
$this->serializer = $serializer;
$this->transformer = $transformer;
}
/**
* {@inheritdoc }
*/
public function deserialize(string $data, string $type, string $format = 'json'): object
{
return $this->serializer->deserialize($data, $type, $format, $this->context);
}
/**
* {@inheritdoc }
*/
public function arrayToObject(array $data, string $type, ?string $format = null): object
{
return $this->transformer->fromArray($data, $type, $this->context);
}
/**
* @param Context $context
*/
public function setContext(Context $context): void
{
$this->context = $context;
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace RetailCrm\ServiceBundle\Serializer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Class SymfonySerializerAdapter
*
* @package RetailCrm\ServiceBundle\Serializer
*/
class SymfonySerializerAdapter implements Adapter
{
private $serializer;
private $denormalizer;
private $context = [];
/**
* SymfonySerializerAdapter constructor.
*
* @param SerializerInterface $serializer
* @param DenormalizerInterface $denormalizer
*/
public function __construct(SerializerInterface $serializer, DenormalizerInterface $denormalizer)
{
$this->serializer = $serializer;
$this->denormalizer = $denormalizer;
}
/**
* {@inheritdoc }
*/
public function deserialize(string $data, string $type,string $format = 'json'): object
{
return $this->serializer->deserialize($data, $type, $format, $this->context);
}
/**
* {@inheritdoc }
*/
public function arrayToObject(array $data, string $type, string $format = null): object
{
return $this->denormalizer->denormalize($data, $type, $format, $this->context);
}
/**
* @param array $context
*/
public function setContext(array $context): void
{
$this->context = $context;
}
}

View file

@ -5,6 +5,7 @@ namespace RetailCrm\ServiceBundle\Tests\ArgumentResolver;
use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\ArgumentResolver\CallbackValueResolver;
use RetailCrm\ServiceBundle\Exceptions\InvalidRequestArgumentException;
use RetailCrm\ServiceBundle\Serializer\SymfonySerializerAdapter;
use RetailCrm\ServiceBundle\Tests\DataFixtures\RequestDto;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
@ -27,7 +28,7 @@ class CallbackValueResolverTest extends TestCase
{
$serializer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
$this->resolver = new CallbackValueResolver(
$serializer,
new SymfonySerializerAdapter($serializer, $serializer),
Validation::createValidatorBuilder()
->enableAnnotationMapping()
->getValidator(),

View file

@ -5,6 +5,7 @@ namespace RetailCrm\ServiceBundle\Tests\ArgumentResolver;
use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\ArgumentResolver\ClientValueResolver;
use RetailCrm\ServiceBundle\Exceptions\InvalidRequestArgumentException;
use RetailCrm\ServiceBundle\Serializer\SymfonySerializerAdapter;
use RetailCrm\ServiceBundle\Tests\DataFixtures\RequestDto;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
@ -27,11 +28,10 @@ class ClientValueResolverTest extends TestCase
{
$serializer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
$this->resolver = new ClientValueResolver(
new SymfonySerializerAdapter($serializer, $serializer),
Validation::createValidatorBuilder()
->enableAnnotationMapping()
->getValidator(),
$serializer,
$serializer,
[
RequestDto::class
]

View file

@ -3,6 +3,7 @@
namespace RetailCrm\ServiceBundle\Tests\DataFixtures;
use Symfony\Component\Validator\Constraints as Assert;
use JMS\Serializer\Annotation as JMS;
/**
* Class RequestDto
@ -14,6 +15,7 @@ class RequestDto
/**
* @var string
* @Assert\NotNull()
* @JMS\Type("string")
*/
public $param;
}

View file

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

View file

@ -21,15 +21,23 @@ class ConfigurationTest extends TestCase
[
'request_schema' => [
'callback' => [
[
'type' => 'type',
'params' => ['param']
'supports' => [
[
'type' => 'type',
'params' => ['param']
]
]
],
'client' => [
'type1',
'type2'
'supports' => [
'type1',
'type2'
]
]
],
'messenger' => [
'message_handler' => 'simple_console_runner',
'process_timeout' => 60
]
]
];
@ -44,14 +52,14 @@ class ConfigurationTest extends TestCase
'type' => 'type',
'params' => ['param']
],
$config['request_schema']['callback'][0]
$config['request_schema']['callback']['supports'][0]
);
static::assertEquals(
[
'type1',
'type2'
],
$config['request_schema']['client']
$config['request_schema']['client']['supports']
);
}
@ -63,8 +71,13 @@ class ConfigurationTest extends TestCase
[
'request_schema' => [
'client' => [
'type',
'supports' => [
'type',
]
]
],
'messenger' => [
'message_handler' => 'simple_console_runner'
]
]
];
@ -72,6 +85,6 @@ class ConfigurationTest extends TestCase
$config = $processor->processConfiguration(new Configuration(), $configs);
static::assertArrayHasKey('client', $config['request_schema']);
static::assertEquals(['type'], $config['request_schema']['client']);
static::assertEquals(['type'], $config['request_schema']['client']['supports']);
}
}

View file

@ -31,7 +31,13 @@ class RetailCrmServiceExtensionTest extends TestCase
$extension->load(
[
[
'request_schema' => []
'request_schema' => [
'callback' => [],
'client' => []
],
'messenger' => [
'message_handler' => 'simple_console_runner'
]
]
],
$container
@ -44,8 +50,10 @@ class RetailCrmServiceExtensionTest extends TestCase
public function testLoad(): void
{
static::assertTrue($this->container->hasParameter('retail_crm_service.request_schema.callback'));
static::assertTrue($this->container->hasParameter('retail_crm_service.request_schema.client'));
static::assertTrue($this->container->hasParameter('retail_crm_service.request_schema.callback.supports'));
static::assertTrue($this->container->hasParameter('retail_crm_service.request_schema.callback.serializer'));
static::assertTrue($this->container->hasParameter('retail_crm_service.request_schema.client.supports'));
static::assertTrue($this->container->hasParameter('retail_crm_service.request_schema.client.serializer'));
static::assertTrue($this->container->hasDefinition(CallbackValueResolver::class));
static::assertTrue($this->container->hasDefinition(ClientValueResolver::class));
static::assertTrue($this->container->hasDefinition(ErrorJsonResponseFactory::class));

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

@ -0,0 +1,113 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\Messenger\Middleware;
use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\Messenger\Middleware\LockableMessageMiddleware;
use RetailCrm\ServiceBundle\Tests\DataFixtures\TestMessage;
use Symfony\Component\Lock\Exception\LockConflictedException;
use Symfony\Component\Lock\Key;
use Symfony\Component\Lock\Lock;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\PersistingStoreInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
/**
* Class LockableMessageMiddlewareTest
*
* @package RetailCrm\ServiceBundle\Tests\Messenger\Middleware
*/
class LockableMessageMiddlewareTest extends TestCase
{
/**
* @var LockFactory
*/
private $lockFactory;
protected function setUp(): void
{
$this->lockFactory = $this->createMock(LockFactory::class);
}
public function testHandle(): void
{
$store = $this->createMock(PersistingStoreInterface::class);
$key = new Key(uniqid());
$lock = new Lock($key, $store);
$this->lockFactory->expects(static::once())->method('createLock')->willReturn($lock);
$envelope = new Envelope(new TestMessage(), [new ReceivedStamp('test')]);
$next = $this->createMock(MiddlewareInterface::class);
$next->method('handle')->willReturn($envelope);
$stack = $this->createMock(StackInterface::class);
$stack->method('next')->willReturn($next);
$middleware = new LockableMessageMiddleware($this->lockFactory);
$result = $middleware->handle($envelope, $stack);
static::assertInstanceOf(Envelope::class, $result);
}
public function testLockHandle(): void
{
$store = $this->createMock(PersistingStoreInterface::class);
$store->method('save')->willThrowException(new LockConflictedException);
$key = new Key(uniqid());
$lock = new Lock($key, $store);
$this->lockFactory->expects(static::once())->method('createLock')->willReturn($lock);
$envelope = new Envelope(new TestMessage(), [new ReceivedStamp('test')]);
$next = $this->createMock(MiddlewareInterface::class);
$next->method('handle')->willReturn($envelope);
$stack = $this->createMock(StackInterface::class);
$stack->method('next')->willReturn($next);
$middleware = new LockableMessageMiddleware($this->lockFactory);
$result = $middleware->handle($envelope, $stack);
static::assertInstanceOf(Envelope::class, $result);
}
public function testNonLockableHandle(): void
{
$store = $this->createMock(PersistingStoreInterface::class);
$store->method('save')->willThrowException(new LockConflictedException);
$key = new Key(uniqid());
$lock = new Lock($key, $store);
$this->lockFactory->expects(static::never())->method('createLock')->willReturn($lock);
$envelope = new Envelope(new \stdClass(), [new ReceivedStamp('test')]);
$next = $this->createMock(MiddlewareInterface::class);
$next->method('handle')->willReturn($envelope);
$stack = $this->createMock(StackInterface::class);
$stack->method('next')->willReturn($next);
$middleware = new LockableMessageMiddleware($this->lockFactory);
$result = $middleware->handle($envelope, $stack);
static::assertInstanceOf(Envelope::class, $result);
}
public function testNonReceivedHandle(): void
{
$store = $this->createMock(PersistingStoreInterface::class);
$store->method('save')->willThrowException(new LockConflictedException);
$key = new Key(uniqid());
$lock = new Lock($key, $store);
$this->lockFactory->expects(static::never())->method('createLock')->willReturn($lock);
$envelope = new Envelope(new TestMessage());
$next = $this->createMock(MiddlewareInterface::class);
$next->method('handle')->willReturn($envelope);
$stack = $this->createMock(StackInterface::class);
$stack->method('next')->willReturn($next);
$middleware = new LockableMessageMiddleware($this->lockFactory);
$result = $middleware->handle($envelope, $stack);
static::assertInstanceOf(Envelope::class, $result);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\Serializer;
use JMS\Serializer\SerializerBuilder;
use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\Serializer\JMSSerializerAdapter;
use RetailCrm\ServiceBundle\Tests\DataFixtures\RequestDto;
class JSMSerializerAdapterTest extends TestCase
{
private $serializer;
private $transformer;
protected function setUp(): void
{
$this->serializer = SerializerBuilder::create()->build();
$this->transformer = SerializerBuilder::create()->build();
}
public function testDeserialize(): void
{
$adapter = new JMSSerializerAdapter($this->serializer, $this->transformer);
$object = $adapter->deserialize('{"param": "string"}', RequestDto::class,'json');
static::assertInstanceOf(RequestDto::class, $object);
static::assertEquals('string', $object->param);
}
public function testArrayToObject(): void
{
$adapter = new JMSSerializerAdapter($this->serializer, $this->transformer);
$object = $adapter->arrayToObject(['param' => 'string'], RequestDto::class);
static::assertInstanceOf(RequestDto::class, $object);
static::assertEquals('string', $object->param);
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\Serializer;
use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\Serializer\SymfonySerializerAdapter;
use RetailCrm\ServiceBundle\Tests\DataFixtures\RequestDto;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
class SymfonySerializerAdapterTest extends TestCase
{
private $serializer;
private $denormalizer;
protected function setUp(): void
{
$this->serializer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
$this->denormalizer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
}
public function testDeserialize(): void
{
$adapter = new SymfonySerializerAdapter($this->serializer, $this->denormalizer);
$object = $adapter->deserialize('{"param": "string"}', RequestDto::class,'json');
static::assertInstanceOf(RequestDto::class, $object);
static::assertEquals('string', $object->param);
}
public function testArrayToObject(): void
{
$adapter = new SymfonySerializerAdapter($this->serializer, $this->denormalizer);
$object = $adapter->arrayToObject(['param' => 'string'], RequestDto::class);
static::assertInstanceOf(RequestDto::class, $object);
static::assertEquals('string', $object->param);
}
}

View file

@ -15,8 +15,13 @@
"symfony/framework-bundle": "^4.0|^5.0",
"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/validator": "^4.0|^5.3",
"symfony/security-guard": "^4.0|^5.0",
"symfony/console": "^5.2",
"symfony/messenger": "^5.2",
"symfony/process": "^5.2",
"symfony/event-dispatcher": "^5.2",
"symfony/lock": "^5.2"
},
"autoload": {
"psr-4": {
@ -35,7 +40,8 @@
"ext-json": "*",
"phpunit/phpunit": "^8.0 || ^9.0",
"doctrine/annotations": "^1.11",
"doctrine/cache": "^1.10"
"doctrine/cache": "^1.10",
"jms/serializer-bundle": "^3.8"
},
"scripts": {
"tests": "./vendor/bin/phpunit -c phpunit.xml.dist"

View file

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