Skip to content

Commit ac51c9d

Browse files
committed
Merge pull request #15 from webfactory/sqs_driver
SQS driver support for 1.x
2 parents 1b8bb6d + d4e4033 commit ac51c9d

7 files changed

Lines changed: 232 additions & 12 deletions

File tree

DependencyInjection/BernardBernardExtension.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
use Symfony\Component\DependencyInjection\ContainerBuilder;
77
use Symfony\Component\DependencyInjection\Definition;
88
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
9-
use Symfony\Component\DependencyInjection\DefinitionDecorator;
109
use Symfony\Component\DependencyInjection\Reference;
10+
use Symfony\Component\HttpKernel\Kernel;
1111

1212
class BernardBernardExtension extends \Symfony\Component\HttpKernel\DependencyInjection\Extension
1313
{
@@ -41,6 +41,10 @@ public function load(array $configs, ContainerBuilder $container)
4141
$this->registerJmsConfiguration($container);
4242
}
4343

44+
if ($config['driver'] == 'sqs') {
45+
$this->registerSqsConfiguration($config, $container);
46+
}
47+
4448
$this->registerMiddlewaresConfiguration($config['middlewares'], $container);
4549
}
4650

@@ -49,6 +53,30 @@ protected function registerFlatFileConfiguration($config, $container)
4953
$container->getDefinition('bernard.driver.file')->replaceArgument(0, $config['directory']);
5054
}
5155

56+
protected function registerSqsConfiguration(array $config, ContainerBuilder $container)
57+
{
58+
$sqsClientDefinition = new Definition();
59+
if ($this->definitionClassDeprecatesSetFactoryClassAndSetFactoryMethod()) {
60+
$sqsClientDefinition->setFactory('Aws\Sqs\SqsClient::factory');
61+
} else {
62+
$sqsClientDefinition->setFactoryClass('Aws\Sqs\SqsClient')
63+
->setFactoryMethod('factory');
64+
}
65+
$sqsClientDefinition->setArguments(
66+
array(
67+
array(
68+
'region' => $config['sqs']['region'],
69+
'key' => $config['sqs']['key'],
70+
'secret' => $config['sqs']['secret'],
71+
)
72+
)
73+
);
74+
$container->getDefinition('bernard.driver.sqs')->replaceArgument(0, $sqsClientDefinition);
75+
76+
$container->getDefinition('bernard.driver.sqs')->replaceArgument(1, $config['options']['queue_map']);
77+
$container->getDefinition('bernard.driver.sqs')->replaceArgument(2, $config['options']['prefetch']);
78+
}
79+
5280
protected function registerDoctrineConfiguration($config, $container)
5381
{
5482
$container->getDefinition('bernard.schema_listener')
@@ -91,4 +119,12 @@ protected function registerPhpRedisConfiguration($config, $container)
91119
{
92120
$container->getDefinition('bernard.driver.phpredis')->replaceArgument(0, new Reference($config['phpredis_service']));
93121
}
122+
123+
/**
124+
* @return bool
125+
*/
126+
private function definitionClassDeprecatesSetFactoryClassAndSetFactoryMethod()
127+
{
128+
return method_exists(new Definition(), 'setFactory');
129+
}
94130
}

DependencyInjection/Configuration.php

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,31 @@
33
namespace Bernard\BernardBundle\DependencyInjection;
44

55
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
6+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
67
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
8+
use Symfony\Component\Config\Definition\ConfigurationInterface;
79

