├── .gitattributes ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json └── src ├── AsyncAwsBundle.php ├── DependencyInjection ├── AsyncAwsExtension.php ├── AwsPackagesProvider.php ├── Compiler │ └── InjectCasterPass.php └── Configuration.php ├── Secrets ├── CachedEnvVarLoader.php └── SsmVault.php └── VarDumper └── ResultCaster.php /.gitattributes: -------------------------------------------------------------------------------- 1 | /.github export-ignore 2 | /tests export-ignore 3 | /.gitignore export-ignore 4 | /Makefile export-ignore 5 | /phpunit.xml.dist export-ignore 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## NOT RELEASED 4 | 5 | ## 1.14.0 6 | 7 | ### Added 8 | 9 | - Support for BedrockRuntime 10 | 11 | ## 1.13.0 12 | 13 | ### Added 14 | 15 | - Support for SSOOIDC 16 | 17 | ## 1.12.3 18 | 19 | ### Changed 20 | 21 | - Enable compiler optimization for the `sprintf` function. 22 | 23 | ### Fixed 24 | 25 | - Fix bundle configuration with both `credential_provider` and `credential_provider_cache`. 26 | 27 | ## 1.12.2 28 | 29 | ### Changed 30 | 31 | - Replace deprecated Extension by Symfony\Component\DependencyInjection\Extension\Extension 32 | instead of the deprecated Symfony\Component\HttpKernel\DependencyInjection\Extension class 33 | 34 | ## 1.12.1 35 | 36 | ### Changed 37 | 38 | - Adding `async-aws/s3` 2.0, `async-aws/sqs` 2.0, `async-aws/ssm` 2.0 in dev dependencies 39 | - Adding `max_results` option in `secrets` configuration 40 | 41 | ## 1.12.0 42 | 43 | ### Added 44 | 45 | - Support for LocationService 46 | - Support for SSO 47 | 48 | ### Changed 49 | 50 | - Improve parameter type and return type in phpdoc 51 | 52 | ## 1.11.0 53 | 54 | ### Added 55 | 56 | - Support for Athena 57 | - Support for MediaConvert 58 | 59 | ## 1.10.0 60 | 61 | ### Added 62 | 63 | - Support for Scheduler 64 | 65 | ## 1.9.0 66 | 67 | ### Added 68 | 69 | - Support for Iot Data 70 | 71 | ## 1.8.0 72 | 73 | ### Added 74 | 75 | - Support for CodeBuild 76 | - Support for CodeCommit 77 | - Support for TimestreamQuery 78 | - Support for TimestreamWrite 79 | - Support for Iot Core 80 | 81 | ## 1.7.0 82 | 83 | ### Added 84 | 85 | - Support for KMS 86 | 87 | ## 1.6.0 88 | 89 | ### Added 90 | 91 | - Support for AppSync 92 | - Support for XRay 93 | 94 | ## 1.5.0 95 | 96 | ### Added 97 | 98 | - Support for CloudWatch 99 | - Support for ElastiCache 100 | - Support for Firehose 101 | - Support for Kinesis 102 | - Support for SecretsManager 103 | - Support for StepFunctions 104 | 105 | ## 1.4.0 106 | 107 | ### Added 108 | 109 | - Support for Symfony 6 110 | - Support for Route 53 111 | 112 | ## 1.3.0 113 | 114 | ### Added 115 | 116 | - The `async_aws.credential.cache` service will log cache failures. 117 | 118 | ## 1.2.0 119 | 120 | ### Added 121 | 122 | - Support for ECR client. 123 | 124 | ### Changed 125 | 126 | - Added channel "async_aws" to Monolog logger. 127 | 128 | ## 1.1.1 129 | 130 | ### Fixed 131 | 132 | - Make sure you can override config in different environments. 133 | 134 | ## 1.1.0 135 | 136 | ### Added 137 | 138 | - Support for Rekognition. 139 | 140 | ## 1.0.0 141 | 142 | ### Added 143 | 144 | - Support for PHP 8 145 | 146 | ## 0.2.6 147 | 148 | ### Added 149 | 150 | - Caching of credentials fetched by Clients 151 | 152 | ## 0.2.5 153 | 154 | ### Added 155 | 156 | - Support for EventBridge. 157 | - Support for IAM. 158 | - Caching of SSM parameters. 159 | 160 | ## 0.2.4 161 | 162 | ### Added 163 | 164 | - Support for Cognito Identity Provider. 165 | - Support for CloudWatch Logs. 166 | 167 | ## 0.2.3 168 | 169 | ### Changed 170 | 171 | - Support only version 1.0 of async-aws/core 172 | 173 | ## 0.2.2 174 | 175 | ### Added 176 | 177 | - Support for loading environment variables from SSM 178 | 179 | ### Fixed 180 | 181 | - Config when client config override default config. 182 | 183 | ## 0.2.1 184 | 185 | ### Added 186 | 187 | - Support for async-aws/core: ^0.4 and ^0.5 188 | - Support for DynamoDb 189 | 190 | ## 0.2.0 191 | 192 | ### Added 193 | 194 | - Support for async-aws/core: ^0.3 195 | - Adding VarDumpers 196 | 197 | ## 0.1.1 198 | 199 | ### Added 200 | 201 | - Support for async-aws/core: ^0.2 202 | - Support for CloudFormation, Lambda and SNS 203 | 204 | ### Fixed 205 | 206 | - Configuration bug where you specified `async_aws.client:` and nothing more 207 | 208 | ## 0.1.0 209 | 210 | First version 211 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Jérémy Derussé, Tobias Nyholm 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AsyncAws Symfony Bundle 2 | 3 | ![](https://github.com/async-aws/symfony-bundle/workflows/Tests/badge.svg?branch=master) 4 | ![](https://github.com/async-aws/symfony-bundle/workflows/BC%20Check/badge.svg?branch=master) 5 | 6 | A small SymfonyBundle that helps with configuration and autowiring. 7 | 8 | ## Install 9 | 10 | ```cli 11 | composer require async-aws/async-aws-bundle 12 | ``` 13 | 14 | ## Documentation 15 | 16 | See https://async-aws.com/integration/symfony-bundle.html for documentation. 17 | 18 | ## Contribute 19 | 20 | Contributions are welcome and appreciated. Please read https://async-aws.com/contribute/ 21 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async-aws/async-aws-bundle", 3 | "description": "Configure all your AsyncAws services and enjoy the autowire greatness.", 4 | "license": "MIT", 5 | "type": "symfony-bundle", 6 | "keywords": [ 7 | "aws", 8 | "amazon", 9 | "sdk", 10 | "async-aws", 11 | "symfony", 12 | "bundle" 13 | ], 14 | "require": { 15 | "php": "^7.2.5 || ^8.0", 16 | "async-aws/core": "^1.0", 17 | "symfony/config": "^4.4 || ^5.0 || ^6.0 || ^7.0", 18 | "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0 || ^7.0", 19 | "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0 || ^7.0" 20 | }, 21 | "require-dev": { 22 | "async-aws/s3": "^1.0 || ^2.0", 23 | "async-aws/ses": "^1.0", 24 | "async-aws/sqs": "^1.0 || ^2.0", 25 | "async-aws/ssm": "^1.0 || ^2.0", 26 | "matthiasnoback/symfony-config-test": "^4.1 || ^5.0", 27 | "nyholm/symfony-bundle-test": "^3.0", 28 | "symfony/cache": "^4.4 || ^5.0 || ^6.0 || ^7.0" 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "AsyncAws\\Symfony\\Bundle\\": "src" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "AsyncAws\\Symfony\\Bundle\\Tests\\": "tests/" 38 | } 39 | }, 40 | "extra": { 41 | "branch-alias": { 42 | "dev-master": "1.14-dev" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/AsyncAwsBundle.php: -------------------------------------------------------------------------------- 1 | addCompilerPass(new InjectCasterPass()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/DependencyInjection/AsyncAwsExtension.php: -------------------------------------------------------------------------------- 1 | processConfiguration($configuration, $configs); 32 | 33 | $usedServices = $this->registerConfiguredServices($container, $config); 34 | $usedServices = $this->registerInstalledServices($container, $config, $usedServices); 35 | $this->registerEnvLoader($container, $config); 36 | $this->autowireServices($container, $usedServices); 37 | } 38 | 39 | /** 40 | * @param array{ 41 | * register_service: bool, 42 | * logger?: string|null, 43 | * http_client?: string|null, 44 | * credential_provider: string|null, 45 | * credential_provider_cache: string|null, 46 | * config: array, 47 | * secrets: array{ 48 | * enabled: bool, 49 | * client: string|null, 50 | * path: string|null, 51 | * max_results: int|null, 52 | * recursive: bool, 53 | * cache: array{enabled: bool, pool: string, ttl: int}, 54 | * }, 55 | * clients: array, 62 | * }>, 63 | * } $config 64 | * 65 | * @return array 66 | */ 67 | private function registerConfiguredServices(ContainerBuilder $container, array $config): array 68 | { 69 | $availableServices = AwsPackagesProvider::getAllServices(); 70 | $usedServices = []; 71 | $defaultConfig = $config; 72 | unset($defaultConfig['clients'], $defaultConfig['secrets']); 73 | 74 | foreach ($config['clients'] as $name => $data) { 75 | $client = $availableServices[$data['type']]['class']; 76 | if (!class_exists($client)) { 77 | throw new InvalidConfigurationException(\sprintf('You have configured "async_aws.%s" but the "%s" package is not installed. Try running "composer require %s"', $name, $data['type'], $availableServices[$data['type']]['package'])); 78 | } 79 | 80 | $serviceConfig = array_merge($defaultConfig, $data); 81 | $serviceConfig['config'] = array_merge($defaultConfig['config'], $data['config']); 82 | if ($serviceConfig['register_service']) { 83 | $usedServices[$name] = $client; 84 | $this->addServiceDefinition($container, $name, $serviceConfig, $client); 85 | } else { 86 | $usedServices[$name] = null; 87 | } 88 | } 89 | 90 | return $usedServices; 91 | } 92 | 93 | /** 94 | * @param array{ 95 | * register_service: bool, 96 | * clients: array, 97 | * logger?: string|null, 98 | * http_client?: string|null, 99 | * credential_provider: string|null, 100 | * credential_provider_cache: string|null, 101 | * config: array, 102 | * } $config 103 | * @param array $usedServices 104 | * 105 | * @return array 106 | */ 107 | private function registerInstalledServices(ContainerBuilder $container, array $config, array $usedServices): array 108 | { 109 | if (!$config['register_service']) { 110 | return $usedServices; 111 | } 112 | 113 | unset($config['clients']); 114 | $availableServices = AwsPackagesProvider::getAllServices(); 115 | foreach ($availableServices as $name => $data) { 116 | if (\array_key_exists($name, $usedServices)) { 117 | continue; 118 | } 119 | 120 | $client = $data['class']; 121 | if (!class_exists($client)) { 122 | continue; 123 | } 124 | 125 | $usedServices[$name] = $client; 126 | $this->addServiceDefinition($container, $name, $config, $client); 127 | } 128 | 129 | return $usedServices; 130 | } 131 | 132 | /** 133 | * @param array{ 134 | * register_service: bool, 135 | * logger?: string|null, 136 | * http_client?: string|null, 137 | * credential_provider: string|null, 138 | * credential_provider_cache: string|null, 139 | * config: array, 140 | * ... 141 | * } $config 142 | */ 143 | private function addServiceDefinition(ContainerBuilder $container, string $name, array $config, string $clientClass): void 144 | { 145 | if (\array_key_exists('logger', $config)) { 146 | $logger = $config['logger'] ? new Reference($config['logger']) : null; 147 | } else { 148 | // Use default Symfony logger unless explicitly set to null. 149 | $logger = new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE); 150 | } 151 | if (\array_key_exists('http_client', $config)) { 152 | $httpClient = $config['http_client'] ? new Reference($config['http_client']) : null; 153 | } else { 154 | // Use default Symfony http_client unless explicitly set to null. 155 | if (class_exists(AwsHttpClientFactory::class)) { 156 | $httpClient = new Definition(HttpClientInterface::class); 157 | $httpClient->setFactory([AwsHttpClientFactory::class, 'createRetryableClient']) 158 | ->setArguments([ 159 | new Reference('http_client', ContainerInterface::NULL_ON_INVALID_REFERENCE), 160 | $logger, 161 | ]); 162 | } else { 163 | $httpClient = new Reference('http_client', ContainerInterface::NULL_ON_INVALID_REFERENCE); 164 | } 165 | } 166 | 167 | $credentialServiceId = $config['credential_provider']; 168 | $credentialProviderCache = class_exists(SymfonyCacheProvider::class) && interface_exists(CacheInterface::class) ? $config['credential_provider_cache'] : null; 169 | 170 | if (null === $credentialServiceId) { 171 | $credentialServiceId = 'async_aws.credential'; 172 | if (!$container->hasDefinition($credentialServiceId)) { 173 | $container->register($credentialServiceId, CredentialProvider::class) 174 | ->setFactory([ChainProvider::class, 'createDefaultChain']) 175 | ->setArguments([$httpClient, $logger]) 176 | ->addTag('monolog.logger', ['channel' => 'async_aws']); 177 | } 178 | } 179 | 180 | if (null !== $credentialProviderCache) { 181 | $container->register($credentialServiceId . '.cache', SymfonyCacheProvider::class) 182 | ->setDecoratedService($credentialServiceId) 183 | ->setArguments([ 184 | new Reference($credentialServiceId . '.cache.inner'), 185 | new Reference($credentialProviderCache), 186 | $logger, 187 | ]) 188 | ->addTag('monolog.logger', ['channel' => 'async_aws']); 189 | 190 | $container->register($credentialServiceId . '.memory', CacheProvider::class) 191 | ->setDecoratedService($credentialServiceId) 192 | ->setArguments([ 193 | new Reference($credentialServiceId . '.memory.inner'), 194 | ]); 195 | } 196 | 197 | $definition = new Definition($clientClass); 198 | $definition->setArguments([ 199 | $config['config'], 200 | $credentialServiceId ? new Reference($credentialServiceId) : null, 201 | $httpClient, 202 | $logger, 203 | ]); 204 | $definition->addTag('monolog.logger', ['channel' => 'async_aws']); 205 | $container->setDefinition(\sprintf('async_aws.client.%s', $name), $definition); 206 | } 207 | 208 | /** 209 | * @param array{ 210 | * register_service: bool, 211 | * logger?: string|null, 212 | * http_client?: string|null, 213 | * credential_provider: string|null, 214 | * credential_provider_cache: string|null, 215 | * config: array, 216 | * secrets: array{ 217 | * enabled: bool, 218 | * client: string|null, 219 | * path: string|null, 220 | * recursive: bool, 221 | * max_results: int|null, 222 | * cache: array{enabled: bool, pool: string, ttl: int}, 223 | * }, 224 | * clients: array, 225 | * } $config 226 | */ 227 | private function registerEnvLoader(ContainerBuilder $container, array $config): void 228 | { 229 | if (!$config['secrets']['enabled']) { 230 | return; 231 | } 232 | 233 | $availableServices = AwsPackagesProvider::getAllServices(); 234 | if (!class_exists($className = $availableServices['ssm']['class'])) { 235 | throw new InvalidConfigurationException(\sprintf('You have enabled "async_aws.secrets" but the "%s" package is not installed. Try running "composer require %s"', 'ssm', $availableServices['ssm']['package'])); 236 | } 237 | 238 | if (null !== $client = $config['secrets']['client']) { 239 | if (!isset($config['clients'][$client])) { 240 | throw new InvalidConfigurationException(\sprintf('The client "%s" configured in "async_aws.secrets" does not exists. Available clients are "%s"', $client, implode(', ', array_keys($config['clients'])))); 241 | } 242 | if ('ssm' !== $config['clients'][$client]['type']) { 243 | throw new InvalidConfigurationException(\sprintf('The client "%s" configured in "async_aws.secrets" is not a SSM client.', $client)); 244 | } 245 | } else { 246 | if (!isset($config['clients']['ssm'])) { 247 | $client = 'ssm'; 248 | } else { 249 | $client = 'secrets'; 250 | $i = 1; 251 | while (isset($config['clients'][$client])) { 252 | $client = 'secrets_' . $i; 253 | } 254 | } 255 | $this->addServiceDefinition($container, $client, $config, $className); 256 | } 257 | 258 | $container->register(SsmVault::class) 259 | ->setAutoconfigured(true) 260 | ->setArguments([ 261 | new Reference('async_aws.client.' . $client), 262 | $config['secrets']['path'], 263 | $config['secrets']['recursive'], 264 | $config['secrets']['max_results'], 265 | ]); 266 | 267 | if ($config['secrets']['cache']['enabled']) { 268 | if (!interface_exists(CacheInterface::class)) { 269 | throw new InvalidConfigurationException('You have enabled "async_aws.secrets.cache" but the "symfony/cache" package is not installed. Try running "composer require symfony/cache"'); 270 | } 271 | 272 | $container->register(CachedEnvVarLoader::class) 273 | ->setDecoratedService(SsmVault::class) 274 | ->setArguments([ 275 | new Reference(CachedEnvVarLoader::class . '.inner'), 276 | new Reference($config['secrets']['cache']['pool']), 277 | $config['secrets']['cache']['ttl'], 278 | ]); 279 | } 280 | } 281 | 282 | /** 283 | * @param array $usedServices 284 | */ 285 | private function autowireServices(ContainerBuilder $container, array $usedServices): void 286 | { 287 | $awsServices = AwsPackagesProvider::getAllServices(); 288 | foreach ($usedServices as $name => $client) { 289 | if (null === $client) { 290 | // This client is disabled. 291 | continue; 292 | } 293 | 294 | $serviceId = \sprintf('async_aws.client.%s', $name); 295 | if (isset($awsServices[$name])) { 296 | $container->setAlias($client, $serviceId); 297 | 298 | continue; 299 | } 300 | 301 | // Assert: Custom name 302 | $container->registerAliasForArgument($serviceId, $client, $name); 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /src/DependencyInjection/AwsPackagesProvider.php: -------------------------------------------------------------------------------- 1 | , package: string}> 13 | */ 14 | public static function getAllServices(): array 15 | { 16 | return [ 17 | 'app_sync' => [ 18 | 'class' => \AsyncAws\AppSync\AppSyncClient::class, 19 | 'package' => 'async-aws/app-sync', 20 | ], 21 | 'athena' => [ 22 | 'class' => \AsyncAws\Athena\AthenaClient::class, 23 | 'package' => 'async-aws/athena', 24 | ], 25 | 'bedrock_runtime' => [ 26 | 'class' => \AsyncAws\BedrockRuntime\BedrockRuntimeClient::class, 27 | 'package' => 'async-aws/bedrock-runtime', 28 | ], 29 | 'cloud_formation' => [ 30 | 'class' => \AsyncAws\CloudFormation\CloudFormationClient::class, 31 | 'package' => 'async-aws/cloud-formation', 32 | ], 33 | 'cloud_front' => [ 34 | 'class' => \AsyncAws\CloudFront\CloudFrontClient::class, 35 | 'package' => 'async-aws/cloud-front', 36 | ], 37 | 'cloud_watch' => [ 38 | 'class' => \AsyncAws\CloudWatch\CloudWatchClient::class, 39 | 'package' => 'async-aws/cloud-watch', 40 | ], 41 | 'cloud_watch_logs' => [ 42 | 'class' => \AsyncAws\CloudWatchLogs\CloudWatchLogsClient::class, 43 | 'package' => 'async-aws/cloud-watch-logs', 44 | ], 45 | 'code_build' => [ 46 | 'class' => \AsyncAws\CodeBuild\CodeBuildClient::class, 47 | 'package' => 'async-aws/code-build', 48 | ], 49 | 'code_commit' => [ 50 | 'class' => \AsyncAws\CodeCommit\CodeCommitClient::class, 51 | 'package' => 'async-aws/code-commit', 52 | ], 53 | 'code_deploy' => [ 54 | 'class' => \AsyncAws\CodeDeploy\CodeDeployClient::class, 55 | 'package' => 'async-aws/code-deploy', 56 | ], 57 | 'cognito_identity_provider' => [ 58 | 'class' => \AsyncAws\CognitoIdentityProvider\CognitoIdentityProviderClient::class, 59 | 'package' => 'async-aws/cognito-identity-provider', 60 | ], 61 | 'comprehend' => [ 62 | 'class' => \AsyncAws\Comprehend\ComprehendClient::class, 63 | 'package' => 'async-aws/comprehend', 64 | ], 65 | 'dynamo_db' => [ 66 | 'class' => \AsyncAws\DynamoDb\DynamoDbClient::class, 67 | 'package' => 'async-aws/dynamo-db', 68 | ], 69 | 'ecr' => [ 70 | 'class' => \AsyncAws\Ecr\EcrClient::class, 71 | 'package' => 'async-aws/ecr', 72 | ], 73 | 'elasti_cache' => [ 74 | 'class' => \AsyncAws\ElastiCache\ElastiCacheClient::class, 75 | 'package' => 'async-aws/elasti-cache', 76 | ], 77 | 'event_bridge' => [ 78 | 'class' => \AsyncAws\EventBridge\EventBridgeClient::class, 79 | 'package' => 'async-aws/event-bridge', 80 | ], 81 | 'firehose' => [ 82 | 'class' => \AsyncAws\Firehose\FirehoseClient::class, 83 | 'package' => 'async-aws/firehose', 84 | ], 85 | 'iam' => [ 86 | 'class' => \AsyncAws\Iam\IamClient::class, 87 | 'package' => 'async-aws/iam', 88 | ], 89 | 'iot' => [ 90 | 'class' => \AsyncAws\Iot\IotClient::class, 91 | 'package' => 'async-aws/iot', 92 | ], 93 | 'iot_data' => [ 94 | 'class' => \AsyncAws\IotData\IotDataClient::class, 95 | 'package' => 'async-aws/iot-data', 96 | ], 97 | 'kinesis' => [ 98 | 'class' => \AsyncAws\Kinesis\KinesisClient::class, 99 | 'package' => 'async-aws/kinesis', 100 | ], 101 | 'kms' => [ 102 | 'class' => \AsyncAws\Kms\KmsClient::class, 103 | 'package' => 'async-aws/kms', 104 | ], 105 | 'lambda' => [ 106 | 'class' => \AsyncAws\Lambda\LambdaClient::class, 107 | 'package' => 'async-aws/lambda', 108 | ], 109 | 'location_service' => [ 110 | 'class' => \AsyncAws\LocationService\LocationServiceClient::class, 111 | 'package' => 'async-aws/location-service', 112 | ], 113 | 'media_convert' => [ 114 | 'class' => \AsyncAws\MediaConvert\MediaConvertClient::class, 115 | 'package' => 'async-aws/media-convert', 116 | ], 117 | 'rds_data_service' => [ 118 | 'class' => \AsyncAws\RdsDataService\RdsDataServiceClient::class, 119 | 'package' => 'async-aws/rds-data-service', 120 | ], 121 | 'rekognition' => [ 122 | 'class' => \AsyncAws\Rekognition\RekognitionClient::class, 123 | 'package' => 'async-aws/rekognition', 124 | ], 125 | 'route53' => [ 126 | 'class' => \AsyncAws\Route53\Route53Client::class, 127 | 'package' => 'async-aws/route53', 128 | ], 129 | 's3' => [ 130 | 'class' => \AsyncAws\S3\S3Client::class, 131 | 'package' => 'async-aws/s3', 132 | ], 133 | 'simple_s3' => [ 134 | 'class' => \AsyncAws\SimpleS3\SimpleS3Client::class, 135 | 'package' => 'async-aws/simple-s3', 136 | ], 137 | 'scheduler' => [ 138 | 'class' => \AsyncAws\Scheduler\SchedulerClient::class, 139 | 'package' => 'async-aws/scheduler', 140 | ], 141 | 'secrets_manager' => [ 142 | 'class' => \AsyncAws\SecretsManager\SecretsManagerClient::class, 143 | 'package' => 'async-aws/secrets-manager', 144 | ], 145 | 'ses' => [ 146 | 'class' => \AsyncAws\Ses\SesClient::class, 147 | 'package' => 'async-aws/ses', 148 | ], 149 | 'sns' => [ 150 | 'class' => \AsyncAws\Sns\SnsClient::class, 151 | 'package' => 'async-aws/sns', 152 | ], 153 | 'sqs' => [ 154 | 'class' => \AsyncAws\Sqs\SqsClient::class, 155 | 'package' => 'async-aws/sqs', 156 | ], 157 | 'ssm' => [ 158 | 'class' => \AsyncAws\Ssm\SsmClient::class, 159 | 'package' => 'async-aws/ssm', 160 | ], 161 | 'sso' => [ 162 | 'class' => \AsyncAws\Sso\SsoClient::class, 163 | 'package' => 'async-aws/sso', 164 | ], 165 | 'sso_oidc' => [ 166 | 'class' => \AsyncAws\SsoOidc\SsoOidcClient::class, 167 | 'package' => 'async-aws/sso-oidc', 168 | ], 169 | 'sts' => [ 170 | 'class' => \AsyncAws\Core\Sts\StsClient::class, 171 | 'package' => 'async-aws/core', 172 | ], 173 | 'step_functions' => [ 174 | 'class' => \AsyncAws\StepFunctions\StepFunctionsClient::class, 175 | 'package' => 'async-aws/step-functions', 176 | ], 177 | 'timestream_query' => [ 178 | 'class' => \AsyncAws\TimestreamQuery\TimestreamQueryClient::class, 179 | 'package' => 'async-aws/timestream-query', 180 | ], 181 | 'timestream_write' => [ 182 | 'class' => \AsyncAws\TimestreamWrite\TimestreamWriteClient::class, 183 | 'package' => 'async-aws/timestream-write', 184 | ], 185 | 'translate' => [ 186 | 'class' => \AsyncAws\Translate\TranslateClient::class, 187 | 'package' => 'async-aws/translate', 188 | ], 189 | 'x_ray' => [ 190 | 'class' => \AsyncAws\XRay\XRayClient::class, 191 | 'package' => 'async-aws/x-ray', 192 | ], 193 | ]; 194 | } 195 | 196 | /** 197 | * @return list 198 | */ 199 | public static function getServiceNames(): array 200 | { 201 | $services = self::getAllServices(); 202 | 203 | return array_keys($services); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/InjectCasterPass.php: -------------------------------------------------------------------------------- 1 | hasDefinition('var_dumper.cloner')) { 16 | return; 17 | } 18 | 19 | $container->getDefinition('var_dumper.cloner') 20 | ->addMethodCall('addCasters', [[ 21 | Result::class => [ResultCaster::class, 'castResult'], 22 | Waiter::class => [ResultCaster::class, 'castWaiter'], 23 | ]]); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | getRootNode(); 19 | /** @phpstan-ignore-next-line */ 20 | $rootNode 21 | ->fixXmlConfig('client') 22 | ->children() 23 | ->booleanNode('register_service')->info('If set to false, no services will be created.')->defaultTrue()->end() 24 | ->scalarNode('credential_provider')->info('A service name for AsyncAws\Core\Credentials\CredentialProvider.')->defaultNull()->end() 25 | ->scalarNode('credential_provider_cache')->info('A service implementing Symfony\Contracts\Cache\CacheInterface to efficiently cache credentials.')->defaultValue('cache.app')->end() 26 | ->scalarNode('http_client')->info('A service name for Symfony\Contracts\HttpClient\HttpClientInterface.')->end() 27 | ->scalarNode('logger')->info('A service name for Psr\Log\LoggerInterface.')->end() 28 | ->arrayNode('config')->info('Default config that will be merged will all services.')->useAttributeAsKey('name')->normalizeKeys(false)->prototype('variable')->end()->end() 29 | 30 | ->arrayNode('clients') 31 | ->beforeNormalization()->always()->then(\Closure::fromCallable([$this, 'validateType']))->end() 32 | ->useAttributeAsKey('name') 33 | ->arrayPrototype() 34 | ->children() 35 | ->booleanNode('register_service')->info('If set to false, no service will be created for this AWS type.')->defaultTrue()->end() 36 | ->arrayNode('config')->info('Configuration specific to this service.')->useAttributeAsKey('name')->normalizeKeys(false)->prototype('variable')->end()->end() 37 | ->enumNode('type')->info('A valid AWS type. The service name will be used as default.')->values(AwsPackagesProvider::getServiceNames())->end() 38 | ->scalarNode('credential_provider')->info('A service name for AsyncAws\Core\Credentials\CredentialProvider.')->end() 39 | ->scalarNode('http_client')->info('A service name for Symfony\Contracts\HttpClient\HttpClientInterface.')->end() 40 | ->scalarNode('logger')->info('A service name for Psr\Log\LoggerInterface.')->end() 41 | ->end() 42 | ->end() 43 | ->end() 44 | 45 | ->arrayNode('secrets') 46 | ->info('The SSM EnvLoader configuration.') 47 | ->canBeEnabled() 48 | ->children() 49 | ->scalarNode('path')->info('Path to the parameters.')->defaultNull()->end() 50 | ->booleanNode('recursive')->info('Retrieve all parameters within a hierarchy.')->defaultValue(true)->end() 51 | ->integerNode('max_results')->info('The maximum number of items for each ssm call. Maximum value of 50.')->min(1)->max(50)->defaultNull()->end() 52 | ->scalarNode('client')->info('Name of the SSM client. When null, use the default SSM configuration.')->defaultNull()->end() 53 | ->arrayNode('cache') 54 | ->canBeEnabled() 55 | ->children() 56 | ->scalarNode('pool')->info('Identifier of the Symfony Cache Pool.')->defaultValue('cache.system')->end() 57 | ->integerNode('ttl')->info('Duration of cache in seconds')->min(0)->defaultValue(600)->end() 58 | ->end() 59 | ->end() 60 | ->end() 61 | ->end() 62 | ->end(); 63 | 64 | return $treeBuilder; 65 | } 66 | 67 | /** 68 | * @param null|array $clients 69 | * 70 | * @return array 71 | */ 72 | private static function validateType(?array $clients): array 73 | { 74 | if (null === $clients) { 75 | return []; 76 | } 77 | 78 | $awsServices = AwsPackagesProvider::getServiceNames(); 79 | foreach ($clients as $name => $config) { 80 | if (\in_array($name, $awsServices)) { 81 | if (isset($config['type']) && $name !== $config['type']) { 82 | throw new InvalidConfigurationException(\sprintf('You cannot define a service named "%s" with type "%s". That is super confusing.', $name, $config['type'])); 83 | } 84 | $clients[$name]['type'] = $name; 85 | } elseif (!isset($config['type'])) { 86 | if (!\in_array($name, $awsServices)) { 87 | throw new InvalidConfigurationException(\sprintf('The "async_aws.client.%s" does not have a type. We were unable to guess what AWS service you want. Please add "aws.service.%s.type".', $name, $name)); 88 | } 89 | 90 | $clients[$name]['type'] = $name; 91 | } 92 | } 93 | 94 | return $clients; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Secrets/CachedEnvVarLoader.php: -------------------------------------------------------------------------------- 1 | decorated = $decorated; 29 | $this->cache = $cache; 30 | $this->ttl = $ttl; 31 | } 32 | 33 | public function loadEnvVars(): array 34 | { 35 | return $this->cache->get('AsyncAws.Secrets', function (ItemInterface $item) { 36 | $item->expiresAfter($this->ttl); 37 | 38 | return $this->decorated->loadEnvVars(); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Secrets/SsmVault.php: -------------------------------------------------------------------------------- 1 | client = $client; 34 | $this->path = $path ?? '/'; 35 | $this->recursive = $recursive; 36 | $this->maxResults = $maxResults; 37 | } 38 | 39 | public function loadEnvVars(): array 40 | { 41 | $parameters = $this->client->getParametersByPath([ 42 | 'Path' => $this->path, 43 | 'Recursive' => $this->recursive, 44 | 'WithDecryption' => true, 45 | 'MaxResults' => $this->maxResults, 46 | ]); 47 | 48 | $secrets = []; 49 | $prefixLen = \strlen($this->path); 50 | /** @var Parameter $parameter */ 51 | foreach ($parameters as $parameter) { 52 | if ((null === $name = $parameter->getName()) || (null === $value = $parameter->getValue())) { 53 | continue; 54 | } 55 | $name = strtoupper(strtr(ltrim(substr($name, $prefixLen), '/'), '/', '_')); 56 | $secrets[$name] = $value; 57 | } 58 | 59 | return $secrets; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/VarDumper/ResultCaster.php: -------------------------------------------------------------------------------- 1 | $a 14 | * 15 | * @return array 16 | */ 17 | public static function castResult(Result $result, array $a, Stub $stub, bool $isNested): array 18 | { 19 | foreach (["\0AsyncAws\\Core\\Result\0httpClient", "\0AsyncAws\\Core\\Result\0response", "\0AsyncAws\\Core\\Result\0prefetchResults", Caster::PREFIX_PROTECTED . 'awsClient', Caster::PREFIX_PROTECTED . 'input'] as $k) { 20 | if (\array_key_exists($k, $a)) { 21 | unset($a[$k]); 22 | ++$stub->cut; 23 | } 24 | } 25 | 26 | return $a; 27 | } 28 | 29 | /** 30 | * @param array $a 31 | * 32 | * @return array 33 | */ 34 | public static function castWaiter(Waiter $waiter, array $a, Stub $stub, bool $isNested): array 35 | { 36 | foreach (["\0AsyncAws\\Core\\Waiter\0httpClient", "\0AsyncAws\\Core\\Waiter\0response", Caster::PREFIX_PROTECTED . 'awsClient', Caster::PREFIX_PROTECTED . 'input'] as $k) { 37 | if (\array_key_exists($k, $a)) { 38 | unset($a[$k]); 39 | ++$stub->cut; 40 | } 41 | } 42 | 43 | return $a; 44 | } 45 | } 46 | --------------------------------------------------------------------------------