diff --git a/.github/workflows/branch_alias.yml b/.github/workflows/branch_alias.yml deleted file mode 100644 index f43964b..0000000 --- a/.github/workflows/branch_alias.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Update branch alias - -on: - push: - tags: ['*'] - -jobs: - branch-alias: - name: Update branch alias - runs-on: ubuntu-latest - - steps: - - name: Set up PHP - uses: shivammathur/setup-php@2.7.0 - with: - php-version: 8.0 - coverage: none - - - name: Find branch alias - id: find_alias - run: | - TAG=$(echo $GITHUB_REF | cut -d'/' -f 3) - echo "Last tag was $TAG" - ARR=(${TAG//./ }) - ARR[1]=$((${ARR[1]}+1)) - echo ::set-output name=alias::${ARR[0]}.${ARR[1]} - - - name: Checkout main repo - run: | - git clone --branch master https://${{ secrets.BOT_GITHUB_TOKEN }}:x-oauth-basic@github.com/async-aws/aws aws - - - name: Update branch alias - run: | - cd aws/src/Service/Firehose - CURRENT_ALIAS=$(composer config extra.branch-alias.dev-master | cut -d'-' -f 1) - - # If there is a current value on the branch alias - if [ ! -z $CURRENT_ALIAS ]; then - NEW_ALIAS=${{ steps.find_alias.outputs.alias }} - CURRENT_ARR=(${CURRENT_ALIAS//./ }) - NEW_ARR=(${NEW_ALIAS//./ }) - - if [ ${CURRENT_ARR[0]} -gt ${NEW_ARR[0]} ]; then - echo "The current value for major version is larger" - exit 1; - fi - - if [ ${CURRENT_ARR[0]} -eq ${NEW_ARR[0]} ] && [ ${CURRENT_ARR[1]} -gt ${NEW_ARR[1]} ]; then - echo "The current value for minor version is larger" - exit 1; - fi - fi - - composer config extra.branch-alias.dev-master ${{ steps.find_alias.outputs.alias }}-dev - - - name: Commit & push the new files - run: | - echo "::group::git status" - cd aws - git status - echo "::endgroup::" - - git add -N . - if [[ $(git diff --numstat | wc -l) -eq 0 ]]; then - echo "No changes found. Exiting." - exit 0; - fi - - git config --local user.email "github@async-aws.com" - git config --local user.name "AsyncAws Bot" - - echo "::group::git push" - git add . - git commit -m "Update branch alias" - git push - echo "::endgroup::" diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 1a72276..b9c47e8 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -12,7 +12,9 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Modify composer.json run: | @@ -23,5 +25,14 @@ jobs: git config --local user.name "AsyncAws Bot" git commit -am "Allow unstable dependencies" + - name: Install PHP with extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + tools: composer:v2 + + - name: Install roave/backward-compatibility-check + run: composer require --dev roave/backward-compatibility-check + - name: Roave BC Check - uses: docker://nyholm/roave-bc-check-ga + run: vendor/bin/roave-backward-compatibility-check diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c790a2..0778da5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,17 +13,17 @@ jobs: strategy: max-parallel: 10 matrix: - php: ['7.2', '7.3', '7.4', '8.0'] + php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] steps: - name: Set up PHP - uses: shivammathur/setup-php@2.7.0 + uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} coverage: none - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Initialize tests run: make initialize diff --git a/CHANGELOG.md b/CHANGELOG.md index 457d417..acc0ff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,86 @@ ## NOT RELEASED +## 1.5.0 + +### Added + +- AWS api-change: Added `eu-isoe-west-1` region + +### Changed + +- Sort exception alphabetically. + +## 1.4.0 + +### Added + +- AWS api-change: Added `us-isof-east-1` and `us-isof-south-1` regions + +## 1.3.3 + +### Changed + +- AWS enhancement: Documentation updates. + +## 1.3.2 + +### Changed + +- Enable compiler optimization for the `sprintf` function. + +## 1.3.1 + +### Changed + +- Add `Accept: application/json` header in request to fix incompatibility with 3rd party providers + +## 1.3.0 + +### Added + +- AWS api-change: Added `us-isob-east-1` region. + +## 1.2.1 + +### Changed + +- AWS enhancement: Documentation updates. + +## 1.2.0 + +### Added + +- AWS api-change: Added InvalidSourceException in put operations + +## 1.1.1 + +### Changed + +- AWS enhancement: Documentation updates. + +## 1.1.0 + +### Added + +- Avoid overriding the exception message with the raw message + +### Changed + +- Improve parameter type and return type in phpdoc + +## 1.0.0 + +### Added + +- AWS api-change: Added `us-iso-west-1` region + +## 0.1.1 + +### Added + +- AWS api-change: Use specific configuration for `us` regions + ## 0.1.0 First version diff --git a/composer.json b/composer.json index 8ae8f2c..bd6472d 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,8 @@ { "name": "async-aws/firehose", - "type": "library", "description": "Firehose client, part of the AWS SDK provided by AsyncAws.", + "license": "MIT", + "type": "library", "keywords": [ "aws", "amazon", @@ -9,18 +10,12 @@ "async-aws", "firehose" ], - "license": "MIT", "require": { "php": "^7.2.5 || ^8.0", "ext-filter": "*", "ext-json": "*", "async-aws/core": "^1.9" }, - "extra": { - "branch-alias": { - "dev-master": "0.1-dev" - } - }, "autoload": { "psr-4": { "AsyncAws\\Firehose\\": "src" @@ -30,5 +25,10 @@ "psr-4": { "AsyncAws\\Firehose\\Tests\\": "tests/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } } } diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index 94e6415..6b39b70 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -3,19 +3,10 @@ namespace AsyncAws\Firehose\Exception; use AsyncAws\Core\Exception\Http\ClientException; -use Symfony\Contracts\HttpClient\ResponseInterface; /** * The specified input parameter has a value that is not valid. */ final class InvalidArgumentException extends ClientException { - protected function populateResult(ResponseInterface $response): void - { - $data = $response->toArray(false); - - if (null !== $v = (isset($data['message']) ? (string) $data['message'] : null)) { - $this->message = $v; - } - } } diff --git a/src/Exception/InvalidKMSResourceException.php b/src/Exception/InvalidKMSResourceException.php index d018a0c..90bff4b 100644 --- a/src/Exception/InvalidKMSResourceException.php +++ b/src/Exception/InvalidKMSResourceException.php @@ -3,24 +3,12 @@ namespace AsyncAws\Firehose\Exception; use AsyncAws\Core\Exception\Http\ClientException; -use Symfony\Contracts\HttpClient\ResponseInterface; /** - * Kinesis Data Firehose throws this exception when an attempt to put records or to start or stop delivery stream - * encryption fails. This happens when the KMS service throws one of the following exception types: - * `AccessDeniedException`, `InvalidStateException`, `DisabledException`, or `NotFoundException`. + * Firehose throws this exception when an attempt to put records or to start or stop Firehose stream encryption fails. + * This happens when the KMS service throws one of the following exception types: `AccessDeniedException`, + * `InvalidStateException`, `DisabledException`, or `NotFoundException`. */ final class InvalidKMSResourceException extends ClientException { - protected function populateResult(ResponseInterface $response): void - { - $data = $response->toArray(false); - - if (null !== $v = (isset($data['code']) ? (string) $data['code'] : null)) { - $this->code = $v; - } - if (null !== $v = (isset($data['message']) ? (string) $data['message'] : null)) { - $this->message = $v; - } - } } diff --git a/src/Exception/InvalidSourceException.php b/src/Exception/InvalidSourceException.php new file mode 100644 index 0000000..5f583e1 --- /dev/null +++ b/src/Exception/InvalidSourceException.php @@ -0,0 +1,12 @@ +toArray(false); - - if (null !== $v = (isset($data['message']) ? (string) $data['message'] : null)) { - $this->message = $v; - } - } } diff --git a/src/Exception/ServiceUnavailableException.php b/src/Exception/ServiceUnavailableException.php index 2de2343..4c9e5d3 100644 --- a/src/Exception/ServiceUnavailableException.php +++ b/src/Exception/ServiceUnavailableException.php @@ -3,23 +3,14 @@ namespace AsyncAws\Firehose\Exception; use AsyncAws\Core\Exception\Http\ClientException; -use Symfony\Contracts\HttpClient\ResponseInterface; /** * The service is unavailable. Back off and retry the operation. If you continue to see the exception, throughput limits - * for the delivery stream may have been exceeded. For more information about limits and how to request an increase, see - * Amazon Kinesis Data Firehose Limits. + * for the Firehose stream may have been exceeded. For more information about limits and how to request an increase, see + * Amazon Firehose Limits [^1]. * - * @see https://docs.aws.amazon.com/firehose/latest/dev/limits.html + * [^1]: https://docs.aws.amazon.com/firehose/latest/dev/limits.html */ final class ServiceUnavailableException extends ClientException { - protected function populateResult(ResponseInterface $response): void - { - $data = $response->toArray(false); - - if (null !== $v = (isset($data['message']) ? (string) $data['message'] : null)) { - $this->message = $v; - } - } } diff --git a/src/FirehoseClient.php b/src/FirehoseClient.php index 938f425..7e21193 100644 --- a/src/FirehoseClient.php +++ b/src/FirehoseClient.php @@ -9,6 +9,7 @@ use AsyncAws\Core\RequestContext; use AsyncAws\Firehose\Exception\InvalidArgumentException; use AsyncAws\Firehose\Exception\InvalidKMSResourceException; +use AsyncAws\Firehose\Exception\InvalidSourceException; use AsyncAws\Firehose\Exception\ResourceNotFoundException; use AsyncAws\Firehose\Exception\ServiceUnavailableException; use AsyncAws\Firehose\Input\PutRecordBatchInput; @@ -20,8 +21,49 @@ class FirehoseClient extends AbstractApi { /** - * Writes a single data record into an Amazon Kinesis Data Firehose delivery stream. To write multiple data records into - * a delivery stream, use PutRecordBatch. Applications using these operations are referred to as producers. + * Writes a single data record into an Firehose stream. To write multiple data records into a Firehose stream, use + * PutRecordBatch. Applications using these operations are referred to as producers. + * + * By default, each Firehose stream can take in up to 2,000 transactions per second, 5,000 records per second, or 5 MB + * per second. If you use PutRecord and PutRecordBatch, the limits are an aggregate across these two operations for each + * Firehose stream. For more information about limits and how to request an increase, see Amazon Firehose Limits [^1]. + * + * Firehose accumulates and publishes a particular metric for a customer account in one minute intervals. It is possible + * that the bursts of incoming bytes/records ingested to a Firehose stream last only for a few seconds. Due to this, the + * actual spikes in the traffic might not be fully visible in the customer's 1 minute CloudWatch metrics. + * + * You must specify the name of the Firehose stream and the data record when using PutRecord. The data record consists + * of a data blob that can be up to 1,000 KiB in size, and any kind of data. For example, it can be a segment from a log + * file, geographic location data, website clickstream data, and so on. + * + * For multi record de-aggregation, you can not put more than 500 records even if the data blob length is less than 1000 + * KiB. If you include more than 500 records, the request succeeds but the record de-aggregation doesn't work as + * expected and transformation lambda is invoked with the complete base64 encoded data blob instead of de-aggregated + * base64 decoded records. + * + * Firehose buffers records before delivering them to the destination. To disambiguate the data blobs at the + * destination, a common solution is to use delimiters in the data, such as a newline (`\n`) or some other character + * unique within the data. This allows the consumer application to parse individual data items when reading the data + * from the destination. + * + * The `PutRecord` operation returns a `RecordId`, which is a unique string assigned to each record. Producer + * applications can use this ID for purposes such as auditability and investigation. + * + * If the `PutRecord` operation throws a `ServiceUnavailableException`, the API is automatically reinvoked (retried) 3 + * times. If the exception persists, it is possible that the throughput limits have been exceeded for the Firehose + * stream. + * + * Re-invoking the Put API operations (for example, PutRecord and PutRecordBatch) can result in data duplicates. For + * larger data assets, allow for a longer time out before retrying Put API operations. + * + * Data records sent to Firehose are stored for 24 hours from the time they are added to a Firehose stream as it tries + * to send the records to the destination. If the destination is unreachable for more than 24 hours, the data is no + * longer available. + * + * ! Don't concatenate two or more base64 strings to form the data fields of your records. Instead, concatenate the raw + * ! data, then perform base64 encoding. + * + * [^1]: https://docs.aws.amazon.com/firehose/latest/dev/limits.html * * @see https://docs.aws.amazon.com/firehose/latest/APIReference/API_PutRecord.html * @see https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-firehose-2015-08-04.html#putrecord @@ -29,21 +71,23 @@ class FirehoseClient extends AbstractApi * @param array{ * DeliveryStreamName: string, * Record: Record|array, - * @region?: string, + * '@region'?: string|null, * }|PutRecordInput $input * - * @throws ResourceNotFoundException * @throws InvalidArgumentException * @throws InvalidKMSResourceException + * @throws InvalidSourceException + * @throws ResourceNotFoundException * @throws ServiceUnavailableException */ public function putRecord($input): PutRecordOutput { $input = PutRecordInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'PutRecord', 'region' => $input->getRegion(), 'exceptionMapping' => [ - 'ResourceNotFoundException' => ResourceNotFoundException::class, 'InvalidArgumentException' => InvalidArgumentException::class, 'InvalidKMSResourceException' => InvalidKMSResourceException::class, + 'InvalidSourceException' => InvalidSourceException::class, + 'ResourceNotFoundException' => ResourceNotFoundException::class, 'ServiceUnavailableException' => ServiceUnavailableException::class, ]])); @@ -51,31 +95,90 @@ public function putRecord($input): PutRecordOutput } /** - * Writes multiple data records into a delivery stream in a single call, which can achieve higher throughput per - * producer than when writing single records. To write single data records into a delivery stream, use PutRecord. + * Writes multiple data records into a Firehose stream in a single call, which can achieve higher throughput per + * producer than when writing single records. To write single data records into a Firehose stream, use PutRecord. * Applications using these operations are referred to as producers. * + * Firehose accumulates and publishes a particular metric for a customer account in one minute intervals. It is possible + * that the bursts of incoming bytes/records ingested to a Firehose stream last only for a few seconds. Due to this, the + * actual spikes in the traffic might not be fully visible in the customer's 1 minute CloudWatch metrics. + * + * For information about service quota, see Amazon Firehose Quota [^1]. + * + * Each PutRecordBatch request supports up to 500 records. Each record in the request can be as large as 1,000 KB + * (before base64 encoding), up to a limit of 4 MB for the entire request. These limits cannot be changed. + * + * You must specify the name of the Firehose stream and the data record when using PutRecord. The data record consists + * of a data blob that can be up to 1,000 KB in size, and any kind of data. For example, it could be a segment from a + * log file, geographic location data, website clickstream data, and so on. + * + * For multi record de-aggregation, you can not put more than 500 records even if the data blob length is less than 1000 + * KiB. If you include more than 500 records, the request succeeds but the record de-aggregation doesn't work as + * expected and transformation lambda is invoked with the complete base64 encoded data blob instead of de-aggregated + * base64 decoded records. + * + * Firehose buffers records before delivering them to the destination. To disambiguate the data blobs at the + * destination, a common solution is to use delimiters in the data, such as a newline (`\n`) or some other character + * unique within the data. This allows the consumer application to parse individual data items when reading the data + * from the destination. + * + * The PutRecordBatch response includes a count of failed records, `FailedPutCount`, and an array of responses, + * `RequestResponses`. Even if the PutRecordBatch call succeeds, the value of `FailedPutCount` may be greater than 0, + * indicating that there are records for which the operation didn't succeed. Each entry in the `RequestResponses` array + * provides additional information about the processed record. It directly correlates with a record in the request array + * using the same ordering, from the top to the bottom. The response array always includes the same number of records as + * the request array. `RequestResponses` includes both successfully and unsuccessfully processed records. Firehose tries + * to process all records in each PutRecordBatch request. A single record failure does not stop the processing of + * subsequent records. + * + * A successfully processed record includes a `RecordId` value, which is unique for the record. An unsuccessfully + * processed record includes `ErrorCode` and `ErrorMessage` values. `ErrorCode` reflects the type of error, and is one + * of the following values: `ServiceUnavailableException` or `InternalFailure`. `ErrorMessage` provides more detailed + * information about the error. + * + * If there is an internal server error or a timeout, the write might have completed or it might have failed. If + * `FailedPutCount` is greater than 0, retry the request, resending only those records that might have failed + * processing. This minimizes the possible duplicate records and also reduces the total bytes sent (and corresponding + * charges). We recommend that you handle any duplicates at the destination. + * + * If PutRecordBatch throws `ServiceUnavailableException`, the API is automatically reinvoked (retried) 3 times. If the + * exception persists, it is possible that the throughput limits have been exceeded for the Firehose stream. + * + * Re-invoking the Put API operations (for example, PutRecord and PutRecordBatch) can result in data duplicates. For + * larger data assets, allow for a longer time out before retrying Put API operations. + * + * Data records sent to Firehose are stored for 24 hours from the time they are added to a Firehose stream as it + * attempts to send the records to the destination. If the destination is unreachable for more than 24 hours, the data + * is no longer available. + * + * ! Don't concatenate two or more base64 strings to form the data fields of your records. Instead, concatenate the raw + * ! data, then perform base64 encoding. + * + * [^1]: https://docs.aws.amazon.com/firehose/latest/dev/limits.html + * * @see https://docs.aws.amazon.com/firehose/latest/APIReference/API_PutRecordBatch.html * @see https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-firehose-2015-08-04.html#putrecordbatch * * @param array{ * DeliveryStreamName: string, - * Records: Record[], - * @region?: string, + * Records: array, + * '@region'?: string|null, * }|PutRecordBatchInput $input * - * @throws ResourceNotFoundException * @throws InvalidArgumentException * @throws InvalidKMSResourceException + * @throws InvalidSourceException + * @throws ResourceNotFoundException * @throws ServiceUnavailableException */ public function putRecordBatch($input): PutRecordBatchOutput { $input = PutRecordBatchInput::create($input); $response = $this->getResponse($input->request(), new RequestContext(['operation' => 'PutRecordBatch', 'region' => $input->getRegion(), 'exceptionMapping' => [ - 'ResourceNotFoundException' => ResourceNotFoundException::class, 'InvalidArgumentException' => InvalidArgumentException::class, 'InvalidKMSResourceException' => InvalidKMSResourceException::class, + 'InvalidSourceException' => InvalidSourceException::class, + 'ResourceNotFoundException' => ResourceNotFoundException::class, 'ServiceUnavailableException' => ServiceUnavailableException::class, ]])); @@ -102,32 +205,31 @@ protected function getEndpointMetadata(?string $region): array 'signService' => 'firehose', 'signVersions' => ['v4'], ]; - case 'us-gov-east-1': - case 'us-gov-west-1': + case 'fips-us-east-1': return [ - 'endpoint' => "https://firehose.$region.amazonaws.com", - 'signRegion' => $region, + 'endpoint' => 'https://firehose-fips.us-east-1.amazonaws.com', + 'signRegion' => 'us-east-1', 'signService' => 'firehose', 'signVersions' => ['v4'], ]; - case 'us-iso-east-1': + case 'fips-us-east-2': return [ - 'endpoint' => "https://firehose.$region.c2s.ic.gov", - 'signRegion' => $region, + 'endpoint' => 'https://firehose-fips.us-east-2.amazonaws.com', + 'signRegion' => 'us-east-2', 'signService' => 'firehose', 'signVersions' => ['v4'], ]; - case 'fips-us-east-1': + case 'fips-us-west-1': return [ - 'endpoint' => 'https://firehose-fips.us-east-1.amazonaws.com', - 'signRegion' => 'us-east-1', + 'endpoint' => 'https://firehose-fips.us-west-1.amazonaws.com', + 'signRegion' => 'us-west-1', 'signService' => 'firehose', 'signVersions' => ['v4'], ]; - case 'fips-us-east-2': + case 'fips-us-west-2': return [ - 'endpoint' => 'https://firehose-fips.us-east-2.amazonaws.com', - 'signRegion' => 'us-east-2', + 'endpoint' => 'https://firehose-fips.us-west-2.amazonaws.com', + 'signRegion' => 'us-west-2', 'signService' => 'firehose', 'signVersions' => ['v4'], ]; @@ -145,17 +247,33 @@ protected function getEndpointMetadata(?string $region): array 'signService' => 'firehose', 'signVersions' => ['v4'], ]; - case 'fips-us-west-1': + case 'us-iso-east-1': + case 'us-iso-west-1': return [ - 'endpoint' => 'https://firehose-fips.us-west-1.amazonaws.com', - 'signRegion' => 'us-west-1', + 'endpoint' => "https://firehose.$region.c2s.ic.gov", + 'signRegion' => $region, 'signService' => 'firehose', 'signVersions' => ['v4'], ]; - case 'fips-us-west-2': + case 'us-isof-east-1': + case 'us-isof-south-1': return [ - 'endpoint' => 'https://firehose-fips.us-west-2.amazonaws.com', - 'signRegion' => 'us-west-2', + 'endpoint' => "https://firehose.$region.csp.hci.ic.gov", + 'signRegion' => $region, + 'signService' => 'firehose', + 'signVersions' => ['v4'], + ]; + case 'eu-isoe-west-1': + return [ + 'endpoint' => 'https://firehose.eu-isoe-west-1.cloud.adc-e.uk', + 'signRegion' => 'eu-isoe-west-1', + 'signService' => 'firehose', + 'signVersions' => ['v4'], + ]; + case 'us-isob-east-1': + return [ + 'endpoint' => 'https://firehose.us-isob-east-1.sc2s.sgov.gov', + 'signRegion' => 'us-isob-east-1', 'signService' => 'firehose', 'signVersions' => ['v4'], ]; diff --git a/src/Input/PutRecordBatchInput.php b/src/Input/PutRecordBatchInput.php index 7668eec..1c7faca 100644 --- a/src/Input/PutRecordBatchInput.php +++ b/src/Input/PutRecordBatchInput.php @@ -11,7 +11,7 @@ final class PutRecordBatchInput extends Input { /** - * The name of the delivery stream. + * The name of the Firehose stream. * * @required * @@ -31,8 +31,8 @@ final class PutRecordBatchInput extends Input /** * @param array{ * DeliveryStreamName?: string, - * Records?: Record[], - * @region?: string, + * Records?: array, + * '@region'?: string|null, * } $input */ public function __construct(array $input = []) @@ -42,6 +42,13 @@ public function __construct(array $input = []) parent::__construct($input); } + /** + * @param array{ + * DeliveryStreamName?: string, + * Records?: array, + * '@region'?: string|null, + * }|PutRecordBatchInput $input + */ public static function create($input): self { return $input instanceof self ? $input : new self($input); @@ -69,6 +76,7 @@ public function request(): Request $headers = [ 'Content-Type' => 'application/x-amz-json-1.1', 'X-Amz-Target' => 'Firehose_20150804.PutRecordBatch', + 'Accept' => 'application/json', ]; // Prepare query @@ -106,11 +114,11 @@ private function requestBody(): array { $payload = []; if (null === $v = $this->deliveryStreamName) { - throw new InvalidArgument(sprintf('Missing parameter "DeliveryStreamName" for "%s". The value cannot be null.', __CLASS__)); + throw new InvalidArgument(\sprintf('Missing parameter "DeliveryStreamName" for "%s". The value cannot be null.', __CLASS__)); } $payload['DeliveryStreamName'] = $v; if (null === $v = $this->records) { - throw new InvalidArgument(sprintf('Missing parameter "Records" for "%s". The value cannot be null.', __CLASS__)); + throw new InvalidArgument(\sprintf('Missing parameter "Records" for "%s". The value cannot be null.', __CLASS__)); } $index = -1; diff --git a/src/Input/PutRecordInput.php b/src/Input/PutRecordInput.php index f6bef97..7f61c65 100644 --- a/src/Input/PutRecordInput.php +++ b/src/Input/PutRecordInput.php @@ -11,7 +11,7 @@ final class PutRecordInput extends Input { /** - * The name of the delivery stream. + * The name of the Firehose stream. * * @required * @@ -32,7 +32,7 @@ final class PutRecordInput extends Input * @param array{ * DeliveryStreamName?: string, * Record?: Record|array, - * @region?: string, + * '@region'?: string|null, * } $input */ public function __construct(array $input = []) @@ -42,6 +42,13 @@ public function __construct(array $input = []) parent::__construct($input); } + /** + * @param array{ + * DeliveryStreamName?: string, + * Record?: Record|array, + * '@region'?: string|null, + * }|PutRecordInput $input + */ public static function create($input): self { return $input instanceof self ? $input : new self($input); @@ -66,6 +73,7 @@ public function request(): Request $headers = [ 'Content-Type' => 'application/x-amz-json-1.1', 'X-Amz-Target' => 'Firehose_20150804.PutRecord', + 'Accept' => 'application/json', ]; // Prepare query @@ -100,11 +108,11 @@ private function requestBody(): array { $payload = []; if (null === $v = $this->deliveryStreamName) { - throw new InvalidArgument(sprintf('Missing parameter "DeliveryStreamName" for "%s". The value cannot be null.', __CLASS__)); + throw new InvalidArgument(\sprintf('Missing parameter "DeliveryStreamName" for "%s". The value cannot be null.', __CLASS__)); } $payload['DeliveryStreamName'] = $v; if (null === $v = $this->record) { - throw new InvalidArgument(sprintf('Missing parameter "Record" for "%s". The value cannot be null.', __CLASS__)); + throw new InvalidArgument(\sprintf('Missing parameter "Record" for "%s". The value cannot be null.', __CLASS__)); } $payload['Record'] = $v->requestBody(); diff --git a/src/Result/PutRecordBatchOutput.php b/src/Result/PutRecordBatchOutput.php index 9f9c7ce..66016c7 100644 --- a/src/Result/PutRecordBatchOutput.php +++ b/src/Result/PutRecordBatchOutput.php @@ -11,17 +11,23 @@ class PutRecordBatchOutput extends Result /** * The number of records that might have failed processing. This number might be greater than 0 even if the * PutRecordBatch call succeeds. Check `FailedPutCount` to determine whether there are records that you need to resend. + * + * @var int */ private $failedPutCount; /** * Indicates whether server-side encryption (SSE) was enabled during this operation. + * + * @var bool|null */ private $encrypted; /** * The results array. For each record, the index of the response element is the same as the index used in the request * array. + * + * @var PutRecordBatchResponseEntry[] */ private $requestResponses; @@ -55,7 +61,16 @@ protected function populateResult(Response $response): void $this->failedPutCount = (int) $data['FailedPutCount']; $this->encrypted = isset($data['Encrypted']) ? filter_var($data['Encrypted'], \FILTER_VALIDATE_BOOLEAN) : null; - $this->requestResponses = $this->populateResultPutRecordBatchResponseEntryList($data['RequestResponses']); + $this->requestResponses = $this->populateResultPutRecordBatchResponseEntryList($data['RequestResponses'] ?? []); + } + + private function populateResultPutRecordBatchResponseEntry(array $json): PutRecordBatchResponseEntry + { + return new PutRecordBatchResponseEntry([ + 'RecordId' => isset($json['RecordId']) ? (string) $json['RecordId'] : null, + 'ErrorCode' => isset($json['ErrorCode']) ? (string) $json['ErrorCode'] : null, + 'ErrorMessage' => isset($json['ErrorMessage']) ? (string) $json['ErrorMessage'] : null, + ]); } /** @@ -65,11 +80,7 @@ private function populateResultPutRecordBatchResponseEntryList(array $json): arr { $items = []; foreach ($json as $item) { - $items[] = new PutRecordBatchResponseEntry([ - 'RecordId' => isset($item['RecordId']) ? (string) $item['RecordId'] : null, - 'ErrorCode' => isset($item['ErrorCode']) ? (string) $item['ErrorCode'] : null, - 'ErrorMessage' => isset($item['ErrorMessage']) ? (string) $item['ErrorMessage'] : null, - ]); + $items[] = $this->populateResultPutRecordBatchResponseEntry($item); } return $items; diff --git a/src/Result/PutRecordOutput.php b/src/Result/PutRecordOutput.php index ed95ce5..4a872c9 100644 --- a/src/Result/PutRecordOutput.php +++ b/src/Result/PutRecordOutput.php @@ -9,11 +9,15 @@ class PutRecordOutput extends Result { /** * The ID of the record. + * + * @var string */ private $recordId; /** * Indicates whether server-side encryption (SSE) was enabled during this operation. + * + * @var bool|null */ private $encrypted; diff --git a/src/ValueObject/PutRecordBatchResponseEntry.php b/src/ValueObject/PutRecordBatchResponseEntry.php index a688c6f..54b8993 100644 --- a/src/ValueObject/PutRecordBatchResponseEntry.php +++ b/src/ValueObject/PutRecordBatchResponseEntry.php @@ -4,23 +4,29 @@ /** * Contains the result for an individual record from a PutRecordBatch request. If the record is successfully added to - * your delivery stream, it receives a record ID. If the record fails to be added to your delivery stream, the result + * your Firehose stream, it receives a record ID. If the record fails to be added to your Firehose stream, the result * includes an error code and an error message. */ final class PutRecordBatchResponseEntry { /** * The ID of the record. + * + * @var string|null */ private $recordId; /** * The error code for an individual record result. + * + * @var string|null */ private $errorCode; /** * The error message for an individual record result. + * + * @var string|null */ private $errorMessage; @@ -38,6 +44,13 @@ public function __construct(array $input) $this->errorMessage = $input['ErrorMessage'] ?? null; } + /** + * @param array{ + * RecordId?: null|string, + * ErrorCode?: null|string, + * ErrorMessage?: null|string, + * }|PutRecordBatchResponseEntry $input + */ public static function create($input): self { return $input instanceof self ? $input : new self($input); diff --git a/src/ValueObject/Record.php b/src/ValueObject/Record.php index dbb0ed0..5241ea1 100644 --- a/src/ValueObject/Record.php +++ b/src/ValueObject/Record.php @@ -5,13 +5,15 @@ use AsyncAws\Core\Exception\InvalidArgument; /** - * The record. + * The unit of data in a Firehose stream. */ final class Record { /** * The data blob, which is base64-encoded when the blob is serialized. The maximum size of the data blob, before * base64-encoding, is 1,000 KiB. + * + * @var string */ private $data; @@ -22,9 +24,14 @@ final class Record */ public function __construct(array $input) { - $this->data = $input['Data'] ?? null; + $this->data = $input['Data'] ?? $this->throwException(new InvalidArgument('Missing required field "Data".')); } + /** + * @param array{ + * Data: string, + * }|Record $input + */ public static function create($input): self { return $input instanceof self ? $input : new self($input); @@ -41,11 +48,17 @@ public function getData(): string public function requestBody(): array { $payload = []; - if (null === $v = $this->data) { - throw new InvalidArgument(sprintf('Missing parameter "Data" for "%s". The value cannot be null.', __CLASS__)); - } + $v = $this->data; $payload['Data'] = base64_encode($v); return $payload; } + + /** + * @return never + */ + private function throwException(\Throwable $exception) + { + throw $exception; + } } diff --git a/tests/Unit/Input/PutRecordBatchInputTest.php b/tests/Unit/Input/PutRecordBatchInputTest.php index 91cde75..ec04c79 100644 --- a/tests/Unit/Input/PutRecordBatchInputTest.php +++ b/tests/Unit/Input/PutRecordBatchInputTest.php @@ -27,6 +27,7 @@ public function testRequest(): void POST / HTTP/1.0 Content-Type: application/x-amz-json-1.1 x-amz-target: Firehose_20150804.PutRecordBatch + Accept: application/json { "DeliveryStreamName": "streamfoo", diff --git a/tests/Unit/Input/PutRecordInputTest.php b/tests/Unit/Input/PutRecordInputTest.php index 1f5742c..3846333 100644 --- a/tests/Unit/Input/PutRecordInputTest.php +++ b/tests/Unit/Input/PutRecordInputTest.php @@ -22,6 +22,7 @@ public function testRequest(): void POST / HTTP/1.0 Content-Type: application/x-amz-json-1.1 x-amz-target: Firehose_20150804.PutRecord + Accept: application/json { "DeliveryStreamName": "streamfoo",