Skip to content

Commit

Permalink
Add support for caching authentication tokens (#18)
Browse files Browse the repository at this point in the history
* Add support for caching authentication tokens

* Fix typo in README.md

* Allow to use psr/cache 1.0

* Use `--php-version` flah in psalm CI

* Allow to disable the auth cache

* Code enhancement
  • Loading branch information
pulzarraider authored Oct 21, 2022
1 parent c096aa1 commit 29211cc
Show file tree
Hide file tree
Showing 14 changed files with 238 additions and 51 deletions.
18 changes: 13 additions & 5 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@ on:

jobs:
build:
strategy:
matrix:
php-version:
- '7.4'
- '8.0'
- '8.1'
platform: [ubuntu-latest]
name: PHP ${{ matrix.php-version }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: nanasess/setup-php@master
with:
php-version: '7.4'
php-version: ${{ matrix.php-version }}

- name: Validate composer.json
run: composer validate
Expand All @@ -22,19 +30,19 @@ jobs:
uses: actions/cache@v2
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: |
${{ runner.os }}-php-
${{ runner.os }}-composer-
- name: Install dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
run: composer install --prefer-dist --no-progress --no-suggest
run: composer install --prefer-dist --no-progress --no-suggest --no-ansi --no-interaction --no-scripts

- name: Run style tests
run: vendor/bin/ecs check -vv

- name: Run static analyse tests
run: vendor/bin/psalm
run: vendor/bin/psalm --php-version=${{ matrix.php-version }}

- name: Run unit tests
run: vendor/bin/phpunit
28 changes: 28 additions & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace PetitPress\GpsMessengerBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

final class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('petit_press_gps_messenger');
$rootNode = $treeBuilder->getRootNode();

$rootNode
->children()
->scalarNode('auth_cache')
->cannotBeEmpty()
->defaultValue('cache.app')
->info('A cache for storing access tokens.')
->end()
->end();

return $treeBuilder;
}
}
22 changes: 0 additions & 22 deletions DependencyInjection/GpsMessengerExtension.php

This file was deleted.

32 changes: 32 additions & 0 deletions DependencyInjection/PetitPressGpsMessengerExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace PetitPress\GpsMessengerBundle\DependencyInjection;

use PetitPress\GpsMessengerBundle\Transport\GpsTransportFactory;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

