diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ed2af1..0778da5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: max-parallel: 10 matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] steps: - name: Set up PHP diff --git a/CHANGELOG.md b/CHANGELOG.md index a370d43..8467370 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,44 @@ ## NOT RELEASED +## 3.6.0 + +### Added + +- AWS enhancement: Documentation update for secondary indexes and Create_Table. +- AWS api-change: Added `eu-isoe-west-1` region + +### Changed + +- Sort exception alphabetically. +- AWS enhancement: Documentation updates. + +### Fixed + +- Fix the lowest bound for the `symfony/polyfill-uuid` requirement + +## 3.5.0 + +### Added + +- AWS api-change: Added `us-isof-east-1` and `us-isof-south-1` regions + +## 3.4.0 + +### Added + +- AWS api-change: This change adds support for global tables with multi-Region strong consistency (in preview). The UpdateTable API now supports a new attribute MultiRegionConsistency to set consistency when creating global tables. The DescribeTable output now optionally includes the MultiRegionConsistency attribute. + +### Changed + +- Avoid usage of `alias` when use statement refers to self + +## 3.3.1 + +### Changed + +- fix pagination when next token is an array + ## 3.3.0 ### Added diff --git a/composer.json b/composer.json index 096d3f0..81b4ee0 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "ext-filter": "*", "ext-json": "*", "async-aws/core": "^1.16", - "symfony/polyfill-uuid": "^1.0" + "symfony/polyfill-uuid": "^1.13.1" }, "conflict": { "symfony/http-client": "<4.4.16 <5.1.7" @@ -32,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.6-dev" } } } diff --git a/src/DynamoDbClient.php b/src/DynamoDbClient.php index 4b11e15..3604619 100644 --- a/src/DynamoDbClient.php +++ b/src/DynamoDbClient.php @@ -9,6 +9,7 @@ use AsyncAws\Core\RequestContext; use AsyncAws\DynamoDb\Enum\BillingMode; use AsyncAws\DynamoDb\Enum\ConditionalOperator; +use AsyncAws\DynamoDb\Enum\MultiRegionConsistency; use AsyncAws\DynamoDb\Enum\ReturnConsumedCapacity; use AsyncAws\DynamoDb\Enum\ReturnItemCollectionMetrics; use AsyncAws\DynamoDb\Enum\ReturnValue; @@ -22,6 +23,7 @@ use AsyncAws\DynamoDb\Exception\ItemCollectionSizeLimitExceededException; use AsyncAws\DynamoDb\Exception\LimitExceededException; use AsyncAws\DynamoDb\Exception\ProvisionedThroughputExceededException; +use AsyncAws\DynamoDb\Exception\ReplicatedWriteConflictException; use AsyncAws\DynamoDb\Exception\RequestLimitExceededException; use AsyncAws\DynamoDb\Exception\ResourceInUseException; use AsyncAws\DynamoDb\Exception\ResourceNotFoundException; @@ -129,6 +131,8 @@ class DynamoDbClient extends AbstractApi * minimum read capacity units according to the type of read. For more information, see Working with Tables [^2] in the * *Amazon DynamoDB Developer Guide*. * + * > `BatchGetItem` will result in a `ValidationException` if the same key is specified multiple times. + * * [^1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ErrorHandling.html#BatchOperations * [^2]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.html#CapacityUnitCalculations * @@ -141,19 +145,19 @@ class DynamoDbClient extends AbstractApi * '@region'?: string|null, * }|BatchGetItemInput $input * + * @throws InternalServerErrorException * @throws ProvisionedThroughputExceededException - * @throws ResourceNotFoundException * @throws RequestLimitExceededException - * @throws InternalServerErrorException + * @throws ResourceNotFoundException */ public function batchGetItem($input): BatchGetItemOutput { $input = BatchGetItemInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'BatchGetItem', 'region' => $input->getRegion(), 'exceptionMapping' => [ + 'InternalServerError' => InternalServerErrorException::class, 'ProvisionedThroughputExceededException' => ProvisionedThroughputExceededException::class, - 'ResourceNotFoundException' => ResourceNotFoundException::class, 'RequestLimitExceeded' => RequestLimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, + 'ResourceNotFoundException' => ResourceNotFoundException::class, ], 'usesEndpointDiscovery' => true])); return new BatchGetItemOutput($response, $this, $input); @@ -234,21 +238,21 @@ public function batchGetItem($input): BatchGetItemOutput * '@region'?: string|null, * }|BatchWriteItemInput $input * - * @throws ProvisionedThroughputExceededException - * @throws ResourceNotFoundException + * @throws InternalServerErrorException * @throws ItemCollectionSizeLimitExceededException + * @throws ProvisionedThroughputExceededException * @throws RequestLimitExceededException - * @throws InternalServerErrorException + * @throws ResourceNotFoundException */ public function batchWriteItem($input): BatchWriteItemOutput { $input = BatchWriteItemInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'BatchWriteItem', 'region' => $input->getRegion(), 'exceptionMapping' => [ - 'ProvisionedThroughputExceededException' => ProvisionedThroughputExceededException::class, - 'ResourceNotFoundException' => ResourceNotFoundException::class, + 'InternalServerError' => InternalServerErrorException::class, 'ItemCollectionSizeLimitExceededException' => ItemCollectionSizeLimitExceededException::class, + 'ProvisionedThroughputExceededException' => ProvisionedThroughputExceededException::class, 'RequestLimitExceeded' => RequestLimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, + 'ResourceNotFoundException' => ResourceNotFoundException::class, ], 'usesEndpointDiscovery' => true])); return new BatchWriteItemOutput($response); @@ -291,17 +295,17 @@ public function batchWriteItem($input): BatchWriteItemOutput * '@region'?: string|null, * }|CreateTableInput $input * - * @throws ResourceInUseException - * @throws LimitExceededException * @throws InternalServerErrorException + * @throws LimitExceededException + * @throws ResourceInUseException */ public function createTable($input): CreateTableOutput { $input = CreateTableInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'CreateTable', 'region' => $input->getRegion(), 'exceptionMapping' => [ - 'ResourceInUseException' => ResourceInUseException::class, - 'LimitExceededException' => LimitExceededException::class, 'InternalServerError' => InternalServerErrorException::class, + 'LimitExceededException' => LimitExceededException::class, + 'ResourceInUseException' => ResourceInUseException::class, ], 'usesEndpointDiscovery' => true])); return new CreateTableOutput($response); @@ -339,24 +343,26 @@ public function createTable($input): CreateTableOutput * }|DeleteItemInput $input * * @throws ConditionalCheckFailedException + * @throws InternalServerErrorException + * @throws ItemCollectionSizeLimitExceededException * @throws ProvisionedThroughputExceededException + * @throws ReplicatedWriteConflictException + * @throws RequestLimitExceededException * @throws ResourceNotFoundException - * @throws ItemCollectionSizeLimitExceededException * @throws TransactionConflictException - * @throws RequestLimitExceededException - * @throws InternalServerErrorException */ public function deleteItem($input): DeleteItemOutput { $input = DeleteItemInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'DeleteItem', 'region' => $input->getRegion(), 'exceptionMapping' => [ 'ConditionalCheckFailedException' => ConditionalCheckFailedException::class, + 'InternalServerError' => InternalServerErrorException::class, + 'ItemCollectionSizeLimitExceededException' => ItemCollectionSizeLimitExceededException::class, 'ProvisionedThroughputExceededException' => ProvisionedThroughputExceededException::class, + 'ReplicatedWriteConflictException' => ReplicatedWriteConflictException::class, + 'RequestLimitExceeded' => RequestLimitExceededException::class, 'ResourceNotFoundException' => ResourceNotFoundException::class, - 'ItemCollectionSizeLimitExceededException' => ItemCollectionSizeLimitExceededException::class, 'TransactionConflictException' => TransactionConflictException::class, - 'RequestLimitExceeded' => RequestLimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, ], 'usesEndpointDiscovery' => true])); return new DeleteItemOutput($response); @@ -391,19 +397,19 @@ public function deleteItem($input): DeleteItemOutput * '@region'?: string|null, * }|DeleteTableInput $input * + * @throws InternalServerErrorException + * @throws LimitExceededException * @throws ResourceInUseException * @throws ResourceNotFoundException - * @throws LimitExceededException - * @throws InternalServerErrorException */ public function deleteTable($input): DeleteTableOutput { $input = DeleteTableInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'DeleteTable', 'region' => $input->getRegion(), 'exceptionMapping' => [ + 'InternalServerError' => InternalServerErrorException::class, + 'LimitExceededException' => LimitExceededException::class, 'ResourceInUseException' => ResourceInUseException::class, 'ResourceNotFoundException' => ResourceNotFoundException::class, - 'LimitExceededException' => LimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, ], 'usesEndpointDiscovery' => true])); return new DeleteTableOutput($response); @@ -449,15 +455,15 @@ public function describeEndpoints($input = []): DescribeEndpointsResponse * '@region'?: string|null, * }|DescribeTableInput $input * - * @throws ResourceNotFoundException * @throws InternalServerErrorException + * @throws ResourceNotFoundException */ public function describeTable($input): DescribeTableOutput { $input = DescribeTableInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'DescribeTable', 'region' => $input->getRegion(), 'exceptionMapping' => [ - 'ResourceNotFoundException' => ResourceNotFoundException::class, 'InternalServerError' => InternalServerErrorException::class, + 'ResourceNotFoundException' => ResourceNotFoundException::class, ], 'usesEndpointDiscovery' => true])); return new DescribeTableOutput($response); @@ -491,26 +497,26 @@ public function describeTable($input): DescribeTableOutput * }|ExecuteStatementInput $input * * @throws ConditionalCheckFailedException + * @throws DuplicateItemException + * @throws InternalServerErrorException + * @throws ItemCollectionSizeLimitExceededException * @throws ProvisionedThroughputExceededException + * @throws RequestLimitExceededException * @throws ResourceNotFoundException - * @throws ItemCollectionSizeLimitExceededException * @throws TransactionConflictException - * @throws RequestLimitExceededException - * @throws InternalServerErrorException - * @throws DuplicateItemException */ public function executeStatement($input): ExecuteStatementOutput { $input = ExecuteStatementInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'ExecuteStatement', 'region' => $input->getRegion(), 'exceptionMapping' => [ 'ConditionalCheckFailedException' => ConditionalCheckFailedException::class, + 'DuplicateItemException' => DuplicateItemException::class, + 'InternalServerError' => InternalServerErrorException::class, + 'ItemCollectionSizeLimitExceededException' => ItemCollectionSizeLimitExceededException::class, 'ProvisionedThroughputExceededException' => ProvisionedThroughputExceededException::class, + 'RequestLimitExceeded' => RequestLimitExceededException::class, 'ResourceNotFoundException' => ResourceNotFoundException::class, - 'ItemCollectionSizeLimitExceededException' => ItemCollectionSizeLimitExceededException::class, 'TransactionConflictException' => TransactionConflictException::class, - 'RequestLimitExceeded' => RequestLimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, - 'DuplicateItemException' => DuplicateItemException::class, ]])); return new ExecuteStatementOutput($response); @@ -538,19 +544,19 @@ public function executeStatement($input): ExecuteStatementOutput * '@region'?: string|null, * }|GetItemInput $input * + * @throws InternalServerErrorException * @throws ProvisionedThroughputExceededException - * @throws ResourceNotFoundException * @throws RequestLimitExceededException - * @throws InternalServerErrorException + * @throws ResourceNotFoundException */ public function getItem($input): GetItemOutput { $input = GetItemInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'GetItem', 'region' => $input->getRegion(), 'exceptionMapping' => [ + 'InternalServerError' => InternalServerErrorException::class, 'ProvisionedThroughputExceededException' => ProvisionedThroughputExceededException::class, - 'ResourceNotFoundException' => ResourceNotFoundException::class, 'RequestLimitExceeded' => RequestLimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, + 'ResourceNotFoundException' => ResourceNotFoundException::class, ], 'usesEndpointDiscovery' => true])); return new GetItemOutput($response); @@ -624,24 +630,26 @@ public function listTables($input = []): ListTablesOutput * }|PutItemInput $input * * @throws ConditionalCheckFailedException + * @throws InternalServerErrorException + * @throws ItemCollectionSizeLimitExceededException * @throws ProvisionedThroughputExceededException + * @throws ReplicatedWriteConflictException + * @throws RequestLimitExceededException * @throws ResourceNotFoundException - * @throws ItemCollectionSizeLimitExceededException * @throws TransactionConflictException - * @throws RequestLimitExceededException - * @throws InternalServerErrorException */ public function putItem($input): PutItemOutput { $input = PutItemInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'PutItem', 'region' => $input->getRegion(), 'exceptionMapping' => [ 'ConditionalCheckFailedException' => ConditionalCheckFailedException::class, + 'InternalServerError' => InternalServerErrorException::class, + 'ItemCollectionSizeLimitExceededException' => ItemCollectionSizeLimitExceededException::class, 'ProvisionedThroughputExceededException' => ProvisionedThroughputExceededException::class, + 'ReplicatedWriteConflictException' => ReplicatedWriteConflictException::class, + 'RequestLimitExceeded' => RequestLimitExceededException::class, 'ResourceNotFoundException' => ResourceNotFoundException::class, - 'ItemCollectionSizeLimitExceededException' => ItemCollectionSizeLimitExceededException::class, 'TransactionConflictException' => TransactionConflictException::class, - 'RequestLimitExceeded' => RequestLimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, ], 'usesEndpointDiscovery' => true])); return new PutItemOutput($response); @@ -713,19 +721,19 @@ public function putItem($input): PutItemOutput * '@region'?: string|null, * }|QueryInput $input * + * @throws InternalServerErrorException * @throws ProvisionedThroughputExceededException - * @throws ResourceNotFoundException * @throws RequestLimitExceededException - * @throws InternalServerErrorException + * @throws ResourceNotFoundException */ public function query($input): QueryOutput { $input = QueryInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'Query', 'region' => $input->getRegion(), 'exceptionMapping' => [ + 'InternalServerError' => InternalServerErrorException::class, 'ProvisionedThroughputExceededException' => ProvisionedThroughputExceededException::class, - 'ResourceNotFoundException' => ResourceNotFoundException::class, 'RequestLimitExceeded' => RequestLimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, + 'ResourceNotFoundException' => ResourceNotFoundException::class, ], 'usesEndpointDiscovery' => true])); return new QueryOutput($response, $this, $input); @@ -790,19 +798,19 @@ public function query($input): QueryOutput * '@region'?: string|null, * }|ScanInput $input * + * @throws InternalServerErrorException * @throws ProvisionedThroughputExceededException - * @throws ResourceNotFoundException * @throws RequestLimitExceededException - * @throws InternalServerErrorException + * @throws ResourceNotFoundException */ public function scan($input): ScanOutput { $input = ScanInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'Scan', 'region' => $input->getRegion(), 'exceptionMapping' => [ + 'InternalServerError' => InternalServerErrorException::class, 'ProvisionedThroughputExceededException' => ProvisionedThroughputExceededException::class, - 'ResourceNotFoundException' => ResourceNotFoundException::class, 'RequestLimitExceeded' => RequestLimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, + 'ResourceNotFoundException' => ResourceNotFoundException::class, ], 'usesEndpointDiscovery' => true])); return new ScanOutput($response, $this, $input); @@ -820,8 +828,8 @@ public function tableExists($input): TableExistsWaiter { $input = DescribeTableInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'DescribeTable', 'region' => $input->getRegion(), 'exceptionMapping' => [ - 'ResourceNotFoundException' => ResourceNotFoundException::class, 'InternalServerError' => InternalServerErrorException::class, + 'ResourceNotFoundException' => ResourceNotFoundException::class, ]])); return new TableExistsWaiter($response, $this, $input); @@ -839,8 +847,8 @@ public function tableNotExists($input): TableNotExistsWaiter { $input = DescribeTableInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'DescribeTable', 'region' => $input->getRegion(), 'exceptionMapping' => [ - 'ResourceNotFoundException' => ResourceNotFoundException::class, 'InternalServerError' => InternalServerErrorException::class, + 'ResourceNotFoundException' => ResourceNotFoundException::class, ]])); return new TableNotExistsWaiter($response, $this, $input); @@ -893,25 +901,25 @@ public function tableNotExists($input): TableNotExistsWaiter * '@region'?: string|null, * }|TransactWriteItemsInput $input * - * @throws ResourceNotFoundException - * @throws TransactionCanceledException - * @throws TransactionInProgressException * @throws IdempotentParameterMismatchException + * @throws InternalServerErrorException * @throws ProvisionedThroughputExceededException * @throws RequestLimitExceededException - * @throws InternalServerErrorException + * @throws ResourceNotFoundException + * @throws TransactionCanceledException + * @throws TransactionInProgressException */ public function transactWriteItems($input): TransactWriteItemsOutput { $input = TransactWriteItemsInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'TransactWriteItems', 'region' => $input->getRegion(), 'exceptionMapping' => [ - 'ResourceNotFoundException' => ResourceNotFoundException::class, - 'TransactionCanceledException' => TransactionCanceledException::class, - 'TransactionInProgressException' => TransactionInProgressException::class, 'IdempotentParameterMismatchException' => IdempotentParameterMismatchException::class, + 'InternalServerError' => InternalServerErrorException::class, 'ProvisionedThroughputExceededException' => ProvisionedThroughputExceededException::class, 'RequestLimitExceeded' => RequestLimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, + 'ResourceNotFoundException' => ResourceNotFoundException::class, + 'TransactionCanceledException' => TransactionCanceledException::class, + 'TransactionInProgressException' => TransactionInProgressException::class, ], 'usesEndpointDiscovery' => true])); return new TransactWriteItemsOutput($response); @@ -947,24 +955,26 @@ public function transactWriteItems($input): TransactWriteItemsOutput * }|UpdateItemInput $input * * @throws ConditionalCheckFailedException + * @throws InternalServerErrorException + * @throws ItemCollectionSizeLimitExceededException * @throws ProvisionedThroughputExceededException + * @throws ReplicatedWriteConflictException + * @throws RequestLimitExceededException * @throws ResourceNotFoundException - * @throws ItemCollectionSizeLimitExceededException * @throws TransactionConflictException - * @throws RequestLimitExceededException - * @throws InternalServerErrorException */ public function updateItem($input): UpdateItemOutput { $input = UpdateItemInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'UpdateItem', 'region' => $input->getRegion(), 'exceptionMapping' => [ 'ConditionalCheckFailedException' => ConditionalCheckFailedException::class, + 'InternalServerError' => InternalServerErrorException::class, + 'ItemCollectionSizeLimitExceededException' => ItemCollectionSizeLimitExceededException::class, 'ProvisionedThroughputExceededException' => ProvisionedThroughputExceededException::class, + 'ReplicatedWriteConflictException' => ReplicatedWriteConflictException::class, + 'RequestLimitExceeded' => RequestLimitExceededException::class, 'ResourceNotFoundException' => ResourceNotFoundException::class, - 'ItemCollectionSizeLimitExceededException' => ItemCollectionSizeLimitExceededException::class, 'TransactionConflictException' => TransactionConflictException::class, - 'RequestLimitExceeded' => RequestLimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, ], 'usesEndpointDiscovery' => true])); return new UpdateItemOutput($response); @@ -1001,24 +1011,25 @@ public function updateItem($input): UpdateItemOutput * ReplicaUpdates?: null|array, * TableClass?: null|TableClass::*, * DeletionProtectionEnabled?: null|bool, + * MultiRegionConsistency?: null|MultiRegionConsistency::*, * OnDemandThroughput?: null|OnDemandThroughput|array, * WarmThroughput?: null|WarmThroughput|array, * '@region'?: string|null, * }|UpdateTableInput $input * + * @throws InternalServerErrorException + * @throws LimitExceededException * @throws ResourceInUseException * @throws ResourceNotFoundException - * @throws LimitExceededException - * @throws InternalServerErrorException */ public function updateTable($input): UpdateTableOutput { $input = UpdateTableInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'UpdateTable', 'region' => $input->getRegion(), 'exceptionMapping' => [ + 'InternalServerError' => InternalServerErrorException::class, + 'LimitExceededException' => LimitExceededException::class, 'ResourceInUseException' => ResourceInUseException::class, 'ResourceNotFoundException' => ResourceNotFoundException::class, - 'LimitExceededException' => LimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, ], 'usesEndpointDiscovery' => true])); return new UpdateTableOutput($response); @@ -1058,19 +1069,19 @@ public function updateTable($input): UpdateTableOutput * '@region'?: string|null, * }|UpdateTimeToLiveInput $input * + * @throws InternalServerErrorException + * @throws LimitExceededException * @throws ResourceInUseException * @throws ResourceNotFoundException - * @throws LimitExceededException - * @throws InternalServerErrorException */ public function updateTimeToLive($input): UpdateTimeToLiveOutput { $input = UpdateTimeToLiveInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'UpdateTimeToLive', 'region' => $input->getRegion(), 'exceptionMapping' => [ + 'InternalServerError' => InternalServerErrorException::class, + 'LimitExceededException' => LimitExceededException::class, 'ResourceInUseException' => ResourceInUseException::class, 'ResourceNotFoundException' => ResourceNotFoundException::class, - 'LimitExceededException' => LimitExceededException::class, - 'InternalServerError' => InternalServerErrorException::class, ], 'usesEndpointDiscovery' => true])); return new UpdateTimeToLiveOutput($response); @@ -1152,14 +1163,14 @@ protected function getEndpointMetadata(?string $region): array ]; case 'us-gov-east-1-fips': return [ - 'endpoint' => 'https://dynamodb.us-gov-east-1.amazonaws.com', + 'endpoint' => 'https://dynamodb-fips.us-gov-east-1.amazonaws.com', 'signRegion' => 'us-gov-east-1', 'signService' => 'dynamodb', 'signVersions' => ['v4'], ]; case 'us-gov-west-1-fips': return [ - 'endpoint' => 'https://dynamodb.us-gov-west-1.amazonaws.com', + 'endpoint' => 'https://dynamodb-fips.us-gov-west-1.amazonaws.com', 'signRegion' => 'us-gov-west-1', 'signService' => 'dynamodb', 'signVersions' => ['v4'], @@ -1172,6 +1183,21 @@ protected function getEndpointMetadata(?string $region): array 'signService' => 'dynamodb', 'signVersions' => ['v4'], ]; + case 'us-isof-east-1': + case 'us-isof-south-1': + return [ + 'endpoint' => "https://dynamodb.$region.csp.hci.ic.gov", + 'signRegion' => $region, + 'signService' => 'dynamodb', + 'signVersions' => ['v4'], + ]; + case 'eu-isoe-west-1': + return [ + 'endpoint' => 'https://dynamodb.eu-isoe-west-1.cloud.adc-e.uk', + 'signRegion' => 'eu-isoe-west-1', + 'signService' => 'dynamodb', + 'signVersions' => ['v4'], + ]; case 'us-isob-east-1': return [ 'endpoint' => 'https://dynamodb.us-isob-east-1.sc2s.sgov.gov', diff --git a/src/Enum/MultiRegionConsistency.php b/src/Enum/MultiRegionConsistency.php new file mode 100644 index 0000000..6d3c6ce --- /dev/null +++ b/src/Enum/MultiRegionConsistency.php @@ -0,0 +1,17 @@ + true, + self::STRONG => true, + ][$value]); + } +} diff --git a/src/Exception/ConditionalCheckFailedException.php b/src/Exception/ConditionalCheckFailedException.php index 781cad1..7778716 100644 --- a/src/Exception/ConditionalCheckFailedException.php +++ b/src/Exception/ConditionalCheckFailedException.php @@ -7,7 +7,7 @@ use Symfony\Contracts\HttpClient\ResponseInterface; /** - * A condition specified in the operation could not be evaluated. + * A condition specified in the operation failed to be evaluated. */ final class ConditionalCheckFailedException extends ClientException { diff --git a/src/Exception/ReplicatedWriteConflictException.php b/src/Exception/ReplicatedWriteConflictException.php new file mode 100644 index 0000000..f9fd234 --- /dev/null +++ b/src/Exception/ReplicatedWriteConflictException.php @@ -0,0 +1,12 @@ + Multi-Region strong consistency (MRSC) is a new DynamoDB global tables capability currently available in preview + * > mode. For more information, see Global tables multi-Region strong consistency [^3]. + * + * + * If you don't specify this parameter, the global table consistency mode defaults to `EVENTUAL`. + * + * [^1]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ReplicationGroupUpdate.html#DDB-Type-ReplicationGroupUpdate-Create + * [^2]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html#DDB-UpdateTable-request-ReplicaUpdates + * [^3]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/PreviewFeatures.html#multi-region-strong-consistency-gt + * + * @var MultiRegionConsistency::*|null + */ + private $multiRegionConsistency; + /** * Updates the maximum number of read and write units for the specified table in on-demand capacity mode. If you use * this parameter, you must specify `MaxReadRequestUnits`, `MaxWriteRequestUnits`, or both. @@ -150,6 +175,7 @@ final class UpdateTableInput extends Input * ReplicaUpdates?: null|array, * TableClass?: null|TableClass::*, * DeletionProtectionEnabled?: null|bool, + * MultiRegionConsistency?: null|MultiRegionConsistency::*, * OnDemandThroughput?: null|OnDemandThroughput|array, * WarmThroughput?: null|WarmThroughput|array, * '@region'?: string|null, @@ -167,6 +193,7 @@ public function __construct(array $input = []) $this->replicaUpdates = isset($input['ReplicaUpdates']) ? array_map([ReplicationGroupUpdate::class, 'create'], $input['ReplicaUpdates']) : null; $this->tableClass = $input['TableClass'] ?? null; $this->deletionProtectionEnabled = $input['DeletionProtectionEnabled'] ?? null; + $this->multiRegionConsistency = $input['MultiRegionConsistency'] ?? null; $this->onDemandThroughput = isset($input['OnDemandThroughput']) ? OnDemandThroughput::create($input['OnDemandThroughput']) : null; $this->warmThroughput = isset($input['WarmThroughput']) ? WarmThroughput::create($input['WarmThroughput']) : null; parent::__construct($input); @@ -184,6 +211,7 @@ public function __construct(array $input = []) * ReplicaUpdates?: null|array, * TableClass?: null|TableClass::*, * DeletionProtectionEnabled?: null|bool, + * MultiRegionConsistency?: null|MultiRegionConsistency::*, * OnDemandThroughput?: null|OnDemandThroughput|array, * WarmThroughput?: null|WarmThroughput|array, * '@region'?: string|null, @@ -223,6 +251,14 @@ public function getGlobalSecondaryIndexUpdates(): array return $this->globalSecondaryIndexUpdates ?? []; } + /** + * @return MultiRegionConsistency::*|null + */ + public function getMultiRegionConsistency(): ?string + { + return $this->multiRegionConsistency; + } + public function getOnDemandThroughput(): ?OnDemandThroughput { return $this->onDemandThroughput; @@ -332,6 +368,16 @@ public function setGlobalSecondaryIndexUpdates(array $value): self return $this; } + /** + * @param MultiRegionConsistency::*|null $value + */ + public function setMultiRegionConsistency(?string $value): self + { + $this->multiRegionConsistency = $value; + + return $this; + } + public function setOnDemandThroughput(?OnDemandThroughput $value): self { $this->onDemandThroughput = $value; @@ -449,6 +495,12 @@ private function requestBody(): array if (null !== $v = $this->deletionProtectionEnabled) { $payload['DeletionProtectionEnabled'] = (bool) $v; } + if (null !== $v = $this->multiRegionConsistency) { + if (!MultiRegionConsistency::exists($v)) { + throw new InvalidArgument(\sprintf('Invalid parameter "MultiRegionConsistency" for "%s". The value "%s" is not a valid "MultiRegionConsistency".', __CLASS__, $v)); + } + $payload['MultiRegionConsistency'] = $v; + } if (null !== $v = $this->onDemandThroughput) { $payload['OnDemandThroughput'] = $v->requestBody(); } diff --git a/src/Result/BatchGetItemOutput.php b/src/Result/BatchGetItemOutput.php index 11237a3..ed57f41 100644 --- a/src/Result/BatchGetItemOutput.php +++ b/src/Result/BatchGetItemOutput.php @@ -83,7 +83,7 @@ public function getConsumedCapacity(bool $currentPageOnly = false): iterable $page = $this; while (true) { $page->initialize(); - if (null !== $page->unprocessedKeys) { + if ([] !== $page->unprocessedKeys) { $input->setRequestItems($page->unprocessedKeys); $this->registerPrefetch($nextPage = $client->batchGetItem($input)); diff --git a/src/Result/CreateTableOutput.php b/src/Result/CreateTableOutput.php index 4e34b43..b487133 100644 --- a/src/Result/CreateTableOutput.php +++ b/src/Result/CreateTableOutput.php @@ -348,6 +348,7 @@ private function populateResultTableDescription(array $json): TableDescription 'DeletionProtectionEnabled' => isset($json['DeletionProtectionEnabled']) ? filter_var($json['DeletionProtectionEnabled'], \FILTER_VALIDATE_BOOLEAN) : null, 'OnDemandThroughput' => empty($json['OnDemandThroughput']) ? null : $this->populateResultOnDemandThroughput($json['OnDemandThroughput']), 'WarmThroughput' => empty($json['WarmThroughput']) ? null : $this->populateResultTableWarmThroughputDescription($json['WarmThroughput']), + 'MultiRegionConsistency' => isset($json['MultiRegionConsistency']) ? (string) $json['MultiRegionConsistency'] : null, ]); } diff --git a/src/Result/DeleteTableOutput.php b/src/Result/DeleteTableOutput.php index b9a8c96..9b97d18 100644 --- a/src/Result/DeleteTableOutput.php +++ b/src/Result/DeleteTableOutput.php @@ -348,6 +348,7 @@ private function populateResultTableDescription(array $json): TableDescription 'DeletionProtectionEnabled' => isset($json['DeletionProtectionEnabled']) ? filter_var($json['DeletionProtectionEnabled'], \FILTER_VALIDATE_BOOLEAN) : null, 'OnDemandThroughput' => empty($json['OnDemandThroughput']) ? null : $this->populateResultOnDemandThroughput($json['OnDemandThroughput']), 'WarmThroughput' => empty($json['WarmThroughput']) ? null : $this->populateResultTableWarmThroughputDescription($json['WarmThroughput']), + 'MultiRegionConsistency' => isset($json['MultiRegionConsistency']) ? (string) $json['MultiRegionConsistency'] : null, ]); } diff --git a/src/Result/DescribeTableOutput.php b/src/Result/DescribeTableOutput.php index a624c29..e21aa0c 100644 --- a/src/Result/DescribeTableOutput.php +++ b/src/Result/DescribeTableOutput.php @@ -348,6 +348,7 @@ private function populateResultTableDescription(array $json): TableDescription 'DeletionProtectionEnabled' => isset($json['DeletionProtectionEnabled']) ? filter_var($json['DeletionProtectionEnabled'], \FILTER_VALIDATE_BOOLEAN) : null, 'OnDemandThroughput' => empty($json['OnDemandThroughput']) ? null : $this->populateResultOnDemandThroughput($json['OnDemandThroughput']), 'WarmThroughput' => empty($json['WarmThroughput']) ? null : $this->populateResultTableWarmThroughputDescription($json['WarmThroughput']), + 'MultiRegionConsistency' => isset($json['MultiRegionConsistency']) ? (string) $json['MultiRegionConsistency'] : null, ]); } diff --git a/src/Result/QueryOutput.php b/src/Result/QueryOutput.php index 449e03d..0b0a069 100644 --- a/src/Result/QueryOutput.php +++ b/src/Result/QueryOutput.php @@ -116,7 +116,7 @@ public function getItems(bool $currentPageOnly = false): iterable $page = $this; while (true) { $page->initialize(); - if (null !== $page->lastEvaluatedKey) { + if ([] !== $page->lastEvaluatedKey) { $input->setExclusiveStartKey($page->lastEvaluatedKey); $this->registerPrefetch($nextPage = $client->query($input)); diff --git a/src/Result/ScanOutput.php b/src/Result/ScanOutput.php index 85d2ca3..90ce249 100644 --- a/src/Result/ScanOutput.php +++ b/src/Result/ScanOutput.php @@ -116,7 +116,7 @@ public function getItems(bool $currentPageOnly = false): iterable $page = $this; while (true) { $page->initialize(); - if (null !== $page->lastEvaluatedKey) { + if ([] !== $page->lastEvaluatedKey) { $input->setExclusiveStartKey($page->lastEvaluatedKey); $this->registerPrefetch($nextPage = $client->scan($input)); diff --git a/src/Result/UpdateTableOutput.php b/src/Result/UpdateTableOutput.php index 3e6e247..529c113 100644 --- a/src/Result/UpdateTableOutput.php +++ b/src/Result/UpdateTableOutput.php @@ -348,6 +348,7 @@ private function populateResultTableDescription(array $json): TableDescription 'DeletionProtectionEnabled' => isset($json['DeletionProtectionEnabled']) ? filter_var($json['DeletionProtectionEnabled'], \FILTER_VALIDATE_BOOLEAN) : null, 'OnDemandThroughput' => empty($json['OnDemandThroughput']) ? null : $this->populateResultOnDemandThroughput($json['OnDemandThroughput']), 'WarmThroughput' => empty($json['WarmThroughput']) ? null : $this->populateResultTableWarmThroughputDescription($json['WarmThroughput']), + 'MultiRegionConsistency' => isset($json['MultiRegionConsistency']) ? (string) $json['MultiRegionConsistency'] : null, ]); } diff --git a/src/ValueObject/AttributeValue.php b/src/ValueObject/AttributeValue.php index 6b974f1..95f56b7 100644 --- a/src/ValueObject/AttributeValue.php +++ b/src/ValueObject/AttributeValue.php @@ -2,8 +2,6 @@ namespace AsyncAws\DynamoDb\ValueObject; -use AsyncAws\DynamoDb\ValueObject\AttributeValue as AttributeValue1; - /** * Represents the data for an attribute. * @@ -133,8 +131,8 @@ public function __construct(array $input) $this->ss = $input['SS'] ?? null; $this->ns = $input['NS'] ?? null; $this->bs = $input['BS'] ?? null; - $this->m = isset($input['M']) ? array_map([AttributeValue1::class, 'create'], $input['M']) : null; - $this->l = isset($input['L']) ? array_map([AttributeValue1::class, 'create'], $input['L']) : null; + $this->m = isset($input['M']) ? array_map([AttributeValue::class, 'create'], $input['M']) : null; + $this->l = isset($input['L']) ? array_map([AttributeValue::class, 'create'], $input['L']) : null; $this->null = $input['NULL'] ?? null; $this->bool = $input['BOOL'] ?? null; } diff --git a/src/ValueObject/CreateGlobalSecondaryIndexAction.php b/src/ValueObject/CreateGlobalSecondaryIndexAction.php index 1381c5b..0cf8b96 100644 --- a/src/ValueObject/CreateGlobalSecondaryIndexAction.php +++ b/src/ValueObject/CreateGlobalSecondaryIndexAction.php @@ -45,7 +45,8 @@ final class CreateGlobalSecondaryIndexAction /** * The maximum number of read and write units for the global secondary index being created. If you use this parameter, - * you must specify `MaxReadRequestUnits`, `MaxWriteRequestUnits`, or both. + * you must specify `MaxReadRequestUnits`, `MaxWriteRequestUnits`, or both. You must use either `OnDemand Throughput` or + * `ProvisionedThroughput` based on your table's capacity mode. * * @var OnDemandThroughput|null */ diff --git a/src/ValueObject/GlobalSecondaryIndex.php b/src/ValueObject/GlobalSecondaryIndex.php index a22d977..b876a68 100644 --- a/src/ValueObject/GlobalSecondaryIndex.php +++ b/src/ValueObject/GlobalSecondaryIndex.php @@ -43,7 +43,8 @@ final class GlobalSecondaryIndex private $projection; /** - * Represents the provisioned throughput settings for the specified global secondary index. + * Represents the provisioned throughput settings for the specified global secondary index. You must use either + * `OnDemandThroughput` or `ProvisionedThroughput` based on your table's capacity mode. * * For current minimum and maximum provisioned throughput values, see Service, Account, and Table Quotas [^1] in the * *Amazon DynamoDB Developer Guide*. @@ -56,7 +57,8 @@ final class GlobalSecondaryIndex /** * The maximum number of read and write units for the specified global secondary index. If you use this parameter, you - * must specify `MaxReadRequestUnits`, `MaxWriteRequestUnits`, or both. + * must specify `MaxReadRequestUnits`, `MaxWriteRequestUnits`, or both. You must use either `OnDemandThroughput` or + * `ProvisionedThroughput` based on your table's capacity mode. * * @var OnDemandThroughput|null */ diff --git a/src/ValueObject/Projection.php b/src/ValueObject/Projection.php index 8cd8421..a23b622 100644 --- a/src/ValueObject/Projection.php +++ b/src/ValueObject/Projection.php @@ -28,9 +28,11 @@ final class Projection /** * Represents the non-key attribute names which will be projected into the index. * - * For local secondary indexes, the total count of `NonKeyAttributes` summed across all of the local secondary indexes, - * must not exceed 100. If you project the same attribute into two different indexes, this counts as two distinct - * attributes when determining the total. + * For global and local secondary indexes, the total count of `NonKeyAttributes` summed across all of the secondary + * indexes, must not exceed 100. If you project the same attribute into two different indexes, this counts as two + * distinct attributes when determining the total. This limit only applies when you specify the ProjectionType of + * `INCLUDE`. You still can specify the ProjectionType of `ALL` to project all attributes from the source table, even if + * the table has more than 100 attributes. * * @var string[]|null */ diff --git a/src/ValueObject/ProvisionedThroughput.php b/src/ValueObject/ProvisionedThroughput.php index b39b9e4..41002f7 100644 --- a/src/ValueObject/ProvisionedThroughput.php +++ b/src/ValueObject/ProvisionedThroughput.php @@ -5,8 +5,8 @@ use AsyncAws\Core\Exception\InvalidArgument; /** - * Represents the provisioned throughput settings for a specified table or index. The settings can be modified using the - * `UpdateTable` operation. + * Represents the provisioned throughput settings for the specified global secondary index. You must use + * `ProvisionedThroughput` or `OnDemandThroughput` based on your table’s capacity mode. * * For current minimum and maximum provisioned throughput values, see Service, Account, and Table Quotas [^1] in the * *Amazon DynamoDB Developer Guide*. diff --git a/src/ValueObject/TableDescription.php b/src/ValueObject/TableDescription.php index 32f7c59..b7f09cd 100644 --- a/src/ValueObject/TableDescription.php +++ b/src/ValueObject/TableDescription.php @@ -2,6 +2,7 @@ namespace AsyncAws\DynamoDb\ValueObject; +use AsyncAws\DynamoDb\Enum\MultiRegionConsistency; use AsyncAws\DynamoDb\Enum\TableStatus; /** @@ -149,7 +150,9 @@ final class TableDescription * - `NonKeyAttributes` - A list of one or more non-key attribute names that are projected into the secondary index. * The total count of attributes provided in `NonKeyAttributes`, summed across all of the secondary indexes, must * not exceed 100. If you project the same attribute into two different indexes, this counts as two distinct - * attributes when determining the total. + * attributes when determining the total. This limit only applies when you specify the ProjectionType of `INCLUDE`. + * You still can specify the ProjectionType of `ALL` to project all attributes from the source table, even if the + * table has more than 100 attributes. * * - `IndexSizeBytes` - Represents the total size of the index, in bytes. DynamoDB updates this value approximately * every six hours. Recent changes might not be reflected in this value. @@ -202,7 +205,9 @@ final class TableDescription * - `NonKeyAttributes` - A list of one or more non-key attribute names that are projected into the secondary index. * The total count of attributes provided in `NonKeyAttributes`, summed across all of the secondary indexes, must * not exceed 100. If you project the same attribute into two different indexes, this counts as two distinct - * attributes when determining the total. + * attributes when determining the total. This limit only applies when you specify the ProjectionType of `INCLUDE`. + * You still can specify the ProjectionType of `ALL` to project all attributes from the source table, even if the + * table has more than 100 attributes. * * - `ProvisionedThroughput` - The provisioned throughput settings for the global secondary index, consisting of read * and write capacity units, along with data about increases and decreases. @@ -308,6 +313,24 @@ final class TableDescription */ private $warmThroughput; + /** + * Indicates one of the following consistency modes for a global table: + * + * - `EVENTUAL`: Indicates that the global table is configured for multi-Region eventual consistency. + * - `STRONG`: Indicates that the global table is configured for multi-Region strong consistency (preview). + * + * > Multi-Region strong consistency (MRSC) is a new DynamoDB global tables capability currently available in preview + * > mode. For more information, see Global tables multi-Region strong consistency [^1]. + * + * + * If you don't specify this field, the global table consistency mode defaults to `EVENTUAL`. + * + * [^1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/PreviewFeatures.html#multi-region-strong-consistency-gt + * + * @var MultiRegionConsistency::*|null + */ + private $multiRegionConsistency; + /** * @param array{ * AttributeDefinitions?: null|array, @@ -335,6 +358,7 @@ final class TableDescription * DeletionProtectionEnabled?: null|bool, * OnDemandThroughput?: null|OnDemandThroughput|array, * WarmThroughput?: null|TableWarmThroughputDescription|array, + * MultiRegionConsistency?: null|MultiRegionConsistency::*, * } $input */ public function __construct(array $input) @@ -364,6 +388,7 @@ public function __construct(array $input) $this->deletionProtectionEnabled = $input['DeletionProtectionEnabled'] ?? null; $this->onDemandThroughput = isset($input['OnDemandThroughput']) ? OnDemandThroughput::create($input['OnDemandThroughput']) : null; $this->warmThroughput = isset($input['WarmThroughput']) ? TableWarmThroughputDescription::create($input['WarmThroughput']) : null; + $this->multiRegionConsistency = $input['MultiRegionConsistency'] ?? null; } /** @@ -393,6 +418,7 @@ public function __construct(array $input) * DeletionProtectionEnabled?: null|bool, * OnDemandThroughput?: null|OnDemandThroughput|array, * WarmThroughput?: null|TableWarmThroughputDescription|array, + * MultiRegionConsistency?: null|MultiRegionConsistency::*, * }|TableDescription $input */ public static function create($input): self @@ -472,6 +498,14 @@ public function getLocalSecondaryIndexes(): array return $this->localSecondaryIndexes ?? []; } + /** + * @return MultiRegionConsistency::*|null + */ + public function getMultiRegionConsistency(): ?string + { + return $this->multiRegionConsistency; + } + public function getOnDemandThroughput(): ?OnDemandThroughput { return $this->onDemandThroughput; diff --git a/src/ValueObject/TableWarmThroughputDescription.php b/src/ValueObject/TableWarmThroughputDescription.php index 96c73ec..00433ea 100644 --- a/src/ValueObject/TableWarmThroughputDescription.php +++ b/src/ValueObject/TableWarmThroughputDescription.php @@ -5,7 +5,9 @@ use AsyncAws\DynamoDb\Enum\TableStatus; /** - * Represents the warm throughput value (in read units per second and write units per second) of the base table. + * Represents the warm throughput value (in read units per second and write units per second) of the table. Warm + * throughput is applicable for DynamoDB Standard-IA tables and specifies the minimum provisioned capacity maintained + * for immediate data access. */ final class TableWarmThroughputDescription { @@ -24,7 +26,7 @@ final class TableWarmThroughputDescription private $writeUnitsPerSecond; /** - * Represents warm throughput value of the base table.. + * Represents warm throughput value of the base table. * * @var TableStatus::*|null */