Skip to content

Commit

Permalink
Make sure we are always throwing an exception from Core (async-aws#841)
Browse files Browse the repository at this point in the history
* Make sure we are always throwing an exception from Core

* Make sure we generate classes to throw correct exception

* ./generate --all

* Shorten name

* Do cover integrations

* Added changelog
  • Loading branch information
Nyholm authored Nov 3, 2020
1 parent a5d157b commit 5fafb09
Show file tree
Hide file tree
Showing 43 changed files with 219 additions and 122 deletions.
6 changes: 4 additions & 2 deletions src/CodeGenerator/src/Generator/PaginationGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use AsyncAws\CodeGenerator\Generator\CodeGenerator\TypeGenerator;
use AsyncAws\CodeGenerator\Generator\Naming\NamespaceRegistry;
use AsyncAws\CodeGenerator\Generator\PhpGenerator\ClassFactory;
use AsyncAws\Core\Exception\InvalidArgument;
use AsyncAws\Core\Exception\LogicException;
use Nette\PhpGenerator\Parameter;
use Nette\PhpGenerator\PhpNamespace;
Expand Down Expand Up @@ -208,14 +209,15 @@ private function generateOutputPaginationLoader(string $iterator, Pagination $pa
$namespace->addUse($inputClass->getFqdn());
$clientClass = $this->namespaceRegistry->getClient($operation->getService());
$namespace->addUse($clientClass->getFqdn());
$namespace->addUse(InvalidArgument::class);

return strtr('
$client = $this->awsClient;
if (!$client instanceOf CLIENT_CLASSNAME) {
throw new \InvalidArgumentException(\'missing client injected in paginated result\');
throw new InvalidArgument(\'missing client injected in paginated result\');
}
if (!$this->input instanceOf INPUT_CLASSNAME) {
throw new \InvalidArgumentException(\'missing last request injected in paginated result\');
throw new InvalidArgument(\'missing last request injected in paginated result\');
}
$input = clone $this->input;
$page = $this;
Expand Down
6 changes: 4 additions & 2 deletions src/CodeGenerator/src/Generator/WaiterGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use AsyncAws\CodeGenerator\Generator\Naming\NamespaceRegistry;
use AsyncAws\CodeGenerator\Generator\PhpGenerator\ClassFactory;
use AsyncAws\Core\Exception\Http\HttpException;
use AsyncAws\Core\Exception\InvalidArgument;
use AsyncAws\Core\Exception\RuntimeException;
use AsyncAws\Core\RequestContext;
use AsyncAws\Core\Response;
Expand Down Expand Up @@ -137,6 +138,7 @@ private function generateWaiterResult(Waiter $waiter): ClassName
$namespace->addUse($inputClass->getFqdn());
$clientClass = $this->namespaceRegistry->getClient($waiter->getOperation()->getService());
$namespace->addUse($clientClass->getFqdn());
$namespace->addUse(InvalidArgument::class);

$class->addExtend(WaiterResult::class);

Expand All @@ -145,10 +147,10 @@ private function generateWaiterResult(Waiter $waiter): ClassName
->setVisibility(ClassType::VISIBILITY_PROTECTED)
->setBody(strtr('
if (!$this->awsClient instanceOf CLIENT_CLASSNAME) {
throw new \InvalidArgumentException(\'missing client injected in waiter result\');
throw new InvalidArgument(\'missing client injected in waiter result\');
}
if (!$this->input instanceOf INPUT_CLASSNAME) {
throw new \InvalidArgumentException(\'missing last request injected in waiter result\');
throw new InvalidArgument(\'missing last request injected in waiter result\');
}
return $this->awsClient->WAITER_NAME($this->input);
Expand Down
1 change: 1 addition & 0 deletions src/Core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
### Added

- Support for Rekognition in `AwsClientFactory`
- Added exception `AsyncAws\Core\Exception\UnexpectedValue`

## 1.5.0

Expand Down
8 changes: 5 additions & 3 deletions src/Core/src/AwsError/AwsErrorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace AsyncAws\Core\AwsError;

use AsyncAws\Core\Exception\ParseResponse;
use AsyncAws\Core\Exception\RuntimeException;
use AsyncAws\Core\Exception\UnexpectedValue;
use Symfony\Contracts\HttpClient\ResponseInterface;

/**
Expand All @@ -28,7 +30,7 @@ public static function createFromContent(string $content, array $headers): AwsEr

set_error_handler(
static function ($errno, $errstr, $errfile, $errline) {
throw new \RuntimeException($errstr, $errno);
throw new RuntimeException($errstr, $errno);
}
);

Expand Down Expand Up @@ -64,7 +66,7 @@ private static function parseXml(\SimpleXMLElement $xml): AwsError
);
}

throw new \UnexpectedValueException('XML does not contains AWS Error');
throw new UnexpectedValue('XML does not contains AWS Error');
}

private static function parseJson(array $body, array $headers): AwsError
Expand All @@ -87,6 +89,6 @@ private static function parseJson(array $body, array $headers): AwsError
return new AwsError($code, $message, $type, null);
}

throw new \UnexpectedValueException('JSON does not contains AWS Error');
throw new UnexpectedValue('JSON does not contains AWS Error');
}
}
3 changes: 2 additions & 1 deletion src/Core/src/Credentials/ConfigurationProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace AsyncAws\Core\Credentials;

use AsyncAws\Core\Configuration;
use AsyncAws\Core\Exception\RuntimeException;
use AsyncAws\Core\Sts\StsClient;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
Expand Down Expand Up @@ -67,7 +68,7 @@ private function getCredentialsFromRole(Credentials $credentials, string $region

try {
if (null === $credentials = $result->getCredentials()) {
throw new \RuntimeException('The AsumeRole response does not contains credentials');
throw new RuntimeException('The AsumeRole response does not contains credentials');
}
} catch (\Exception $e) {
$this->logger->warning('Failed to get credentials from assumed role: {exception}".', ['exception' => $e]);
Expand Down
3 changes: 2 additions & 1 deletion src/Core/src/Credentials/IniFileProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace AsyncAws\Core\Credentials;

use AsyncAws\Core\Configuration;
use AsyncAws\Core\Exception\RuntimeException;
use AsyncAws\Core\Sts\StsClient;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
Expand Down Expand Up @@ -113,7 +114,7 @@ private function getCredentialsFromRole(array $profilesData, array $profileData,

try {
if (null === $credentials = $result->getCredentials()) {
throw new \RuntimeException('The AsumeRole response does not contains credentials');
throw new RuntimeException('The AsumeRole response does not contains credentials');
}
} catch (\Exception $e) {
$this->logger->warning('Failed to get credentials from assumed role in profile "{profile}: {exception}".', ['profile' => $profile, 'exception' => $e]);
Expand Down
5 changes: 3 additions & 2 deletions src/Core/src/Credentials/WebIdentityProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace AsyncAws\Core\Credentials;

use AsyncAws\Core\Configuration;
use AsyncAws\Core\Exception\RuntimeException;
use AsyncAws\Core\Sts\StsClient;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
Expand Down Expand Up @@ -89,7 +90,7 @@ private function getCredentialsFromRole(string $roleArn, string $tokenFile, ?str

try {
if (false === $token = file_get_contents($tokenFile)) {
throw new \RuntimeException('failed to read data');
throw new RuntimeException('failed to read data');
}
} catch (\Exception $e) {
$this->logger->warning('"Error reading WebIdentityTokenFile "{tokenFile}.', ['tokenFile' => $tokenFile, 'exception' => $e]);
Expand All @@ -106,7 +107,7 @@ private function getCredentialsFromRole(string $roleArn, string $tokenFile, ?str

try {
if (null === $credentials = $result->getCredentials()) {
throw new \RuntimeException('The AssumeRoleWithWebIdentity response does not contains credentials');
throw new RuntimeException('The AssumeRoleWithWebIdentity response does not contains credentials');
}
} catch (\Exception $e) {
$this->logger->warning('Failed to get credentials from assumed role: {exception}".', ['exception' => $e]);
Expand Down
9 changes: 9 additions & 0 deletions src/Core/src/Exception/UnexpectedValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace AsyncAws\Core\Exception;

class UnexpectedValue extends \UnexpectedValueException implements Exception
{
}
3 changes: 2 additions & 1 deletion src/Core/src/Test/Http/SimpleMockedResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace AsyncAws\Core\Test\Http;

use AsyncAws\Core\Exception\LogicException;
use Symfony\Component\HttpClient\Response\MockResponse;

class SimpleMockedResponse extends MockResponse
Expand Down Expand Up @@ -54,7 +55,7 @@ public function toArray(bool $throw = true): array

public function cancel(): void
{
throw new \LogicException('Not implemented');
throw new LogicException('Not implemented');
}

public function getInfo(string $type = null)
Expand Down
17 changes: 9 additions & 8 deletions src/Core/src/Test/ResultMockFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace AsyncAws\Core\Test;

use AsyncAws\Core\Exception\LogicException;
use AsyncAws\Core\Response;
use AsyncAws\Core\Result;
use AsyncAws\Core\Test\Http\SimpleMockedResponse;
Expand Down Expand Up @@ -39,7 +40,7 @@ public static function createFailing(
if (Result::class !== $class) {
$parent = get_parent_class($class);
if (false === $parent || Result::class !== $parent) {
throw new \LogicException(sprintf('The "%s::%s" can only be used for classes that extend "%s"', __CLASS__, __METHOD__, Result::class));
throw new LogicException(sprintf('The "%s::%s" can only be used for classes that extend "%s"', __CLASS__, __METHOD__, Result::class));
}
}

Expand Down Expand Up @@ -69,7 +70,7 @@ public static function create(string $class, array $data = [])
if (Result::class !== $class) {
$parent = get_parent_class($class);
if (false === $parent || Result::class !== $parent) {
throw new \LogicException(sprintf('The "%s::%s" can only be used for classes that extend "%s"', __CLASS__, __METHOD__, Result::class));
throw new LogicException(sprintf('The "%s::%s" can only be used for classes that extend "%s"', __CLASS__, __METHOD__, Result::class));
}
}

Expand Down Expand Up @@ -111,12 +112,12 @@ public static function waiter(string $class, string $finalState)
if (Result::class !== $class) {
$parent = get_parent_class($class);
if (false === $parent || Waiter::class !== $parent) {
throw new \LogicException(sprintf('The "%s::%s" can only be used for classes that extend "%s"', __CLASS__, __METHOD__, Waiter::class));
throw new LogicException(sprintf('The "%s::%s" can only be used for classes that extend "%s"', __CLASS__, __METHOD__, Waiter::class));
}
}

if (Waiter::STATE_SUCCESS !== $finalState && Waiter::STATE_FAILURE !== $finalState) {
throw new \LogicException(sprintf('The state passed to "%s::%s" must be "%s" or "%s".', __CLASS__, __METHOD__, Waiter::STATE_SUCCESS, Waiter::STATE_FAILURE));
throw new LogicException(sprintf('The state passed to "%s::%s" must be "%s" or "%s".', __CLASS__, __METHOD__, Waiter::STATE_SUCCESS, Waiter::STATE_FAILURE));
}

$response = self::getResponseObject();
Expand Down Expand Up @@ -197,7 +198,7 @@ private static function addUndefinedProperties(\ReflectionClass $reflectionClass
private static function addPropertiesOnResult(\ReflectionClass $reflectionClass, object $object, string $class): void
{
if (false === $pos = strrpos($class, '\\')) {
throw new \LogicException(sprintf('Expected class "%s" to have a backslash. ', $class));
throw new LogicException(sprintf('Expected class "%s" to have a backslash. ', $class));
}

$className = substr($class, $pos + 1);
Expand All @@ -208,16 +209,16 @@ private static function addPropertiesOnResult(\ReflectionClass $reflectionClass,
} elseif ('Result' === substr($className, -6)) {
$classNameWithoutSuffix = substr($className, 0, -6);
} else {
throw new \LogicException(sprintf('Unknown class suffix: "%s"', $className));
throw new LogicException(sprintf('Unknown class suffix: "%s"', $className));
}

if (false === $pos = strrpos($class, '\\', -2 - \strlen($className))) {
throw new \LogicException(sprintf('Expected class "%s" to have more than one backslash. ', $class));
throw new LogicException(sprintf('Expected class "%s" to have more than one backslash. ', $class));
}

$baseNamespace = substr($class, 0, $pos);
if (false === $pos = strrpos($baseNamespace, '\\')) {
throw new \LogicException(sprintf('Expected base namespace "%s" to have a backslash. ', $baseNamespace));
throw new LogicException(sprintf('Expected base namespace "%s" to have a backslash. ', $baseNamespace));
}

$awsClientClass = $baseNamespace . (substr($baseNamespace, $pos)) . 'Client';
Expand Down
3 changes: 2 additions & 1 deletion src/Core/src/Waiter.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use AsyncAws\Core\Exception\Http\HttpException;
use AsyncAws\Core\Exception\Http\NetworkException;
use AsyncAws\Core\Exception\LogicException;

/**
* The waiter promise is always returned from every API call to a waiter.
Expand Down Expand Up @@ -104,7 +105,7 @@ final public function getState(): string
case self::STATE_PENDING:
break;
default:
throw new \LogicException(sprintf('Unexpected state "%s" from Waiter "%s".', $state, __CLASS__));
throw new LogicException(sprintf('Unexpected state "%s" from Waiter "%s".', $state, __CLASS__));
}

return $state;
Expand Down
3 changes: 2 additions & 1 deletion src/Integration/Aws/DynamoDbSession/src/SessionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace AsyncAws\DynamoDbSession;

use AsyncAws\Core\Exception\RuntimeException;
use AsyncAws\DynamoDb\DynamoDbClient;
use AsyncAws\DynamoDb\Enum\BillingMode;
use AsyncAws\DynamoDb\Enum\KeyType;
Expand Down Expand Up @@ -82,7 +83,7 @@ public function setUp(): void
$response = $this->client->tableExists(['TableName' => $this->options['table_name']]);
$response->wait(100, 3);
if (!$response->isSuccess()) {
throw new \RuntimeException(sprintf('Could not create table %s', $this->options['table_name']));
throw new RuntimeException(sprintf('Could not create table %s', $this->options['table_name']));
}

$this->client->updateTimeToLive([
Expand Down
6 changes: 4 additions & 2 deletions src/Integration/Flysystem/S3/src/S3FilesystemV1.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace AsyncAws\Flysystem\S3;

use AsyncAws\Core\Exception\Http\ClientException;
use AsyncAws\Core\Exception\InvalidArgument;
use AsyncAws\Core\Exception\LogicException;
use AsyncAws\S3\Result\GetObjectOutput;
use AsyncAws\S3\Result\HeadObjectOutput;
use AsyncAws\S3\Result\PutObjectOutput;
Expand All @@ -19,7 +21,7 @@
use League\Flysystem\Util;

if (!class_exists(AbstractAdapter::class)) {
throw new \LogicException('You cannot use "AsyncAws\Flysystem\S3\S3FilesystemV1" as the "league/flysystem:1.x" package is not installed. Try running "composer require league/flysystem:^1.0".');
throw new LogicException('You cannot use "AsyncAws\Flysystem\S3\S3FilesystemV1" as the "league/flysystem:1.x" package is not installed. Try running "composer require league/flysystem:^1.0".');
}

class S3FilesystemV1 extends AbstractAdapter implements CanOverwriteFiles
Expand Down Expand Up @@ -523,7 +525,7 @@ protected function normalizeResponse(object $output, ?string $path = null): arra
$path = $output->getPrefix();
}
if (empty($path)) {
throw new \InvalidArgumentException('The provided path should not be null');
throw new InvalidArgument('The provided path should not be null');
}

$path = $this->removePathPrefix($path);
Expand Down
8 changes: 5 additions & 3 deletions src/Integration/Flysystem/S3/src/S3FilesystemV2.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace AsyncAws\Flysystem\S3;

use AsyncAws\Core\Exception\Http\ClientException;
use AsyncAws\Core\Exception\LogicException;
use AsyncAws\Core\Exception\RuntimeException;
use AsyncAws\Core\Stream\ResultStream;
use AsyncAws\Flysystem\S3\Visibility\PortableVisibilityConverter;
use AsyncAws\Flysystem\S3\Visibility\VisibilityConverter;
Expand Down Expand Up @@ -35,7 +37,7 @@
use Throwable;

if (!interface_exists(FilesystemAdapter::class)) {
throw new \LogicException('You cannot use "AsyncAws\Flysystem\S3\S3FilesystemV2" as the "league/flysystem:2.x" package is not installed. Try running "composer require league/flysystem:^2.0".');
throw new LogicException('You cannot use "AsyncAws\Flysystem\S3\S3FilesystemV2" as the "league/flysystem:2.x" package is not installed. Try running "composer require league/flysystem:^2.0".');
}

class S3FilesystemV2 implements FilesystemAdapter
Expand Down Expand Up @@ -390,7 +392,7 @@ private function mapS3ObjectMetadata($item, $path = null): StorageAttributes
} elseif ($item instanceof CommonPrefix) {
$path = $this->prefixer->stripPrefix($item->getPrefix() ?? '');
} else {
throw new \RuntimeException(sprintf('Argument 2 of "%s" cannot be null when $item is not instance of "%s" or %s', __METHOD__, AwsObject::class, CommonPrefix::class));
throw new RuntimeException(sprintf('Argument 2 of "%s" cannot be null when $item is not instance of "%s" or %s', __METHOD__, AwsObject::class, CommonPrefix::class));
}
}

Expand All @@ -415,7 +417,7 @@ private function mapS3ObjectMetadata($item, $path = null): StorageAttributes
$dateTime = $item->getLastModified();
$metadata = $this->extractExtraMetadata($item);
} else {
throw new \RuntimeException(sprintf('Object of class "%s" is not supported in %s()', \get_class($item), __METHOD__));
throw new RuntimeException(sprintf('Object of class "%s" is not supported in %s()', \get_class($item), __METHOD__));
}

if ($dateTime instanceof \DateTimeInterface) {
Expand Down
7 changes: 4 additions & 3 deletions src/Integration/Laravel/Cache/src/AsyncAwsDynamoDbStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace AsyncAws\Illuminate\Cache;

use AsyncAws\Core\Exception\Http\HttpException;
use AsyncAws\Core\Exception\RuntimeException;
use AsyncAws\DynamoDb\DynamoDbClient;
use AsyncAws\DynamoDb\Enum\KeyType;
use AsyncAws\DynamoDb\ValueObject\KeySchemaElement;
Expand Down Expand Up @@ -413,15 +414,15 @@ public function flush()
$code = (int) $e->getCode();
if (404 !== $code) {
// Any error but "table not found"
throw new \RuntimeException('Could not flush DynamoDb cache. Table could not be deleted.', $code, $e);
throw new RuntimeException('Could not flush DynamoDb cache. Table could not be deleted.', $code, $e);
}
}

// Wait until table is removed
$response = $this->dynamoDb->tableNotExists(['TableName' => $this->table]);
$response->wait(100, 3);
if (!$response->isSuccess()) {
throw new \RuntimeException('Could not flush DynamoDb cache. Table could not be deleted.');
throw new RuntimeException('Could not flush DynamoDb cache. Table could not be deleted.');
}

// Create a new table
Expand All @@ -436,7 +437,7 @@ public function flush()
$response = $this->dynamoDb->tableExists(['TableName' => $this->table]);
$response->wait(100, 3);
if (!$response->isSuccess()) {
throw new \RuntimeException('Could not flush DynamoDb cache. Table could not be created.');
throw new RuntimeException('Could not flush DynamoDb cache. Table could not be created.');
}

return true;
Expand Down
Loading

0 comments on commit 5fafb09

Please sign in to comment.