diff --git a/src/Api/Serializer/JsonRpcSerializer.php b/src/Api/Serializer/JsonRpcSerializer.php index 508766e3d1..643b5c0ff8 100644 --- a/src/Api/Serializer/JsonRpcSerializer.php +++ b/src/Api/Serializer/JsonRpcSerializer.php @@ -3,8 +3,8 @@ use Aws\Api\Service; use Aws\CommandInterface; -use Aws\EndpointV2\EndpointProviderV2; use Aws\EndpointV2\EndpointV2SerializerTrait; +use Aws\EndpointV2\Ruleset\RulesetEndpoint; use GuzzleHttp\Psr7\Request; use Psr\Http\Message\RequestInterface; @@ -56,8 +56,7 @@ public function __construct( */ public function __invoke( CommandInterface $command, - $endpointProvider = null, - $clientArgs = null + $endpoint = null ) { $operationName = $command->getName(); @@ -68,15 +67,8 @@ public function __invoke( 'Content-Type' => $this->contentType ]; - if ($endpointProvider instanceof EndpointProviderV2) { - $this->setRequestOptions( - $endpointProvider, - $command, - $operation, - $commandArgs, - $clientArgs, - $headers - ); + if ($endpoint instanceof RulesetEndpoint) { + $this->setEndpointV2RequestOptions($endpoint, $headers); } return new Request( diff --git a/src/Api/Serializer/QuerySerializer.php b/src/Api/Serializer/QuerySerializer.php index a565d50773..7d60661af9 100644 --- a/src/Api/Serializer/QuerySerializer.php +++ b/src/Api/Serializer/QuerySerializer.php @@ -5,6 +5,7 @@ use Aws\CommandInterface; use Aws\EndpointV2\EndpointProviderV2; use Aws\EndpointV2\EndpointV2SerializerTrait; +use Aws\EndpointV2\Ruleset\RulesetEndpoint; use GuzzleHttp\Psr7\Request; use Psr\Http\Message\RequestInterface; @@ -42,8 +43,7 @@ public function __construct( */ public function __invoke( CommandInterface $command, - $endpointProvider = null, - $clientArgs = null + $endpoint = null ) { $operation = $this->api->getOperation($command->getName()); @@ -67,15 +67,8 @@ public function __invoke( 'Content-Type' => 'application/x-www-form-urlencoded' ]; - if ($endpointProvider instanceof EndpointProviderV2) { - $this->setRequestOptions( - $endpointProvider, - $command, - $operation, - $commandArgs, - $clientArgs, - $headers - ); + if ($endpoint instanceof RulesetEndpoint) { + $this->setEndpointV2RequestOptions($endpoint, $headers); } return new Request( diff --git a/src/Api/Serializer/RestSerializer.php b/src/Api/Serializer/RestSerializer.php index dbd925ad15..4eb86467a9 100644 --- a/src/Api/Serializer/RestSerializer.php +++ b/src/Api/Serializer/RestSerializer.php @@ -10,6 +10,7 @@ use Aws\CommandInterface; use Aws\EndpointV2\EndpointProviderV2; use Aws\EndpointV2\EndpointV2SerializerTrait; +use Aws\EndpointV2\Ruleset\RulesetEndpoint; use GuzzleHttp\Psr7; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Uri; @@ -49,33 +50,25 @@ public function __construct(Service $api, $endpoint) */ public function __invoke( CommandInterface $command, - $endpointProvider = null, - $clientArgs = null + $endpoint = null ) { $operation = $this->api->getOperation($command->getName()); $commandArgs = $command->toArray(); $opts = $this->serialize($operation, $commandArgs); - $headers = isset($opts['headers']) ? $opts['headers'] : []; - - if ($endpointProvider instanceof EndpointProviderV2) { - $this->setRequestOptions( - $endpointProvider, - $command, - $operation, - $commandArgs, - $clientArgs, - $headers - ); - $this->endpoint = new Uri($this->endpoint); + $headers = $opts['headers'] ?? []; + + if ($endpoint instanceof RulesetEndpoint) { + $this->setEndpointV2RequestOptions($endpoint, $headers); } + $uri = $this->buildEndpoint($operation, $commandArgs, $opts); return new Request( $operation['http']['method'], $uri, $headers, - isset($opts['body']) ? $opts['body'] : null + $opts['body'] ?? null ); } diff --git a/src/AwsClient.php b/src/AwsClient.php index f372e538e1..7cd70a5e2a 100644 --- a/src/AwsClient.php +++ b/src/AwsClient.php @@ -6,6 +6,7 @@ use Aws\Api\Service; use Aws\EndpointDiscovery\EndpointDiscoveryMiddleware; use Aws\EndpointV2\EndpointProviderV2; +use Aws\EndpointV2\EndpointV2Middleware; use Aws\Exception\AwsException; use Aws\Signature\SignatureProvider; use GuzzleHttp\Psr7\Uri; @@ -240,7 +241,9 @@ public function __construct(array $args) $this->loadAliases(); $this->addStreamRequestPayload(); $this->addRecursionDetection(); - $this->addRequestBuilder(); + if ($this->isUseEndpointV2()) { + $this->addEndpointV2Middleware(); + } if (!is_null($this->api->getMetadata('awsQueryCompatible'))) { $this->addQueryCompatibleInputMiddleware($this->api); @@ -512,24 +515,18 @@ private function addRecursionDetection() ); } - /** - * Adds the `builder` middleware such that a client's endpoint - * provider and endpoint resolution arguments can be passed. - */ - private function addRequestBuilder() + private function addEndpointV2Middleware() { - $handlerList = $this->getHandlerList(); - $serializer = $this->serializer; - $endpointProvider = $this->endpointProvider; + $list = $this->getHandlerList(); $endpointArgs = $this->getEndpointProviderArgs(); - $handlerList->prependBuild( - Middleware::requestBuilder( - $serializer, - $endpointProvider, + $list->prependBuild( + EndpointV2Middleware::wrap( + $this->endpointProvider, + $this->getApi(), $endpointArgs ), - 'builderV2' + 'endpointV2_middleware' ); } diff --git a/src/ClientResolver.php b/src/ClientResolver.php index 69f4439d91..2414f0aea2 100644 --- a/src/ClientResolver.php +++ b/src/ClientResolver.php @@ -153,6 +153,7 @@ class ClientResolver ], 'serializer' => [ 'default' => [__CLASS__, '_default_serializer'], + 'fn' => [__CLASS__, '_apply_serializer'], 'internal' => true, 'type' => 'value', 'valid' => ['callable'], @@ -834,6 +835,11 @@ public static function _default_use_dual_stack_endpoint(array &$args) { return UseDualStackConfigProvider::defaultProvider($args); } + public static function _apply_serializer($value, array &$args, HandlerList $list) + { + $list->prependBuild(Middleware::requestBuilder($value), 'builder'); + } + public static function _apply_debug($value, array &$args, HandlerList $list) { if ($value !== false) { diff --git a/src/EndpointV2/EndpointV2Middleware.php b/src/EndpointV2/EndpointV2Middleware.php new file mode 100644 index 0000000000..beddfdc656 --- /dev/null +++ b/src/EndpointV2/EndpointV2Middleware.php @@ -0,0 +1,292 @@ +nextHandler = $nextHandler; + $this->endpointProvider = $endpointProvider; + $this->api = $api; + $this->clientArgs = $args; + } + + /** + * @param CommandInterface $command + * + * @return Promise + */ + public function __invoke(CommandInterface $command) + { + $nextHandler = $this->nextHandler; + $operation = $this->api->getOperation($command->getName()); + $commandArgs = $command->toArray(); + + $providerArgs = $this->resolveArgs($commandArgs, $operation); + $endpoint = $this->endpointProvider->resolveEndpoint($providerArgs); + + if (!empty($endpoint->getProperties()['authSchemes'])) { + $this->applyAuthScheme( + $endpoint->getProperties()['authSchemes'], + $command + ); + } + + return $nextHandler($command, $endpoint); + } + + /** + * Resolves client, context params, static context params and endpoint provider + * arguments provided at the command level. + * + * @param array $commandArgs + * @param Operation $operation + * + * @return array + */ + private function resolveArgs(array $commandArgs, Operation $operation) : array + { + $rulesetParams = $this->endpointProvider->getRuleset()->getParameters(); + $endpointCommandArgs = $this->filterEndpointCommandArgs( + $rulesetParams, + $commandArgs + ); + $staticContextParams = $this->bindStaticContextParams( + $operation->getStaticContextParams() + ); + $contextParams = $this->bindContextParams( + $commandArgs, $operation->getContextParams() + ); + + return array_merge( + $this->clientArgs, + $contextParams, + $staticContextParams, + $endpointCommandArgs + ); + } + + /** + * Compares Ruleset parameters against Command arguments + * to create a mapping of arguments to pass into the + * endpoint provider for endpoint resolution. + * + * @param array $rulesetParams + * @param array $commandArgs + * @return array + */ + private function filterEndpointCommandArgs( + array $rulesetParams, + array $commandArgs + ) : array + { + $endpointMiddlewareOpts = [ + '@use_dual_stack_endpoint' => 'UseDualStack', + '@use_accelerate_endpoint' => 'Accelerate', + '@use_path_style_endpoint' => 'ForcePathStyle' + ]; + + $filteredArgs = []; + + foreach($rulesetParams as $name => $value) { + if (isset($commandArgs[$name])) { + if (!empty($value->getBuiltIn())) { + continue; + } + $filteredArgs[$name] = $commandArgs[$name]; + } + } + + if ($this->api->getServiceName() === 's3') { + foreach($endpointMiddlewareOpts as $optionName => $newValue) { + if (isset($commandArgs[$optionName])) { + $filteredArgs[$newValue] = $commandArgs[$optionName]; + } + } + } + + return $filteredArgs; + } + + /** + * Binds static context params to their corresponding values. + * + * @param $staticContextParams + * + * @return array + */ + private function bindStaticContextParams($staticContextParams) : array + { + $scopedParams = []; + + forEach($staticContextParams as $paramName => $paramValue) { + $scopedParams[$paramName] = $paramValue['value']; + } + return $scopedParams; + } + + /** + * Binds context params to their corresponding values found in + * command arguments. + * + * @param array $commandArgs + * @param array $contextParams + * + * @return array + */ + private function bindContextParams( + array $commandArgs, + array $contextParams + ) : array + { + $scopedParams = []; + + foreach($contextParams as $name => $spec) { + if (isset($commandArgs[$spec['shape']])) { + $scopedParams[$name] = $commandArgs[$spec['shape']]; + } + } + return $scopedParams; + } + + /** + * Applies resolved auth schemes to the command object. + * + * @param $authSchemes + * @param $command + * + * @return void + */ + private function applyAuthScheme( + array $authSchemes, + CommandInterface $command + ) : void + { + $authScheme = $this->resolveAuthScheme($authSchemes); + $command->setAuthSchemes($authScheme); + } + + /** + * Returns the first compatible auth scheme in an endpoint object's + * auth schemes. + * + * @param array $authSchemes + * + * @return array + */ + private function resolveAuthScheme(array $authSchemes) : array + { + $validAuthSchemes = ['sigv4', 'sigv4a', 'none', 'bearer']; + $invalidAuthSchemes = []; + + foreach($authSchemes as $authScheme) { + if (in_array($authScheme['name'], $validAuthSchemes)) { + return $this->normalizeAuthScheme($authScheme); + } else { + $invalidAuthSchemes[] = "`{$authScheme['name']}`"; + } + } + + $invalidAuthSchemesString = implode(', ', $invalidAuthSchemes); + $validAuthSchemesString = '`' . implode('`, `', $validAuthSchemes) . '`'; + throw new \InvalidArgumentException( + "This operation requests {$invalidAuthSchemesString}" + . " auth schemes, but the client only supports {$validAuthSchemesString}." + ); + } + + /** + * Normalizes an auth scheme's name, signing region or signing region set + * to the auth keys recognized by the SDK. + * + * @param array $authScheme + * @return array + */ + private function normalizeAuthScheme(array $authScheme) : array + { + /* + sigv4a will contain a regionSet property. which is guaranteed to be `*` + for now. The SigV4 class handles this automatically for now. It seems + complexity will be added here in the future. + */ + $normalizedAuthScheme = []; + + if (isset($authScheme['disableDoubleEncoding']) + && $authScheme['disableDoubleEncoding'] === true + && $authScheme['name'] !== 'sigv4a' + ) { + $normalizedAuthScheme['version'] = 's3v4'; + } elseif ($authScheme['name'] === 'none') { + $normalizedAuthScheme['version'] = 'anonymous'; + } + else { + $normalizedAuthScheme['version'] = str_replace( + 'sig', '', $authScheme['name'] + ); + } + + $normalizedAuthScheme['name'] = $authScheme['signingName'] ?? null; + $normalizedAuthScheme['region'] = $authScheme['signingRegion'] ?? null; + $normalizedAuthScheme['signingRegionSet'] = $authScheme['signingRegionSet'] ?? null; + + return $normalizedAuthScheme; + } +} diff --git a/src/EndpointV2/EndpointV2SerializerTrait.php b/src/EndpointV2/EndpointV2SerializerTrait.php index 922ec189ec..3bcef17ac8 100644 --- a/src/EndpointV2/EndpointV2SerializerTrait.php +++ b/src/EndpointV2/EndpointV2SerializerTrait.php @@ -3,6 +3,7 @@ namespace Aws\EndpointV2; use Aws\Api\Serializer\RestSerializer; +use Aws\EndpointV2\Ruleset\RulesetEndpoint; use GuzzleHttp\Psr7\Uri; /** @@ -14,144 +15,35 @@ trait EndpointV2SerializerTrait { /** - * Merges endpoint resolution arguments passed from the client - * and command and attempts to resolve an endpoint. Headers and - * auth schemes may be returned in a resolved endpoint object. - * A resolved endpoint uri and headers will be applied to the request. - * Auth schemes are applied to the command and compared against the default - * auth scheme at signing. + * Applies a resolved endpoint, headers and any custom HTTP schemes provided + * in client configuration to options which are applied to the serialized request. * - * @internal + * @param $endpoint + * @param $headers + * + * @return void */ - private function setRequestOptions( - $endpointProvider, - $command, - $operation, - $commandArgs, - $clientArgs, - &$headers - ) + private function setEndpointV2RequestOptions( + RulesetEndpoint $endpoint, + array &$headers + ): void { - $providerArgs = $this->resolveProviderArgs( - $endpointProvider, - $operation, - $commandArgs, - $clientArgs - ); - $endpoint = $endpointProvider->resolveEndpoint($providerArgs); + $this->applyHeaders($endpoint, $headers); $resolvedUrl = $endpoint->getUrl(); - $this->applyScheme($resolvedUrl); - $this->endpoint = $resolvedUrl; - $this->applyAuthSchemeToCommand($endpoint, $command); - $this->applyHeaders($endpoint, $headers); - } - - private function resolveProviderArgs( - $endpointProvider, - $operation, - $commandArgs, - $clientArgs - ) - { - $rulesetParams = $endpointProvider->getRuleset()->getParameters(); - $endpointCommandArgs = $this->filterEndpointCommandArgs( - $rulesetParams, - $commandArgs - ); - $staticContextParams = $this->bindStaticContextParams( - $operation->getStaticContextParams() - ); - $contextParams = $this->bindContextParams( - $commandArgs, $operation->getContextParams() - ); - $providerArgs = $this->normalizeEndpointProviderArgs( - $endpointCommandArgs, - $clientArgs, - $contextParams, - $staticContextParams - ); - - return $providerArgs; + $this->endpoint = $this instanceof RestSerializer + ? new Uri($resolvedUrl) + : $resolvedUrl; } /** - * Merges endpoint provider arguments from different sources. - * Client built-ins are superseded by client context params. - * Client context params are superseded by context params on - * an input member's shape. Context params are superseded by - * static context params. The result of this combination is then - * merged with any appropriate arguments from the command. + * Combines modeled headers and headers resolved from an endpoint object. + * + * @param $endpoint + * @param $headers + * @return void */ - private function normalizeEndpointProviderArgs( - $endpointCommandArgs, - $clientArgs, - $contextParams, - $staticContextParams - ) - { - $commandContextParams = array_merge($contextParams, $staticContextParams); - $combinedEndpointArgs = array_merge($clientArgs, $commandContextParams); - - return array_merge($combinedEndpointArgs, $endpointCommandArgs); - } - - private function bindContextParams($commandArgs, $contextParams) - { - $scopedParams = []; - - foreach($contextParams as $name => $spec) { - if (isset($commandArgs[$spec['shape']])) { - $scopedParams[$name] = $commandArgs[$spec['shape']]; - } - } - return $scopedParams; - } - - private function bindStaticContextParams($staticContextParams) - { - $scopedParams = []; - - forEach($staticContextParams as $paramName => $paramValue) { - $scopedParams[$paramName] = $paramValue['value']; - } - return $scopedParams; - } - - private function filterEndpointCommandArgs( - $rulesetParams, - $commandArgs - ) - { - $endpointMiddlewareOpts = [ - '@use_dual_stack_endpoint' => 'UseDualStack', - '@use_accelerate_endpoint' => 'Accelerate', - '@use_path_style_endpoint' => 'ForcePathStyle' - ]; - - $filteredArgs = []; - - foreach($rulesetParams as $name => $value) { - if (isset($commandArgs[$name])) { - if (!empty($value->getBuiltIn())) { - continue; - } - $filteredArgs[$name] = $commandArgs[$name]; - } - } - - if ($this->api->getServiceName() === 's3') { - foreach($endpointMiddlewareOpts as $optionName => $newValue) { - if (isset($commandArgs[$optionName])) { - $filteredArgs[$newValue] = $commandArgs[$optionName]; - } - } - } - - return $filteredArgs; - } - - private function applyHeaders($endpoint, &$headers) + private function applyHeaders(RulesetEndpoint $endpoint, array &$headers): void { if (!is_null($endpoint->getHeaders())) { $headers = array_merge( @@ -161,71 +53,13 @@ private function applyHeaders($endpoint, &$headers) } } - private function applyAuthSchemeToCommand($endpoint, $command) - { - if (isset($endpoint->getProperties()['authSchemes'])) { - $authScheme = $this->selectAuthScheme( - $endpoint->getProperties()['authSchemes'] - ); - $command->setAuthSchemes($authScheme); - } - } - - private function selectAuthScheme($authSchemes) - { - $validAuthSchemes = ['sigv4', 'sigv4a', 'none', 'bearer']; - $invalidAuthSchemes = []; - - foreach($authSchemes as $authScheme) { - if (in_array($authScheme['name'], $validAuthSchemes)) { - return $this->normalizeAuthScheme($authScheme); - } else { - $invalidAuthSchemes[] = "`{$authScheme['name']}`"; - } - } - - $invalidAuthSchemesString = implode(', ', $invalidAuthSchemes); - $validAuthSchemesString = '`' . implode('`, `', $validAuthSchemes) . '`'; - throw new \InvalidArgumentException( - "This operation requests {$invalidAuthSchemesString}" - . " auth schemes, but the client only supports {$validAuthSchemesString}." - ); - } - - private function normalizeAuthScheme($authScheme) - { - /* - sigv4a will contain a regionSet property. which is guaranteed to be `*` - for now. The SigV4 class handles this automatically for now. It seems - complexity will be added here in the future. - */ - $normalizedAuthScheme = []; - - if (isset($authScheme['disableDoubleEncoding']) - && $authScheme['disableDoubleEncoding'] === true - && $authScheme['name'] !== 'sigv4a' - ) { - $normalizedAuthScheme['version'] = 's3v4'; - } elseif ($authScheme['name'] === 'none') { - $normalizedAuthScheme['version'] = 'anonymous'; - } - else { - $normalizedAuthScheme['version'] = str_replace( - 'sig', '', $authScheme['name'] - ); - } - - $normalizedAuthScheme['name'] = isset($authScheme['signingName']) ? - $authScheme['signingName'] : null; - $normalizedAuthScheme['region'] = isset($authScheme['signingRegion']) ? - $authScheme['signingRegion'] : null; - $normalizedAuthScheme['signingRegionSet'] = isset($authScheme['signingRegionSet']) ? - $authScheme['signingRegionSet'] : null; - - return $normalizedAuthScheme; - } - - private function applyScheme(&$resolvedUrl) + /** + * Applies custom HTTP schemes provided in client configuration. + * + * @param $resolvedUrl + * @return void + */ + private function applyScheme(string &$resolvedUrl): void { $resolvedEndpointScheme = parse_url($resolvedUrl, PHP_URL_SCHEME); $scheme = $this->endpoint instanceof Uri diff --git a/src/EndpointV2/Rule/AbstractRule.php b/src/EndpointV2/Rule/AbstractRule.php index 94da12d01b..adedb4e199 100644 --- a/src/EndpointV2/Rule/AbstractRule.php +++ b/src/EndpointV2/Rule/AbstractRule.php @@ -8,7 +8,7 @@ * A rule within a rule set. All rules contain a conditions property, * which can be empty, and documentation about the rule. */ -abstract Class AbstractRule +abstract class AbstractRule { private $conditions; private $documentation; diff --git a/src/EndpointV2/Rule/EndpointRule.php b/src/EndpointV2/Rule/EndpointRule.php index b6b276d70c..c3a0e9b7e5 100644 --- a/src/EndpointV2/Rule/EndpointRule.php +++ b/src/EndpointV2/Rule/EndpointRule.php @@ -5,7 +5,7 @@ use Aws\EndpointV2\Ruleset\RulesetStandardLibrary; use Aws\EndpointV2\Ruleset\RulesetEndpoint; -Class EndpointRule extends AbstractRule +class EndpointRule extends AbstractRule { /** @var array */ private $endpoint; diff --git a/src/EndpointV2/Ruleset/Ruleset.php b/src/EndpointV2/Ruleset/Ruleset.php index aa4d26e07f..21828c1951 100644 --- a/src/EndpointV2/Ruleset/Ruleset.php +++ b/src/EndpointV2/Ruleset/Ruleset.php @@ -8,7 +8,7 @@ * A collection of rules, parameter definitions and a class of helper functions * used to resolve either an endpoint or an error. */ -Class Ruleset +class Ruleset { /** @var string */ private $version; diff --git a/src/Middleware.php b/src/Middleware.php index 59f4bf18a2..bc5504458a 100644 --- a/src/Middleware.php +++ b/src/Middleware.php @@ -100,15 +100,11 @@ public static function validation(Service $api, Validator $validator = null) * @param array $providerArgs * @return callable */ - public static function requestBuilder( - $serializer, - $endpointProvider = null, - array $providerArgs = null - ) + public static function requestBuilder($serializer) { - return function (callable $handler) use ($serializer, $endpointProvider, $providerArgs) { - return function (CommandInterface $command) use ($serializer, $handler, $endpointProvider, $providerArgs) { - return $handler($command, $serializer($command, $endpointProvider, $providerArgs)); + return function (callable $handler) use ($serializer) { + return function (CommandInterface $command, $endpoint = null) use ($serializer, $handler) { + return $handler($command, $serializer($command, $endpoint)); }; }; } diff --git a/src/S3/S3Client.php b/src/S3/S3Client.php index b82bbc56bc..12d6201fe3 100644 --- a/src/S3/S3Client.php +++ b/src/S3/S3Client.php @@ -419,7 +419,7 @@ public function __construct(array $args) $stack->appendInit($this->getHeadObjectMiddleware(), 's3.head_object'); if ($this->isUseEndpointV2()) { $this->processEndpointV2Model(); - $stack->after('builderV2', + $stack->after('builder', 's3.check_empty_path_with_query', $this->getEmptyPathWithQuery()); } diff --git a/src/S3/StreamWrapper.php b/src/S3/StreamWrapper.php index a70e5cd52d..955b39dfa7 100644 --- a/src/S3/StreamWrapper.php +++ b/src/S3/StreamWrapper.php @@ -245,6 +245,7 @@ public function stream_stat() $stat = $this->getStatTemplate(); $stat[7] = $stat['size'] = $this->getSize(); $stat[2] = $stat['mode'] = $this->mode; + $stat['custom_attribute'] = 'custom_value'; return $stat; } diff --git a/tests/Api/Serializer/JsonRpcSerializerTest.php b/tests/Api/Serializer/JsonRpcSerializerTest.php index 81db7560b5..23c6f111aa 100644 --- a/tests/Api/Serializer/JsonRpcSerializerTest.php +++ b/tests/Api/Serializer/JsonRpcSerializerTest.php @@ -6,6 +6,7 @@ use Aws\Api\Service; use Aws\EndpointV2\EndpointDefinitionProvider; use Aws\EndpointV2\EndpointProviderV2; +use Aws\EndpointV2\Ruleset\RulesetEndpoint; use Aws\Test\UsesServiceTrait; use PHPUnit\Framework\TestCase; @@ -82,14 +83,8 @@ function () {} $q = new JsonRpcSerializer($service, 'http://foo.com'); $cmd = new Command('foo', ['baz' => []]); - $endpointProvider = new EndpointProviderV2( - json_decode( - file_get_contents(__DIR__ . '/../../EndpointV2/valid-rules/aws-region.json'), - true - ), - EndpointDefinitionProvider::getPartitions() - ); - $request = $q($cmd, $endpointProvider, ['Region' => 'us-east-1']); - $this->assertSame('http://us-east-1.amazonaws.com', (string) $request->getUri()); + $endpoint = new RulesetEndpoint('https://foo.com'); + $request = $q($cmd, $endpoint); + $this->assertSame('http://foo.com', (string) $request->getUri()); } } diff --git a/tests/Api/Serializer/QuerySerializerTest.php b/tests/Api/Serializer/QuerySerializerTest.php index 263aa43e93..3edecc1edc 100644 --- a/tests/Api/Serializer/QuerySerializerTest.php +++ b/tests/Api/Serializer/QuerySerializerTest.php @@ -6,6 +6,7 @@ use Aws\Command; use Aws\EndpointV2\EndpointDefinitionProvider; use Aws\EndpointV2\EndpointProviderV2; +use Aws\EndpointV2\Ruleset\RulesetEndpoint; use Aws\Test\UsesServiceTrait; use PHPUnit\Framework\TestCase; @@ -79,14 +80,8 @@ function () {} $q = new QuerySerializer($service, 'http://foo.com'); $cmd = new Command('foo', ['baz' => []]); - $endpointProvider = new EndpointProviderV2( - json_decode( - file_get_contents(__DIR__ . '/../../EndpointV2/valid-rules/aws-region.json'), - true - ), - EndpointDefinitionProvider::getPartitions() - ); - $request = $q($cmd, $endpointProvider, ['Region' => 'us-east-1']); - $this->assertSame('http://us-east-1.amazonaws.com', (string) $request->getUri()); + $endpoint = new RulesetEndpoint('https://foo.com'); + $request = $q($cmd, $endpoint); + $this->assertSame('http://foo.com', (string) $request->getUri()); } } diff --git a/tests/Api/Serializer/RestJsonSerializerTest.php b/tests/Api/Serializer/RestJsonSerializerTest.php index 316ea20115..3f6ea6c33f 100644 --- a/tests/Api/Serializer/RestJsonSerializerTest.php +++ b/tests/Api/Serializer/RestJsonSerializerTest.php @@ -6,6 +6,7 @@ use Aws\Api\Serializer\RestJsonSerializer; use Aws\EndpointV2\EndpointDefinitionProvider; use Aws\EndpointV2\EndpointProviderV2; +use Aws\EndpointV2\Ruleset\RulesetEndpoint; use Aws\Test\UsesServiceTrait; use Yoast\PHPUnitPolyfills\TestCases\TestCase; @@ -347,15 +348,9 @@ public function testDoesNotOverrideScheme() { $serializer = new RestJsonSerializer($this->getTestService(), 'http://foo.com'); $cmd = new Command('foo', ['baz' => 'bar']); - $endpointProvider = new EndpointProviderV2( - json_decode( - file_get_contents(__DIR__ . '/../../EndpointV2/valid-rules/aws-region.json'), - true - ), - EndpointDefinitionProvider::getPartitions() - ); - $request = $serializer($cmd, $endpointProvider, ['Region' => 'us-east-1']); - $this->assertSame('http://us-east-1.amazonaws.com/', (string) $request->getUri()); + $endpoint = new RulesetEndpoint('https://foo.com'); + $request = $serializer($cmd, $endpoint); + $this->assertSame('http://foo.com/', (string) $request->getUri()); } } diff --git a/tests/Api/Serializer/RestXmlSerializerTest.php b/tests/Api/Serializer/RestXmlSerializerTest.php index 7ed0bf4ed3..6908b5762a 100644 --- a/tests/Api/Serializer/RestXmlSerializerTest.php +++ b/tests/Api/Serializer/RestXmlSerializerTest.php @@ -6,6 +6,7 @@ use Aws\Command; use Aws\EndpointV2\EndpointDefinitionProvider; use Aws\EndpointV2\EndpointProviderV2; +use Aws\EndpointV2\Ruleset\RulesetEndpoint; use Aws\Test\UsesServiceTrait; use Yoast\PHPUnitPolyfills\TestCases\TestCase; @@ -111,14 +112,8 @@ public function testDoesNotOverrideScheme() $client = $this->getTestClient('s3', ['region' => 'us-east-1']); $serializer = new RestXmlSerializer($client->getApi(), 'http://test.com'); $cmd = new Command('HeadObject', ['baz' => []]); - $endpointProvider = new EndpointProviderV2( - json_decode( - file_get_contents(__DIR__ . '/../../EndpointV2/valid-rules/aws-region.json'), - true - ), - EndpointDefinitionProvider::getPartitions() - ); - $request = $serializer($cmd, $endpointProvider, ['Region' => 'us-east-1']); - $this->assertSame('http://us-east-1.amazonaws.com/', (string) $request->getUri()); + $endpoint = new RulesetEndpoint('https://foo.com'); + $request = $serializer($cmd, $endpoint); + $this->assertSame('http://foo.com/', (string) $request->getUri()); } } diff --git a/tests/EndpointV2/EndpointV2MiddlewareTest.php b/tests/EndpointV2/EndpointV2MiddlewareTest.php new file mode 100644 index 0000000000..bc36b9bed1 --- /dev/null +++ b/tests/EndpointV2/EndpointV2MiddlewareTest.php @@ -0,0 +1,206 @@ +assertInstanceOf(RulesetEndpoint::class, $endpoint); + $this->assertEquals($expectedUri, $endpoint->getUrl()); + + if (isset($endpoint->getProperties()['authSchemes'])) { + self::assertNotEmpty($command->getAuthSchemes()); + } + }; + + $client = $this->getTestClient($service, $clientArgs); + $api = $client->getApi(); + $endpointProvider = $client->getEndpointProvider(); + $endpointArgs = $client->getEndpointProviderArgs(); + $command = $client->getCommand($commandName, $commandArgs); + + $mw = new EndpointV2Middleware($nextHandler, $endpointProvider, $api, $endpointArgs); + + $mw($command); + } + + public function providedSuccessCases() + { + return [ + [ + 's3', + ['region' => 'us-west-2'], + 'listObjectsV2', + ['Bucket' => 'foo-bucket'], + 'https://foo-bucket.s3.us-west-2.amazonaws.com' + ], + [ + 's3', + ['region' => 'us-west-2', 'use_path_style_endpoint' => true], + 'listObjectsV2', + ['Bucket' => 'foo-bucket'], + 'https://s3.us-west-2.amazonaws.com/foo-bucket' + ], + [ + 's3', + ['region' => 'us-west-2', 'use_accelerate_endpoint' => true], + 'listObjectsV2', + ['Bucket' => 'foo-bucket'], + 'https://foo-bucket.s3-accelerate.amazonaws.com' + ], + [ + 's3', + ['region' => 'us-west-2', 'use_accelerate_endpoint' => true], + 'listObjectsV2', + ['Bucket' => 'foo-bucket'], + 'https://foo-bucket.s3-accelerate.amazonaws.com' + ], + [ + 'sts', + ['region' => 'us-west-2'], + 'getCallerIdentity', + [], + 'https://sts.amazonaws.com' + ], + [ + 'sts', + ['region' => 'us-west-2', 'sts_regional_endpoints' => 'regional'], + 'getCallerIdentity', + [], + 'https://sts.us-west-2.amazonaws.com' + ], + ]; + } + + public function testInvalidAuthSchemeThrows() { + $mockedEndpointProvider = $this->getMockBuilder(EndpointProviderV2::class) + ->setMethods(['resolveEndpoint']) + ->disableOriginalConstructor() + ->getMock(); + + $mockedEndpoint = $this->getMockBuilder(RulesetEndpoint::class) + ->setMethods(['getProperties']) + ->disableOriginalConstructor() + ->getMock(); + + $mockedEndpoint->method('getProperties') + ->willReturn(['authSchemes' => [ ['name' => 'invalidAuthScheme'] ]]); + + $mockedEndpointProvider->method('resolveEndpoint') + ->willReturn($mockedEndpoint); + + $middleware = new EndpointV2Middleware( + function ($command, $endpoint) {}, + $mockedEndpointProvider, + $this->getMockBuilder(Service::class) + ->disableOriginalConstructor() + ->getMock(), + [] + ); + + $reflection = new ReflectionClass(EndpointV2Middleware::class); + $method = $reflection->getMethod('resolveAuthScheme'); + $method->setAccessible(true); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage( + "This operation requests `invalidAuthScheme` auth schemes, but the client only supports" + ); + + $method->invoke($middleware, [['name' => 'invalidAuthScheme']]); + } + + /** + * @dataProvider invalidInitializationProvider + */ + public function testInitializationWithInvalidParameters( + $nextHandler, + $endpointProvider, + $api, + $args + ) + { + $this->expectException(\TypeError::class); + new EndpointV2Middleware($nextHandler, $endpointProvider, $api, $args); + } + + public function invalidInitializationProvider() + { + return [ + 'Invalid nextHandler' => [ + 'not_a_callable', + $this->getMockBuilder(EndpointProviderV2::class) + ->disableOriginalConstructor() + ->getMock(), + $this->getMockBuilder(Service::class) + ->disableOriginalConstructor() + ->getMock(), + [] + ], + 'Invalid endpointProvider' => [ + function ($command, $endpoint) {}, + 'not_an_endpoint_provider', + $this->getMockBuilder(Service::class) + ->disableOriginalConstructor() + ->getMock(), + [] + ], + 'Invalid api' => [ + function ($command, $endpoint) {}, + $this->getMockBuilder(EndpointProviderV2::class) + ->disableOriginalConstructor() + ->getMock(), + 'not_a_service', + [] + ], + 'Invalid array' => [ + function ($command, $endpoint) {}, + $this->getMockBuilder(EndpointProviderV2::class) + ->disableOriginalConstructor() + ->getMock(), + $this->getMockBuilder(Service::class) + ->disableOriginalConstructor() + ->getMock(), + 'not_an_array' + ], + ]; + } + + public function testBadParametersOnInvocation() { + $this->expectException(\TypeError::class); + + $nextHandler = function ($command, $endpoint) {}; + $endpointProvider = $this->getMockBuilder(EndpointProviderV2::class)->getMock(); + $api = $this->getMockBuilder(Service::class)->getMock(); + $args = []; + + $middleware = new EndpointV2Middleware($nextHandler, $endpointProvider, $api, $args); + $middleware('not_a_command'); + } +}