final class PetitPressGpsMessengerExtension extends Extension
{
/**
* {@inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container): void
{
$loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.php');

$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);

if ($config['auth_cache']) {
$gpsTransportFactoryDefinition = $container->getDefinition(GpsTransportFactory::class);
$gpsTransportFactoryDefinition->replaceArgument(1, new Reference($config['auth_cache']));
}
}
}
6 changes: 3 additions & 3 deletions GpsMessengerBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace PetitPress\GpsMessengerBundle;

use PetitPress\GpsMessengerBundle\DependencyInjection\GpsMessengerExtension;
use PetitPress\GpsMessengerBundle\DependencyInjection\PetitPressGpsMessengerExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

Expand All @@ -15,6 +15,6 @@ final class GpsMessengerBundle extends Bundle
*/
public function build(ContainerBuilder $container): void
{
$container->registerExtension(new GpsMessengerExtension());
$container->registerExtension(new PetitPressGpsMessengerExtension());
}
}
}
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,23 @@ framework:
dsn: 'gps://default/messages?client_config[apiEndpoint]=https://europe-west3-pubsub.googleapis.com&max_messages_pull=10'
```
### Step 4: Use available stamps if needed
### Step 4: Configure PetitPressGpsMessengerBundle (optional)
Configure the cache service where authentication tokens are stored. The default is `cache.app`.

```yaml
# config/packages/petit_press_gps_messenger.yaml
petit_press_gps_messenger:
auth_cache: 'cache.app'
```

### Step 5: Use available stamps if needed

* `OrderingKeyStamp`: use for keeping messages of the same context in order.
For more information, read an [official documentation](https://cloud.google.com/pubsub/docs/publisher#using_ordering_keys).

### Step 5: Create topics from config
### Step 6: Create topics from config
```bash
bin/console messenger:setup-transports
```
24 changes: 24 additions & 0 deletions Resources/config/services.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

use PetitPress\GpsMessengerBundle\Transport\GpsTransportFactory;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\DependencyInjection\Loader\Configurator\ReferenceConfigurator;
use PetitPress\GpsMessengerBundle\Transport\GpsConfigurationResolverInterface;
use PetitPress\GpsMessengerBundle\Transport\GpsConfigurationResolver;

return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->services()
->set(GpsTransportFactory::class)
->args([
new ReferenceConfigurator(GpsConfigurationResolverInterface::class),
null
])
->tag('messenger.transport_factory')

->set(GpsConfigurationResolver::class)

->alias(GpsConfigurationResolverInterface::class, GpsConfigurationResolver::class)
;
};
8 changes: 0 additions & 8 deletions Resources/config/services.yaml

This file was deleted.

96 changes: 96 additions & 0 deletions Tests/DependencyInjection/PetitPressGpsMessengerExtensionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

declare(strict_types=1);

namespace DependencyInjection;

use PetitPress\GpsMessengerBundle\DependencyInjection\PetitPressGpsMessengerExtension;
use PetitPress\GpsMessengerBundle\Transport\GpsTransportFactory;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Yaml\Parser;

class PetitPressGpsMessengerExtensionTest extends TestCase
{
private ?ContainerBuilder $configuration;

protected function tearDown(): void
{
$this->configuration = null;
}

public function testSimpleConfiguration(): void
{
$this->configuration = new ContainerBuilder();
$loader = new PetitPressGpsMessengerExtension();
$config = $this->getSimpleConfig();
$loader->load([$config], $this->configuration);

$this->assertTrue($this->configuration->hasDefinition(GpsTransportFactory::class));
$gpsTransportFactoryDefinition = $this->configuration->getDefinition(GpsTransportFactory::class);
$cacheArgument = $gpsTransportFactoryDefinition->getArgument(1);
$this->assertInstanceOf(Reference::class, $cacheArgument);
$this->assertEquals('cache.app', (string) $cacheArgument);
}

/**
* @return mixed
*/
private function getSimpleConfig()
{
// use all defaults
return (new Parser())->parse('');
}

public function testFullConfiguration(): void
{
$this->configuration = new ContainerBuilder();
$loader = new PetitPressGpsMessengerExtension();
$config = $this->getFullConfig();
$loader->load([$config], $this->configuration);

$this->assertTrue($this->configuration->hasDefinition(GpsTransportFactory::class));
$gpsTransportFactoryDefinition = $this->configuration->getDefinition(GpsTransportFactory::class);
$cacheArgument = $gpsTransportFactoryDefinition->getArgument(1);
$this->assertInstanceOf(Reference::class, $cacheArgument);
$this->assertEquals('foo', (string) $cacheArgument);
}

/**
* @return mixed
*/
private function getFullConfig()
{
$yaml = <<<EOF
auth_cache: 'foo'
EOF;

return (new Parser())->parse($yaml);
}


public function testConfigurationWithDisabledAuthCache(): void
{
$this->configuration = new ContainerBuilder();
$loader = new PetitPressGpsMessengerExtension();
$config = $this->getDisabledCacheConfig();
$loader->load([$config], $this->configuration);

$this->assertTrue($this->configuration->hasDefinition(GpsTransportFactory::class));
$gpsTransportFactoryDefinition = $this->configuration->getDefinition(GpsTransportFactory::class);
$this->assertNull($gpsTransportFactoryDefinition->getArgument(1));
}

/**
* @return mixed
*/
private function getDisabledCacheConfig()
{
$yaml = <<<EOF
auth_cache: false
EOF;

return (new Parser())->parse($yaml);
}
}
4 changes: 3 additions & 1 deletion Tests/Transport/GpsTransportFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PetitPress\GpsMessengerBundle\Transport\GpsConfigurationResolverInterface;
use PetitPress\GpsMessengerBundle\Transport\GpsTransportFactory;
use PHPUnit\Framework\TestCase;
use Psr\Cache\CacheItemPoolInterface;

class GpsTransportFactoryTest extends TestCase
{
Expand All @@ -15,7 +16,8 @@ class GpsTransportFactoryTest extends TestCase
protected function setUp(): void
{
$this->subject = new GpsTransportFactory(
$this->createMock(GpsConfigurationResolverInterface::class)
$this->createMock(GpsConfigurationResolverInterface::class),
$this->createMock(CacheItemPoolInterface::class)
);
}

Expand Down
8 changes: 1 addition & 7 deletions Transport/GpsReceiver.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,6 @@ private function createEnvelopeFromPubSubMessage(Message $message): Envelope
throw new MessageDecodingFailedException($exception->getMessage(), 0, $exception);
}

try {
$envelope = $this->serializer->decode($rawData);
} catch (MessageDecodingFailedException $exception) {
throw $exception;
}

return $envelope->with(new GpsReceivedStamp($message));
return $this->serializer->decode($rawData)->with(new GpsReceivedStamp($message));
}
}
7 changes: 6 additions & 1 deletion Transport/GpsSender.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Google\Cloud\PubSub\PubSubClient;
use PetitPress\GpsMessengerBundle\Transport\Stamp\OrderingKeyStamp;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\TransportException;
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
use Symfony\Component\Messenger\Transport\Sender\SenderInterface;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
Expand Down Expand Up @@ -39,7 +40,11 @@ public function send(Envelope $envelope): Envelope
$encodedMessage = $this->serializer->encode($envelope);

$messageBuilder = new MessageBuilder();
$messageBuilder = $messageBuilder->setData(json_encode($encodedMessage));
try {
$messageBuilder = $messageBuilder->setData(json_encode($encodedMessage, JSON_THROW_ON_ERROR));
} catch (\JsonException $exception) {
throw new TransportException($exception->getMessage(), 0, $exception);
}

$redeliveryStamp = $envelope->last(RedeliveryStamp::class);
if ($redeliveryStamp instanceof RedeliveryStamp) {
Expand Down
12 changes: 10 additions & 2 deletions Transport/GpsTransportFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace PetitPress\GpsMessengerBundle\Transport;

use Google\Cloud\PubSub\PubSubClient;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
use Symfony\Component\Messenger\Transport\TransportFactoryInterface;
use Symfony\Component\Messenger\Transport\TransportInterface;
Expand All @@ -15,10 +16,12 @@
final class GpsTransportFactory implements TransportFactoryInterface
{
private GpsConfigurationResolverInterface $gpsConfigurationResolver;
private ?CacheItemPoolInterface $cache;

public function __construct(GpsConfigurationResolverInterface $gpsConfigurationResolver)
public function __construct(GpsConfigurationResolverInterface $gpsConfigurationResolver, ?CacheItemPoolInterface $cache)
{
$this->gpsConfigurationResolver = $gpsConfigurationResolver;
$this->cache = $cache;
}

/**
Expand All @@ -28,8 +31,13 @@ public function createTransport(string $dsn, array $options, SerializerInterface
{
$options = $this->gpsConfigurationResolver->resolve($dsn, $options);

$clientConfig = $options->getClientConfig();
if ($this->cache instanceof CacheItemPoolInterface) {
$clientConfig['authCache'] ??= $this->cache;
}

return new GpsTransport(
new PubSubClient($options->getClientConfig()),
new PubSubClient($clientConfig),
$options,
$serializer
);
Expand Down
Loading

0 comments on commit 29211cc

Please sign in to comment.