Skip to content

Commit

Permalink
add optional config merging (#57)
Browse files Browse the repository at this point in the history
* add optional config merging

* remove getRootNode for compatibility
  • Loading branch information
howardlopez committed Mar 5, 2020
1 parent fecf71d commit 0a66b85
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 5 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,15 @@ class AppKernel extends Kernel

## Configuration

Configuration is handled by the SDK rather than by the bundle, and no validation
is performed at compile time. Full documentation of the configuration options
available can be read in the [SDK Guide](http://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/configuration.html).
By default, configuration is handled by the SDK rather than by the bundle, and
no validation is performed at compile time. Full documentation of the
configuration options available can be read in the [SDK Guide](http://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/configuration.html).

If AWS_MERGE_CONFIG environment variable is set to `true`, configuration
validation and merging are enabled. The bundle validates and merges known
configuration options, including for each service. Additional configuration
options can be included in a single configuration file, but merging will fail
if non-standard options are specified in more than once.

To use a service for any configuration value, use `@` followed by the service
name, such as `@a_service`. This syntax will be converted to a service during
Expand Down
97 changes: 95 additions & 2 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,105 @@ class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
// Maintain backwars compatibility, only merge when AWS_MERGE_CONFIG is set
$mergeConfig = getenv('AWS_MERGE_CONFIG') ?: false;
$treeType = 'variable';

if ($mergeConfig) {
$treeType = 'array';
}

// Most recent versions of TreeBuilder have a constructor
if (\method_exists(TreeBuilder::class, '__construct')) {
$treeBuilder = new TreeBuilder('aws', 'variable');
$treeBuilder = new TreeBuilder('aws', $treeType);
} else { // which is not the case for older versions
$treeBuilder = new TreeBuilder;
$treeBuilder->root('aws', 'variable');
$treeBuilder->root('aws', $treeType);
}

// If not AWS_MERGE_CONFIG, return empty, variable TreeBuilder
if (!$mergeConfig) {
return $treeBuilder;
}

$rootNode = $treeBuilder->root('aws');

// Define TreeBuilder to allow config validation and merging
$rootNode
->ignoreExtraKeys(false)
->children()
->variableNode('credentials')->end()
->variableNode('debug')->end()
->variableNode('stats')->end()
->scalarNode('endpoint')->end()
->variableNode('endpoint_discovery')->end()
->arrayNode('http')
->children()
->floatNode('connect_timeout')->end()
->booleanNode('debug')->end()
->booleanNode('decode_content')->end()
->integerNode('delay')->end()
->variableNode('expect')->end()
->variableNode('proxy')->end()
->scalarNode('sink')->end()
->booleanNode('synchronous')->end()
->booleanNode('stream')->end()
->floatNode('timeout')->end()
->scalarNode('verify')->end()
->end()
->end()
->scalarNode('profile')->end()
->scalarNode('region')->end()
->integerNode('retries')->end()
->scalarNode('scheme')->end()
->scalarNode('service')->end()
->scalarNode('signature_version')->end()
->variableNode('ua_append')->end()
->variableNode('validate')->end()
->scalarNode('version')->end()
->end()
;

//Setup config trees for each of the services
foreach (array_column(Aws\manifest(), 'namespace') as $awsService) {
$rootNode
->children()
->arrayNode($awsService)
->ignoreExtraKeys(false)
->children()
->variableNode('credentials')->end()
->variableNode('debug')->end()
->variableNode('stats')->end()
->scalarNode('endpoint')->end()
->variableNode('endpoint_discovery')->end()
->arrayNode('http')
->children()
->floatNode('connect_timeout')->end()
->booleanNode('debug')->end()
->booleanNode('decode_content')->end()
->integerNode('delay')->end()
->variableNode('expect')->end()
->variableNode('proxy')->end()
->scalarNode('sink')->end()
->booleanNode('synchronous')->end()
->booleanNode('stream')->end()
->floatNode('timeout')->end()
->scalarNode('verify')->end()
->end()
->end()
->scalarNode('profile')->end()
->scalarNode('region')->end()
->integerNode('retries')->end()
->scalarNode('scheme')->end()
->scalarNode('service')->end()
->scalarNode('signature_version')->end()
->variableNode('ua_append')->end()
->variableNode('validate')->end()
->scalarNode('version')->end()
->end()
->end()
->end()
;
}

return $treeBuilder;
Expand Down
116 changes: 116 additions & 0 deletions tests/DependencyInjection/AwsExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,120 @@ public function extension_should_expand_service_references()

$extension->load([$config], $container);
}

/**
* @test
*/
public function extension_should_validate_and_merge_configs()
{
putenv('AWS_MERGE_CONFIG=true');
$extension = new AwsExtension;
$config = [
'credentials' => false,
'debug' => [
'http' => true
],
'stats' => [
'http' => true
],
'retries' => 5,
'endpoint' => 'http://localhost:8000',
'endpoint_discovery' => [
'enabled' => true,
'cache_limit' => 1000
],
'http' => [
'connect_timeout' => 5.5,
'debug' => true,
'decode_content' => true,
'delay' => 1,
'expect' => true,
'proxy' => 'http://localhost:9000',
'sink' => '/path/to/sink',
'synchronous' => true,
'stream' => true,
'timeout' => 3.14,
'verify' => '/path/to/ca_cert_bundle'
],
'profile' => 'prod',
'region' => 'us-west-2',
'retries' => 5,
'scheme' => 'http',
'signature_version' => 'v4',
'ua_append' => [
'prod',
'foo'
],
'validate' => [
'required' => true
],
'version' => 'latest',
'S3' => [
'version' => '2006-03-01',
]
];
$configDev = [
'credentials' => '@aws_sdk',
'debug' => true,
'stats' => true,
'ua_append' => 'dev',
'validate' => true,
];
$container = $this->getMockBuilder(ContainerBuilder::class)
->setMethods(['getDefinition', 'replaceArgument'])
->getMock();
$container->expects($this->once())
->method('getDefinition')
->with('aws_sdk')
->willReturnSelf();
$container->expects($this->once())
->method('replaceArgument')
->with(0, $this->callback(function ($arg) {
return is_array($arg)
&& isset($arg['credentials'])
&& $arg['credentials'] instanceof Reference
&& (string) $arg['credentials'] === 'aws_sdk'
&& isset($arg['debug'])
&& (bool) $arg['debug'] === true
&& isset($arg['stats'])
&& (bool) $arg['stats'] === true
&& isset($arg['retries'])
&& (integer) $arg['retries'] === 5
&& isset($arg['endpoint'])
&& (string) $arg['endpoint'] === 'http://localhost:8000'
&& isset($arg['validate'])
&& (bool) $arg['validate'] === true
&& isset($arg['endpoint_discovery']['enabled'])
&& isset($arg['endpoint_discovery']['cache_limit'])
&& (bool) $arg['endpoint_discovery']['enabled'] === true
&& (integer) $arg['endpoint_discovery']['cache_limit'] === 1000
&& isset($arg['S3']['version'])
&& (string) $arg['S3']['version'] === '2006-03-01'
;
}));

$extension->load([$config, $configDev], $container);
}

/**
* @test
*
* @expectedException RuntimeException
*/
public function extension_should_error_merging_unknown_config_options()
{
putenv('AWS_MERGE_CONFIG=true');
$extension = new AwsExtension;
$config = [
'foo' => 'bar'
];
$configDev = [
'foo' => 'baz'
];
$container = $this->getMockBuilder(ContainerBuilder::class)
->setMethods(['getDefinition', 'replaceArgument'])
->getMock();

$extension->load([$config, $configDev], $container);
}
}

0 comments on commit 0a66b85

Please sign in to comment.