8-
class Configuration implements \Symfony\Component\Config\Definition\ConfigurationInterface
10+
class Configuration implements ConfigurationInterface
911
{
1012
public function getConfigTreeBuilder()
1113
{
1214
$tree = new TreeBuilder();
1315
$root = $tree->root('bernard_bernard');
1416

17+
$this->addNodes($root);
18+
$this->addValidationRules($root);
19+
20+
return $tree;
21+
}
22+
23+
protected function addNodes(NodeDefinition $root)
24+
{
1525
$root
16-
->validate()
17-
->ifTrue(function ($v) { return 'file' === $v['driver'] && empty($v['options']['directory']); })
18-
->thenInvalid('The "directory" option must be defined when using the file driver.')
19-
->end()
2026
->children()
2127
->enumNode('driver')
22-
->values(array('file', 'predis', 'doctrine', 'phpredis', 'ironmq'))
28+
->values(array('file', 'predis', 'doctrine', 'phpredis', 'ironmq', 'sqs'))
29+
->isRequired()
30+
->cannotBeEmpty()
2331
->end()
2432
->enumNode('serializer')
2533
->defaultValue('simple')
@@ -33,6 +41,14 @@ public function getConfigTreeBuilder()
3341
->booleanNode('failures')->defaultFalse()->end()
3442
->end()
3543
->end()
44+
->arrayNode('sqs')
45+
->addDefaultsIfNotSet()
46+
->children()
47+
->scalarNode('region')->defaultNull()->end()
48+
->scalarNode('key')->defaultNull()->end()
49+
->scalarNode('secret')->defaultNull()->end()
50+
->end()
51+
->end()
3652
->arrayNode('options')
3753
->addDefaultsIfNotSet()
3854
->children()
@@ -41,12 +57,34 @@ public function getConfigTreeBuilder()
4157
->scalarNode('connection')->defaultValue('default')->end()
4258
->scalarNode('phpredis_service')->defaultValue('snc_redis.bernard')->end()
4359
->scalarNode('ironmq_service')->defaultNull()->end()
44-
->arrayNode('queue_map')->prototype('array')->end()
60+
->arrayNode('queue_map')
61+
->useAttributeAsKey('name')
62+
->prototype('scalar')->end()
63+
->end()
4564
->end()
4665
->end()
4766
->end()
4867
;
68+
}
4969

50-
return $tree;
70+
protected function addValidationRules(NodeDefinition $root)
71+
{
72+
$root
73+
->validate()
74+
->ifTrue(function ($v) { return 'file' === $v['driver'] && empty($v['options']['directory']); })
75+
->thenInvalid('The "directory" option must be defined when using the file driver.')
76+
->end()
77+
->validate()
78+
->ifTrue(function ($v) { return 'sqs' === $v['driver'] && empty($v['sqs']['region']); })
79+
->thenInvalid('The "region" option must be defined when using the sqs driver.')
80+
->end()
81+
->validate()
82+
->ifTrue(function ($v) { return 'sqs' === $v['driver'] && empty($v['sqs']['key']); })
83+
->thenInvalid('The "key" option must be defined when using the sqs driver.')
84+
->end()
85+
->validate()
86+
->ifTrue(function ($v) { return 'sqs' === $v['driver'] && empty($v['sqs']['secret']); })
87+
->thenInvalid('The "secret" option must be defined when using the sqs driver.')
88+
->end();
5189
}
5290
}

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function registerBundles()
3535
``` yml
3636
# .. previous content of app/config/config.yml
3737
bernard_bernard:
38-
driver: file # you can choose predis, phpredis, file, doctrine etc.
38+
driver: file # you can choose predis, phpredis, file, doctrine, sqs etc.
3939
serializer: simple # this is the default and it is optional. Other values are symfony or jms
4040
```
4141
@@ -164,3 +164,20 @@ bernard_bernard:
164164
options:
165165
ironmq_service: ironmq_connection
166166
```
167+
168+
### Amazon SQS
169+
170+
To use Amazon SQS, configure your driver like this:
171+
172+
``` yaml
173+
bernard_bernard:
174+
driver: sqs
175+
options:
176+
queue_map: # optional for aliasing queue urls, e.g.:
177+
send_newsletter: https://sqs.eu-west-1.amazonaws.com/...
178+
prefetch: 1 # optional, but beware the default is >1 and you may run into invisibility timeout problems with that
179+
sqs:
180+
region: "your aws region" # e.g. "eu-west-1"
181+
key: "your aws user's key"
182+
secret: "your aws user's secret"
183+
```

Resources/config/services.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@
7373
<argument /><!-- IronMQ connection instance -->
7474
</service>
7575

76+
<service id="bernard.driver.sqs" class="Bernard\Driver\SqsDriver" public="false">
77+
<argument /><!-- SQS client-->
78+
<argument /><!-- queue map -->
79+
<argument /><!-- prefetch -->
80+
</service>
81+
7682
<!-- Middlewares -->
7783
<service id="bernard.middleware.error_log" class="Bernard\Middleware\ErrorLogFactory" public="false" />
7884

Tests/DependencyInjection/BernardBernardExtensionTest.php

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,21 @@
44

55
use Bernard\BernardBundle\DependencyInjection\BernardBernardExtension;
66
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
use Symfony\Component\DependencyInjection\Definition;
8+
use Symfony\Component\HttpKernel\Kernel;
79

810
class BernardBernardExtensionTest extends \PHPUnit_Framework_TestCase
911
{
12+
/**
13+
* @var BernardBernardExtension
14+
*/
15+
protected $extension;
16+
17+
/**
18+
* @var ContainerBuilder
19+
*/
20+
protected $container;
21+
1022
public function setUp()
1123
{
1224
$this->extension = new BernardBernardExtension;
@@ -49,7 +61,7 @@ public function testMiddlewaresHaveMiddlewareTag()
4961
$this->assertEquals(array(array('type' => 'consumer')), $definition->getTag('bernard.middleware'));
5062
}
5163

52-
public function testDoctrinEventListenerIsAdded()
64+
public function testDoctrineEventListenerIsAdded()
5365
{
5466
$config = array_filter(array('driver' => 'doctrine', 'options' => array('connection' => 'bernard')));
5567

@@ -97,4 +109,58 @@ public function testDriverIsAliased()
97109
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Alias', $alias);
98110
$this->assertEquals('bernard.driver.doctrine', (string) $alias);
99111
}
112+
113+
public function testSqsDriverCanBeBuildFromConfiguration()
114+
{
115+
$configuredQueueMap = array('name1' => 'url1', 'name2' => 'url2');
116+
$configuredPrefetch = 5;
117+
$configuredRegion = 'test-region';
118+
$configuredKey = 'test-key';
119+
$configuredSecret = 'test-secret';
120+
121+
$config = array(
122+
'driver' => 'sqs',
123+
'options' => array(
124+
'queue_map' => $configuredQueueMap,
125+
'prefetch' => $configuredPrefetch,
126+
),
127+
'sqs' => array(
128+
'region' => $configuredRegion,
129+
'key' => $configuredKey,
130+
'secret' => $configuredSecret,
131+
),
132+
);
133+
134+
$this->extension->load(array($config), $this->container);
135+
$driverDefinition = $this->container->getDefinition('bernard.driver.sqs');
136+
137+
/** @var Definition $resultingSqsClientArgument */
138+
$resultingSqsClientArgument = $driverDefinition->getArgument(0);
139+
if ($this->definitionClassDeprecatesSetFactoryClassAndSetFactoryMethod()) {
140+
$this->assertSame(array('Aws\Sqs\SqsClient', 'factory'), $resultingSqsClientArgument->getFactory());
141+
} else {
142+
$this->assertSame('Aws\Sqs\SqsClient', $resultingSqsClientArgument->getFactoryClass());
143+
$this->assertSame('factory', $resultingSqsClientArgument->getFactoryMethod());
144+
}
145+
146+
$sqsClientFactoryArguments = $resultingSqsClientArgument->getArguments();
147+
$sqsClientFactoryConfiguration = $sqsClientFactoryArguments[0];
148+
$this->assertSame($configuredRegion, $sqsClientFactoryConfiguration['region']);
149+
$this->assertSame($configuredKey, $sqsClientFactoryConfiguration['key']);
150+
$this->assertSame($configuredSecret, $sqsClientFactoryConfiguration['secret']);
151+
152+
$resultingQueueMapArgument = $driverDefinition->getArgument(1);
153+
$this->assertEquals($configuredQueueMap, $resultingQueueMapArgument);
154+
155+
$resultingPrefetchArgument = $driverDefinition->getArgument(2);
156+
$this->assertEquals($configuredPrefetch, $resultingPrefetchArgument);
157+
}
158+
159+
/**
160+
* @return bool
161+
*/
162+
private function definitionClassDeprecatesSetFactoryClassAndSetFactoryMethod()
163+
{
164+
return method_exists(new Definition(), 'setFactory');
165+
}
100166
}

Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public function testDefaults()
1717

1818
$this->assertEquals(array('error_log' => false, 'logger' => false, 'failures' => false), $config['middlewares']);
1919
$this->assertEquals(array('prefetch' => null, 'directory' => '', 'connection' => 'default', 'phpredis_service' => 'snc_redis.bernard', 'ironmq_service' => null, 'queue_map' => array()), $config['options']);
20+
$this->assertEquals(array('region' => null, 'key' => null, 'secret' => null), $config['sqs']);
2021
}
2122

2223
public function testDriverIsRequired()
@@ -47,10 +48,65 @@ public function testFileDriverRequiresDirectoryOptionToBeSet()
4748
$this->processConfig(array('driver' => 'file'));
4849
}
4950

51+
public function testSqsDriverWorksWithRequiredOptionsSet()
52+
{
53+
$this->setExpectedException(null);
54+
55+
$this->processConfig($this->createValidSqsConfiguration());
56+
}
57+
58+
public function testSqsDriverRequiresRegionOptionToBeSet()
59+
{
60+
$this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
61+
62+
$configurationWithoutRegion = $this->createValidSqsConfiguration();
63+
$configurationWithoutRegion['options']['sqs']['region'] = null;
64+
$this->processConfig($configurationWithoutRegion);
65+
}
66+
67+
public function testSqsDriverRequiresKeyOptionToBeSet()
68+
{
69+
$this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
70+
71+
$configurationWithoutKey = $this->createValidSqsConfiguration();
72+
$configurationWithoutKey['options']['sqs']['key'] = null;
73+
$this->processConfig($configurationWithoutKey);
74+
}
75+
76+
public function testSqsDriverRequiresSecretOptionToBeSet()
77+
{
78+
$this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
79+
80+
$configurationWithoutSecret = $this->createValidSqsConfiguration();
81+
$configurationWithoutSecret['options']['sqs']['secret'] = null;
82+
$this->processConfig($configurationWithoutSecret);
83+
}
84+
5085
protected function processConfig($config)
5186
{
5287
$processor = new Processor;
5388

5489
return $processor->processConfiguration(new Configuration, array($config));
5590
}
91+
92+
/**
93+
* @return array(string => string|array)
94+
*/
95+
private function createValidSqsConfiguration()
96+
{
97+
return array(
98+
'driver' => 'sqs',
99+
'options' => array(
100+
'queue_map' => array(
101+
'name1' => 'url1',
102+
'name2' => 'url2',
103+
)
104+
),
105+
'sqs' => array(
106+
'region' => 'test-region',
107+
'key' => 'test-key',
108+
'secret' => 'test-secret',
109+
),
110+
);
111+
}
56112
}

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515

1616
"require-dev" : {
1717
"symfony/console" : "~2.1",
18-
"symfony/finder" : "~2.1"
18+
"symfony/finder" : "~2.1",
19+
"phpunit/phpunit": "~4.0"
1920
},
2021

2122
"extra" : {

0 commit comments

Comments
 (0)