diff --git a/generated/src/aws-cpp-sdk-dynamodb/include/aws/dynamodb/model/pagination/ScanPaginationTraits.h b/generated/src/aws-cpp-sdk-dynamodb/include/aws/dynamodb/model/pagination/ScanPaginationTraits.h new file mode 100644 index 00000000000..0cd157c097e --- /dev/null +++ b/generated/src/aws-cpp-sdk-dynamodb/include/aws/dynamodb/model/pagination/ScanPaginationTraits.h @@ -0,0 +1,31 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once +#include +#include +#include +#include + +namespace Aws { +namespace DynamoDB { +namespace Pagination { + +struct ScanPaginationTraits { + using RequestType = Model::ScanRequest; + using ResultType = Model::ScanResult; + using OutcomeType = Model::ScanOutcome; + using ClientType = DynamoDBClient; + + static OutcomeType Invoke(ClientType& client, const RequestType& request) { return client.Scan(request); } + + static bool HasMoreResults(const ResultType& result) { return !result.GetLastEvaluatedKey().empty(); } + + static void SetNextRequest(const ResultType& result, RequestType& request) { request.SetExclusiveStartKey(result.GetLastEvaluatedKey()); } +}; + +} // namespace Pagination +} // namespace DynamoDB +} // namespace Aws diff --git a/generated/src/aws-cpp-sdk-ec2/include/aws/ec2/model/pagination/DescribeSpotPriceHistoryPaginationTraits.h b/generated/src/aws-cpp-sdk-ec2/include/aws/ec2/model/pagination/DescribeSpotPriceHistoryPaginationTraits.h new file mode 100644 index 00000000000..871357593c2 --- /dev/null +++ b/generated/src/aws-cpp-sdk-ec2/include/aws/ec2/model/pagination/DescribeSpotPriceHistoryPaginationTraits.h @@ -0,0 +1,31 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once +#include +#include +#include +#include + +namespace Aws { +namespace EC2 { +namespace Pagination { + +struct DescribeSpotPriceHistoryPaginationTraits { + using RequestType = Model::DescribeSpotPriceHistoryRequest; + using ResultType = Model::DescribeSpotPriceHistoryResponse; + using OutcomeType = Model::DescribeSpotPriceHistoryOutcome; + using ClientType = EC2Client; + + static OutcomeType Invoke(ClientType& client, const RequestType& request) { return client.DescribeSpotPriceHistory(request); } + + static bool HasMoreResults(const ResultType& result) { return !result.GetNextToken().empty(); } + + static void SetNextRequest(const ResultType& result, RequestType& request) { request.SetNextToken(result.GetNextToken()); } +}; + +} // namespace Pagination +} // namespace EC2 +} // namespace Aws diff --git a/generated/src/aws-cpp-sdk-s3/include/aws/s3/S3ClientPagination.h b/generated/src/aws-cpp-sdk-s3/include/aws/s3/S3ClientPagination.h new file mode 100644 index 00000000000..9afd5a3e990 --- /dev/null +++ b/generated/src/aws-cpp-sdk-s3/include/aws/s3/S3ClientPagination.h @@ -0,0 +1,26 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace Aws { +namespace S3 { + +using ListBucketsPaginator = + Aws::Utils::Pagination::PagePaginator; +using ListDirectoryBucketsPaginator = + Aws::Utils::Pagination::PagePaginator; +using ListObjectsV2Paginator = + Aws::Utils::Pagination::PagePaginator; +using ListPartsPaginator = Aws::Utils::Pagination::PagePaginator; + +} // namespace S3 +} // namespace Aws diff --git a/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListBucketsPaginationTraits.h b/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListBucketsPaginationTraits.h new file mode 100644 index 00000000000..a3ab75a45a2 --- /dev/null +++ b/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListBucketsPaginationTraits.h @@ -0,0 +1,33 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once +#include +#include +#include +#include + +namespace Aws { +namespace S3 { +namespace Pagination { + +struct ListBucketsPaginationTraits { + using RequestType = Model::ListBucketsRequest; + using ResultType = Model::ListBucketsResult; + using OutcomeType = Model::ListBucketsOutcome; + using ClientType = S3Client; + + static OutcomeType Invoke(ClientType& client, const RequestType& request) { return client.ListBuckets(request); } + + static bool HasMoreResults(const ResultType& result) { return !result.GetContinuationToken().empty(); } + + static void SetNextRequest(const ResultType& result, RequestType& request) { + request.SetContinuationToken(result.GetContinuationToken()); + } +}; + +} // namespace Pagination +} // namespace S3 +} // namespace Aws diff --git a/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListDirectoryBucketsPaginationTraits.h b/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListDirectoryBucketsPaginationTraits.h new file mode 100644 index 00000000000..f7f6de8b6fc --- /dev/null +++ b/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListDirectoryBucketsPaginationTraits.h @@ -0,0 +1,33 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once +#include +#include +#include +#include + +namespace Aws { +namespace S3 { +namespace Pagination { + +struct ListDirectoryBucketsPaginationTraits { + using RequestType = Model::ListDirectoryBucketsRequest; + using ResultType = Model::ListDirectoryBucketsResult; + using OutcomeType = Model::ListDirectoryBucketsOutcome; + using ClientType = S3Client; + + static OutcomeType Invoke(ClientType& client, const RequestType& request) { return client.ListDirectoryBuckets(request); } + + static bool HasMoreResults(const ResultType& result) { return !result.GetContinuationToken().empty(); } + + static void SetNextRequest(const ResultType& result, RequestType& request) { + request.SetContinuationToken(result.GetContinuationToken()); + } +}; + +} // namespace Pagination +} // namespace S3 +} // namespace Aws diff --git a/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListObjectsV2PaginationTraits.h b/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListObjectsV2PaginationTraits.h new file mode 100644 index 00000000000..718d6333778 --- /dev/null +++ b/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListObjectsV2PaginationTraits.h @@ -0,0 +1,33 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once +#include +#include +#include +#include + +namespace Aws { +namespace S3 { +namespace Pagination { + +struct ListObjectsV2PaginationTraits { + using RequestType = Model::ListObjectsV2Request; + using ResultType = Model::ListObjectsV2Result; + using OutcomeType = Model::ListObjectsV2Outcome; + using ClientType = S3Client; + + static OutcomeType Invoke(ClientType& client, const RequestType& request) { return client.ListObjectsV2(request); } + + static bool HasMoreResults(const ResultType& result) { return !result.GetNextContinuationToken().empty(); } + + static void SetNextRequest(const ResultType& result, RequestType& request) { + request.SetContinuationToken(result.GetNextContinuationToken()); + } +}; + +} // namespace Pagination +} // namespace S3 +} // namespace Aws diff --git a/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListPartsPaginationTraits.h b/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListPartsPaginationTraits.h new file mode 100644 index 00000000000..dc6a20a510d --- /dev/null +++ b/generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListPartsPaginationTraits.h @@ -0,0 +1,33 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once +#include +#include +#include +#include + +namespace Aws { +namespace S3 { +namespace Pagination { + +struct ListPartsPaginationTraits { + using RequestType = Model::ListPartsRequest; + using ResultType = Model::ListPartsResult; + using OutcomeType = Model::ListPartsOutcome; + using ClientType = S3Client; + + static OutcomeType Invoke(ClientType& client, const RequestType& request) { return client.ListParts(request); } + + static bool HasMoreResults(const ResultType& result) { return result.GetNextPartNumberMarker() != 0; } + + static void SetNextRequest(const ResultType& result, RequestType& request) { + request.SetPartNumberMarker(result.GetNextPartNumberMarker()); + } +}; + +} // namespace Pagination +} // namespace S3 +} // namespace Aws diff --git a/generated/tests/dynamodb-gen-tests/DynamoDBPaginationCompilationTests.cpp b/generated/tests/dynamodb-gen-tests/DynamoDBPaginationCompilationTests.cpp new file mode 100644 index 00000000000..c4e9ee0bb11 --- /dev/null +++ b/generated/tests/dynamodb-gen-tests/DynamoDBPaginationCompilationTests.cpp @@ -0,0 +1,27 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +// Header compilation test for DynamoDB pagination headers +// This test ensures all generated pagination headers compile successfully + +#include +#include +#include +#include +#include +#include +#include + +#include + +class DynamoDBPaginationCompilationTest : public Aws::Testing::AwsCppSdkGTestSuite +{ +}; + +TEST_F(DynamoDBPaginationCompilationTest, DynamoDBPaginationHeadersCompile) +{ + // Test passes if compilation succeeds + SUCCEED(); +} diff --git a/generated/tests/ec2-gen-tests/EC2PaginationCompilationTests.cpp b/generated/tests/ec2-gen-tests/EC2PaginationCompilationTests.cpp new file mode 100644 index 00000000000..9ec13d3291a --- /dev/null +++ b/generated/tests/ec2-gen-tests/EC2PaginationCompilationTests.cpp @@ -0,0 +1,184 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +// Header compilation test for EC2 pagination headers +// This test ensures all generated pagination headers compile successfully + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class EC2PaginationCompilationTest : public Aws::Testing::AwsCppSdkGTestSuite +{ +}; + +TEST_F(EC2PaginationCompilationTest, EC2PaginationHeadersCompile) +{ + // Test passes if compilation succeeds + SUCCEED(); +} diff --git a/generated/tests/s3-gen-tests/S3PaginationCompilationTests.cpp b/generated/tests/s3-gen-tests/S3PaginationCompilationTests.cpp new file mode 100644 index 00000000000..9ea1602aebc --- /dev/null +++ b/generated/tests/s3-gen-tests/S3PaginationCompilationTests.cpp @@ -0,0 +1,25 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +// Header compilation test for S3 pagination headers +// This test ensures all generated pagination headers compile successfully + +#include +#include +#include +#include +#include + +#include + +class S3PaginationCompilationTest : public Aws::Testing::AwsCppSdkGTestSuite +{ +}; + +TEST_F(S3PaginationCompilationTest, S3PaginationHeadersCompile) +{ + // Test passes if compilation succeeds + SUCCEED(); +} diff --git a/tests/aws-cpp-sdk-dynamodb-integration-tests/ScanPaginationIntegrationTest.cpp b/tests/aws-cpp-sdk-dynamodb-integration-tests/ScanPaginationIntegrationTest.cpp new file mode 100644 index 00000000000..a7dc27adcf8 --- /dev/null +++ b/tests/aws-cpp-sdk-dynamodb-integration-tests/ScanPaginationIntegrationTest.cpp @@ -0,0 +1,211 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Aws; +using namespace Aws::DynamoDB; +using namespace Aws::DynamoDB::Model; + +namespace { + static const char* ALLOCATION_TAG = "ScanPaginationTest"; + const size_t ITEM_COUNT = 19; + const Aws::String HASH_KEY_NAME = "id"; + const Aws::String ATTRIBUTE_FOO = "attribute_foo"; +} + +class ScanPaginationTest : public Aws::Testing::AwsCppSdkGTestSuite { +protected: + std::shared_ptr dynamoClient; + Aws::String tableName; + + void SetUp() override { + dynamoClient = Aws::MakeShared(ALLOCATION_TAG); + tableName = CalculateTableName("scan-paginator-test"); + + CreateTable(); + PutTestData(); + } + + void TearDown() override { + DeleteTableRequest deleteRequest; + deleteRequest.SetTableName(tableName); + dynamoClient->DeleteTable(deleteRequest); + } + + Aws::String CalculateTableName(const Aws::String& tablePrefix) { + return Aws::Testing::GetAwsResourcePrefix() + tablePrefix; + } + + void CreateTable() { + CreateTableRequest createRequest; + createRequest.SetTableName(tableName); + + KeySchemaElement keySchema; + keySchema.SetKeyType(KeyType::HASH); + keySchema.SetAttributeName(HASH_KEY_NAME); + createRequest.AddKeySchema(keySchema); + + AttributeDefinition attrDef; + attrDef.SetAttributeType(ScalarAttributeType::N); + attrDef.SetAttributeName(HASH_KEY_NAME); + createRequest.AddAttributeDefinitions(attrDef); + + ProvisionedThroughput throughput; + throughput.SetReadCapacityUnits(5); + throughput.SetWriteCapacityUnits(5); + createRequest.SetProvisionedThroughput(throughput); + + auto outcome = dynamoClient->CreateTable(createRequest); + AWS_ASSERT_SUCCESS(outcome); + + WaitUntilActive(tableName); + } + + DescribeTableResult WaitUntilActive(const Aws::String& tableNameParam) { + DescribeTableRequest describeTableRequest; + describeTableRequest.SetTableName(tableNameParam); + + DescribeTableOutcome outcome = dynamoClient->DescribeTable(describeTableRequest); + while (outcome.IsSuccess() && outcome.GetResult().GetTable().GetTableStatus() != TableStatus::ACTIVE) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + outcome = dynamoClient->DescribeTable(describeTableRequest); + } + + return outcome.GetResult(); + } + + void PutTestData() { + for (size_t i = 0; i < ITEM_COUNT; ++i) { + PutItemRequest putRequest; + putRequest.SetTableName(tableName); + + Aws::Map item; + item[HASH_KEY_NAME] = AttributeValue().SetN(Aws::Utils::StringUtils::to_string(i)); + item[ATTRIBUTE_FOO] = AttributeValue().SetN(Aws::Utils::StringUtils::to_string(i * 2)); + + putRequest.SetItem(item); + auto outcome = dynamoClient->PutItem(putRequest); + ASSERT_TRUE(outcome.IsSuccess()); + } + } +}; + +TEST_F(ScanPaginationTest, TestMultipleIterationOnResponses) { + ScanRequest request; + request.SetTableName(tableName); + request.SetConsistentRead(true); + request.SetLimit(2); + + size_t count = 0; + auto outcome = dynamoClient->Scan(request); + + // First iteration + while (outcome.IsSuccess()) { + const auto& result = outcome.GetResult(); + count += result.GetCount(); + + if (!Aws::DynamoDB::Pagination::ScanPaginationTraits::HasMoreResults(result)) { + break; + } + + Aws::DynamoDB::Pagination::ScanPaginationTraits::SetNextRequest(result, request); + outcome = dynamoClient->Scan(request); + } + + EXPECT_EQ(ITEM_COUNT, count); + + // Reset for second iteration + request = ScanRequest(); + request.SetTableName(tableName); + request.SetConsistentRead(true); + request.SetLimit(2); + + count = 0; + outcome = dynamoClient->Scan(request); + + // Second iteration + while (outcome.IsSuccess()) { + const auto& result = outcome.GetResult(); + count += result.GetCount(); + + if (!Aws::DynamoDB::Pagination::ScanPaginationTraits::HasMoreResults(result)) { + break; + } + + Aws::DynamoDB::Pagination::ScanPaginationTraits::SetNextRequest(result, request); + outcome = dynamoClient->Scan(request); + } + + EXPECT_EQ(ITEM_COUNT, count); +} + +TEST_F(ScanPaginationTest, TestPaginationWithLimit) { + ScanRequest request; + request.SetTableName(tableName); + request.SetConsistentRead(true); + request.SetLimit(3); + + size_t totalItems = 0; + size_t pageCount = 0; + auto outcome = dynamoClient->Scan(request); + + while (outcome.IsSuccess()) { + const auto& result = outcome.GetResult(); + totalItems += result.GetCount(); + pageCount++; + + if (!Aws::DynamoDB::Pagination::ScanPaginationTraits::HasMoreResults(result)) { + break; + } + + Aws::DynamoDB::Pagination::ScanPaginationTraits::SetNextRequest(result, request); + outcome = dynamoClient->Scan(request); + } + + EXPECT_EQ(ITEM_COUNT, totalItems); + EXPECT_GT(pageCount, 1u); // Should have multiple pages with limit=3 +} + +TEST_F(ScanPaginationTest, TestManualPaginationWithExclusiveStartKey) { + ScanRequest request; + request.SetTableName(tableName); + request.SetConsistentRead(true); + request.SetLimit(5); + + Aws::Vector> allItems; + + do { + auto outcome = dynamoClient->Scan(request); + ASSERT_TRUE(outcome.IsSuccess()); + + const auto& result = outcome.GetResult(); + for (const auto& item : result.GetItems()) { + allItems.push_back(item); + } + + if (!result.GetLastEvaluatedKey().empty()) { + request.SetExclusiveStartKey(result.GetLastEvaluatedKey()); + } else { + break; + } + } while (true); + + EXPECT_EQ(ITEM_COUNT, allItems.size()); +} \ No newline at end of file diff --git a/tests/aws-cpp-sdk-ec2-integration-tests/EC2PaginationIntegrationTest.cpp b/tests/aws-cpp-sdk-ec2-integration-tests/EC2PaginationIntegrationTest.cpp new file mode 100644 index 00000000000..ed129d874a9 --- /dev/null +++ b/tests/aws-cpp-sdk-ec2-integration-tests/EC2PaginationIntegrationTest.cpp @@ -0,0 +1,152 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Aws; +using namespace Aws::EC2; +using namespace Aws::EC2::Model; + +namespace { + static const char* ALLOCATION_TAG = "EC2PaginationTest"; +} + +class EC2PaginationTest : public Aws::Testing::AwsCppSdkGTestSuite { +protected: + std::shared_ptr ec2Client; + + void SetUp() override { + Aws::SDKOptions options; + Aws::InitAPI(options); + + Aws::Client::ClientConfiguration config; + config.region = "us-east-1"; + ec2Client = Aws::MakeShared(ALLOCATION_TAG, config); + } + + void TearDown() override { + Aws::SDKOptions options; + Aws::ShutdownAPI(options); + } +}; + +TEST_F(EC2PaginationTest, TestSpotPriceHistoryPagination) { + DescribeSpotPriceHistoryRequest request; + request.SetAvailabilityZone("us-east-1a"); + request.AddProductDescriptions("Linux/UNIX (Amazon VPC)"); + request.AddInstanceTypes(InstanceType::t1_micro); + request.SetStartTime(Aws::Utils::DateTime::Now() - std::chrono::milliseconds(1)); + request.SetMaxResults(10); + + size_t totalSpotPrices = 0; + auto outcome = ec2Client->DescribeSpotPriceHistory(request); + + while (outcome.IsSuccess()) { + const auto& result = outcome.GetResult(); + totalSpotPrices += result.GetSpotPriceHistory().size(); + + if (!Aws::EC2::Pagination::DescribeSpotPriceHistoryPaginationTraits::HasMoreResults(result)) { + break; + } + + Aws::EC2::Pagination::DescribeSpotPriceHistoryPaginationTraits::SetNextRequest(result, request); + outcome = ec2Client->DescribeSpotPriceHistory(request); + } + + ASSERT_TRUE(outcome.IsSuccess()); + // Should have at least one spot price entry + EXPECT_GE(totalSpotPrices, 1u); +} + +TEST_F(EC2PaginationTest, TestSpotPriceHistoryMultipleIteration) { + DescribeSpotPriceHistoryRequest request; + request.SetAvailabilityZone("us-east-1a"); + request.AddProductDescriptions("Linux/UNIX (Amazon VPC)"); + request.AddInstanceTypes(InstanceType::t1_micro); + request.SetStartTime(Aws::Utils::DateTime::Now() - std::chrono::milliseconds(1)); + request.SetMaxResults(5); + + // First iteration + size_t firstCount = 0; + auto outcome = ec2Client->DescribeSpotPriceHistory(request); + + while (outcome.IsSuccess()) { + const auto& result = outcome.GetResult(); + firstCount += result.GetSpotPriceHistory().size(); + + if (!Aws::EC2::Pagination::DescribeSpotPriceHistoryPaginationTraits::HasMoreResults(result)) { + break; + } + + Aws::EC2::Pagination::DescribeSpotPriceHistoryPaginationTraits::SetNextRequest(result, request); + outcome = ec2Client->DescribeSpotPriceHistory(request); + } + + ASSERT_TRUE(outcome.IsSuccess()); + + // Reset for second iteration + request = DescribeSpotPriceHistoryRequest(); + request.SetAvailabilityZone("us-east-1a"); + request.AddProductDescriptions("Linux/UNIX (Amazon VPC)"); + request.AddInstanceTypes(InstanceType::t1_micro); + request.SetStartTime(Aws::Utils::DateTime::Now() - std::chrono::milliseconds(1)); + request.SetMaxResults(5); + + // Second iteration + size_t secondCount = 0; + outcome = ec2Client->DescribeSpotPriceHistory(request); + + while (outcome.IsSuccess()) { + const auto& result = outcome.GetResult(); + secondCount += result.GetSpotPriceHistory().size(); + + if (!Aws::EC2::Pagination::DescribeSpotPriceHistoryPaginationTraits::HasMoreResults(result)) { + break; + } + + Aws::EC2::Pagination::DescribeSpotPriceHistoryPaginationTraits::SetNextRequest(result, request); + outcome = ec2Client->DescribeSpotPriceHistory(request); + } + + ASSERT_TRUE(outcome.IsSuccess()); + EXPECT_EQ(firstCount, secondCount); // Should get same results +} + +TEST_F(EC2PaginationTest, TestManualPaginationWithNextToken) { + DescribeSpotPriceHistoryRequest request; + request.SetAvailabilityZone("us-east-1a"); + request.AddProductDescriptions("Linux/UNIX (Amazon VPC)"); + request.AddInstanceTypes(InstanceType::t1_micro); + request.SetStartTime(Aws::Utils::DateTime::Now() - std::chrono::hours(24)); // Last 24 hours for more data + request.SetMaxResults(3); + + Aws::Vector allSpotPrices; + + do { + auto outcome = ec2Client->DescribeSpotPriceHistory(request); + ASSERT_TRUE(outcome.IsSuccess()); + + const auto& result = outcome.GetResult(); + for (const auto& spotPrice : result.GetSpotPriceHistory()) { + allSpotPrices.push_back(spotPrice); + } + + if (!result.GetNextToken().empty()) { + request.SetNextToken(result.GetNextToken()); + } else { + break; + } + } while (true); + + // Should have at least some spot price data + EXPECT_GE(allSpotPrices.size(), 1u); +} \ No newline at end of file diff --git a/tests/aws-cpp-sdk-s3-integration-tests/ListObjectsV2PaginationIntegrationTest.cpp b/tests/aws-cpp-sdk-s3-integration-tests/ListObjectsV2PaginationIntegrationTest.cpp new file mode 100644 index 00000000000..1e2bca8bf45 --- /dev/null +++ b/tests/aws-cpp-sdk-s3-integration-tests/ListObjectsV2PaginationIntegrationTest.cpp @@ -0,0 +1,261 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Aws; +using namespace Aws::S3; +using namespace Aws::S3::Model; + +namespace { + static const char* ALLOCATION_TAG = "ListObjectsV2PaginationTest"; + static const char* TEST_BUCKET_TAG = "IntegrationTestResource"; + static const int TIMEOUT_MAX = 20; + const size_t OBJECT_COUNT = 15; + const size_t MAX_KEYS = 3; +} + +class ListObjectsV2PaginationTest : public Aws::Testing::AwsCppSdkGTestSuite { +protected: + std::shared_ptr Client; + Aws::String bucketName; + Aws::String emptyBucketName; + + void SetUp() override { + Aws::Client::ClientConfiguration config; + config.region = Aws::Region::US_EAST_1; + config.scheme = Aws::Http::Scheme::HTTPS; + config.connectTimeoutMs = 30000; + config.requestTimeoutMs = 30000; + + Client = Aws::MakeShared(ALLOCATION_TAG, config); + + bucketName = CalculateBucketName("listobjectsv2paginationtest"); + emptyBucketName = CalculateBucketName("listobjectsv2emptytest"); + + CreateBucketRequest createRequest; + createRequest.SetBucket(bucketName); + createRequest.SetACL(BucketCannedACL::private_); + auto createOutcome = Client->CreateBucket(createRequest); + AWS_ASSERT_SUCCESS(createOutcome); + + createRequest.SetBucket(emptyBucketName); + createOutcome = Client->CreateBucket(createRequest); + AWS_ASSERT_SUCCESS(createOutcome); + + ASSERT_TRUE(WaitForBucketToPropagate(bucketName)); + ASSERT_TRUE(WaitForBucketToPropagate(emptyBucketName)); + TagTestBucket(bucketName); + TagTestBucket(emptyBucketName); + + // Create test objects + for (size_t i = 1; i <= OBJECT_COUNT; ++i) { + Aws::StringStream ss; + ss << "key-" << std::setfill('0') << std::setw(2) << i; + CreateTestObject(ss.str()); + } + } + + void TearDown() override { + DeleteBucket(bucketName); + DeleteBucket(emptyBucketName); + Client = nullptr; + } + + static void TagTestBucket(const Aws::String& bucketName, const std::shared_ptr& client) { + ASSERT_TRUE(!bucketName.empty()); + ASSERT_TRUE(client); + + PutBucketTaggingRequest taggingRequest; + taggingRequest.SetBucket(bucketName); + Tag tag; + tag.SetKey(TEST_BUCKET_TAG); + tag.SetValue(TEST_BUCKET_TAG); + Tagging tagging; + tagging.AddTagSet(tag); + taggingRequest.SetTagging(tagging); + + auto taggingOutcome = client->PutBucketTagging(taggingRequest); + AWS_ASSERT_SUCCESS(taggingOutcome); + } + + void TagTestBucket(const Aws::String& bucketNameParam) { + TagTestBucket(bucketNameParam, Client); + } + + bool WaitForBucketToPropagate(const Aws::String& bucketNameParam) { + unsigned timeoutCount = 0; + while (timeoutCount++ < TIMEOUT_MAX) { + ListObjectsV2Request listRequest; + listRequest.SetBucket(bucketNameParam); + auto listOutcome = Client->ListObjectsV2(listRequest); + if (listOutcome.IsSuccess()) { + return true; + } + std::this_thread::sleep_for(std::chrono::seconds(10)); + } + return false; + } + + void EmptyBucket(const Aws::String& bucketNameParam) { + ListObjectsV2Request listObjectsRequest; + listObjectsRequest.SetBucket(bucketNameParam); + + ListObjectsV2Outcome listObjectsOutcome = Client->ListObjectsV2(listObjectsRequest); + + if (!listObjectsOutcome.IsSuccess()) + return; + + for (const auto& object : listObjectsOutcome.GetResult().GetContents()) { + DeleteObjectRequest deleteObjectRequest; + deleteObjectRequest.SetBucket(bucketNameParam); + deleteObjectRequest.SetKey(object.GetKey()); + auto deleteObjectOutcome = Client->DeleteObject(deleteObjectRequest); + AWS_ASSERT_SUCCESS(deleteObjectOutcome); + } + } + + void WaitForBucketToEmpty(const Aws::String& bucketNameParam) { + ListObjectsV2Request listObjectsRequest; + listObjectsRequest.SetBucket(bucketNameParam); + + unsigned checkForObjectsCount = 0; + while (checkForObjectsCount++ < TIMEOUT_MAX) { + ListObjectsV2Outcome listObjectsOutcome = Client->ListObjectsV2(listObjectsRequest); + AWS_EXPECT_SUCCESS(listObjectsOutcome); + + if (listObjectsOutcome.GetResult().GetContents().size() > 0) { + std::this_thread::sleep_for(std::chrono::seconds(5)); + } else { + break; + } + } + } + + void DeleteBucket(const Aws::String& bucketNameParam) { + HeadBucketRequest headBucketRequest; + headBucketRequest.SetBucket(bucketNameParam); + HeadBucketOutcome bucketOutcome = Client->HeadBucket(headBucketRequest); + + if (bucketOutcome.IsSuccess()) { + EmptyBucket(bucketNameParam); + WaitForBucketToEmpty(bucketNameParam); + + DeleteBucketRequest deleteBucketRequest; + deleteBucketRequest.SetBucket(bucketNameParam); + + DeleteBucketOutcome deleteBucketOutcome = Client->DeleteBucket(deleteBucketRequest); + AWS_EXPECT_SUCCESS(deleteBucketOutcome); + } + } + + static Aws::String CalculateBucketName(const Aws::String& bucketPrefix) { + return Aws::Testing::GetAwsResourcePrefix() + bucketPrefix; + } + + void CreateTestObject(const Aws::String& key) { + PutObjectRequest putRequest; + putRequest.SetBucket(bucketName); + putRequest.SetKey(key); + + auto data = Aws::MakeShared(ALLOCATION_TAG); + *data << "Test data for " << key; + putRequest.SetBody(data); + + auto putOutcome = Client->PutObject(putRequest); + AWS_ASSERT_SUCCESS(putOutcome); + } +}; + +TEST_F(ListObjectsV2PaginationTest, TestPaginationOnEmptyBucket) { + ListObjectsV2Request request; + request.SetBucket(emptyBucketName); + request.SetMaxKeys(MAX_KEYS); + + size_t objectCount = 0; + auto outcome = Client->ListObjectsV2(request); + + while (outcome.IsSuccess()) { + const auto& result = outcome.GetResult(); + objectCount += result.GetContents().size(); + + if (!Aws::S3::Pagination::ListObjectsV2PaginationTraits::HasMoreResults(result)) { + break; + } + + Aws::S3::Pagination::ListObjectsV2PaginationTraits::SetNextRequest(result, request); + outcome = Client->ListObjectsV2(request); + } + + AWS_ASSERT_SUCCESS(outcome); + EXPECT_EQ(0u, objectCount); +} + +TEST_F(ListObjectsV2PaginationTest, TestPaginationOnNonEmptyBucket) { + ListObjectsV2Request request; + request.SetBucket(bucketName); + request.SetMaxKeys(MAX_KEYS); + + size_t objectCount = 0; + size_t pageCount = 0; + auto outcome = Client->ListObjectsV2(request); + + while (outcome.IsSuccess()) { + const auto& result = outcome.GetResult(); + objectCount += result.GetContents().size(); + pageCount++; + + if (!Aws::S3::Pagination::ListObjectsV2PaginationTraits::HasMoreResults(result)) { + break; + } + + Aws::S3::Pagination::ListObjectsV2PaginationTraits::SetNextRequest(result, request); + outcome = Client->ListObjectsV2(request); + } + + AWS_ASSERT_SUCCESS(outcome); + EXPECT_EQ(OBJECT_COUNT, objectCount); + EXPECT_GT(pageCount, 1u); // Should have multiple pages with MaxKeys=3 +} + +TEST_F(ListObjectsV2PaginationTest, TestManualPaginationLoop) { + ListObjectsV2Request request; + request.SetBucket(bucketName); + request.SetMaxKeys(MAX_KEYS); + + Aws::Vector allKeys; + + do { + auto outcome = Client->ListObjectsV2(request); + AWS_ASSERT_SUCCESS(outcome); + + const auto& result = outcome.GetResult(); + for (const auto& object : result.GetContents()) { + allKeys.push_back(object.GetKey()); + } + + if (!result.GetIsTruncated()) { + break; + } + + request.SetContinuationToken(result.GetNextContinuationToken()); + } while (true); + + EXPECT_EQ(OBJECT_COUNT, allKeys.size()); +} diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/build.gradle.kts b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/build.gradle.kts new file mode 100644 index 00000000000..d929cca7cbb --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + `java-library` +} + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + implementation("software.amazon.smithy:smithy-model:1.51.0") + implementation("software.amazon.smithy:smithy-codegen-core:1.51.0") + implementation("software.amazon.smithy:smithy-aws-traits:1.51.0") + implementation("software.amazon.smithy:smithy-waiters:1.51.0") + implementation("software.amazon.smithy:smithy-rules-engine:1.51.0") + implementation("software.amazon.smithy:smithy-aws-endpoints:1.51.0") + implementation("software.amazon.smithy:smithy-aws-iam-traits:1.51.0") +} diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/ClientCodegenSettings.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/ClientCodegenSettings.java new file mode 100644 index 00000000000..f992d3b76df --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/ClientCodegenSettings.java @@ -0,0 +1,20 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators; + +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.model.shapes.ShapeId; + +public class ClientCodegenSettings { + private final ShapeId service; + + public ClientCodegenSettings(ObjectNode settings) { + this.service = ShapeId.from(settings.expectStringMember("service").getValue()); + } + + public ShapeId getService() { + return service; + } +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CompilationTestParser.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CompilationTestParser.java new file mode 100644 index 00000000000..7c8d4ed4131 --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CompilationTestParser.java @@ -0,0 +1,45 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators; + +import software.amazon.smithy.build.PluginContext; +import software.amazon.smithy.model.shapes.*; +import software.amazon.smithy.aws.traits.ServiceTrait; +import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter; +import com.amazonaws.util.awsclientsmithygenerator.generators.ServiceNameUtil; +import java.util.*; +import java.util.function.Consumer; + +public class CompilationTestParser { + private final ServiceShape service; + private final CppWriterDelegator writerDelegator; + private final String testType; + private final Consumer renderFunction; + private final Map serviceMap; + + public CompilationTestParser(PluginContext context, ServiceShape service, List operations, + String testType, Consumer renderFunction, Map serviceMap) { + this.service = service; + this.writerDelegator = new CppWriterDelegator(context.getFileManifest()); + this.testType = testType; + this.renderFunction = renderFunction; + this.serviceMap = serviceMap; + } + + public void run() { + generateCompilationTest(); + writerDelegator.flushWriters(); + } + + private void generateCompilationTest() { + String serviceName = ServiceNameUtil.getServiceName(service); + String c2jServiceName = ServiceNameUtil.getSmithyServiceName(service, serviceMap); + + writerDelegator.useFileWriter( + "generated/tests/" + c2jServiceName + "-gen-tests/" + serviceName + testType + "CompilationTests.cpp", + renderFunction + ); + } +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppImportContainer.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppImportContainer.java new file mode 100644 index 00000000000..8c26688ae5f --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppImportContainer.java @@ -0,0 +1,36 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators; + +import software.amazon.smithy.codegen.core.ImportContainer; +import software.amazon.smithy.codegen.core.Symbol; +import java.util.HashSet; +import java.util.Set; + +/** + * CppImportContainer tracks symbols used in code generation. + * For C++, we handle includes manually via writeInclude method. + */ +public class CppImportContainer implements ImportContainer { + private final Set imports = new HashSet<>(); + + @Override + public void importSymbol(Symbol symbol, String alias) { + // Track the symbol's namespace for potential future use + if (symbol.getNamespace() != null && !symbol.getNamespace().isEmpty()) { + imports.add(symbol.getNamespace()); + } + } + + public Set getImports() { + return imports; + } + + @Override + public String toString() { + // Return empty string since we handle includes manually + return ""; + } +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriter.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriter.java new file mode 100644 index 00000000000..d3729c6223e --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriter.java @@ -0,0 +1,45 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators; + +import software.amazon.smithy.codegen.core.SymbolWriter; + +public class CppWriter extends SymbolWriter { + + public CppWriter() { + super(new CppImportContainer()); + + // Add custom formatter for symbols + putFormatter('T', (arg, indent) -> { + if (!(arg instanceof software.amazon.smithy.codegen.core.Symbol symbol)) { + throw new software.amazon.smithy.codegen.core.CodegenException("Expected a symbol but got " + arg); + } + + // Record our symbol so we can generate appropriate includes later if needed + getImportContainer().importSymbol(symbol, null); + + // For C++, use namespace::name syntax (e.g. Aws::S3::Model::ListBucketsRequest) + if (symbol.getNamespace() != null && !symbol.getNamespace().isEmpty()) { + return symbol.getNamespace() + "::" + symbol.getName(); + } + return symbol.getName(); + }); + } + + public CppWriter writeInclude(String header) { + write("#include <$L>", header); + return this; + } + + public CppWriter writeNamespaceOpen(String namespace) { + openBlock("namespace $L\n{", namespace); + return this; + } + + public CppWriter writeNamespaceClose(String namespace) { + closeBlock("} // namespace $L", namespace); + return this; + } +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java new file mode 100644 index 00000000000..9d42e8b7090 --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java @@ -0,0 +1,37 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators; + +import software.amazon.smithy.build.FileManifest; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +public class CppWriterDelegator { + private final FileManifest fileManifest; + private final Map writers = new HashMap<>(); + + public CppWriterDelegator(FileManifest fileManifest) { + this.fileManifest = fileManifest; + } + + public void useFileWriter(String filename, Consumer writerConsumer) { + CppWriter writer = writers.computeIfAbsent(filename, k -> new CppWriter()); + writerConsumer.accept(writer); + } + + public void flushWriters() { + writers.forEach((filename, writer) -> { + try { + Path outputPath = fileManifest.getBaseDir().resolve(filename); + java.nio.file.Files.createDirectories(outputPath.getParent()); + java.nio.file.Files.writeString(outputPath, writer.toString()); + } catch (Exception e) { + throw new RuntimeException("Failed to write file: " + filename, e); + } + }); + } +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/FeatureParser.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/FeatureParser.java new file mode 100644 index 00000000000..6d76901b024 --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/FeatureParser.java @@ -0,0 +1,67 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators; + +import software.amazon.smithy.build.PluginContext; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.model.shapes.*; +import software.amazon.smithy.aws.traits.ServiceTrait; +import java.util.*; +import java.util.function.Consumer; + +public class FeatureParser { + private final PluginContext context; + private final ServiceShape service; + private final List operations; + private final CppWriterDelegator writerDelegator; + private final Map serviceMap; + private final String featureName; + + public FeatureParser(PluginContext context, ServiceShape service, List operations, String featureName) { + this.context = context; + this.service = service; + this.operations = operations; + this.featureName = featureName; + this.writerDelegator = new CppWriterDelegator(context.getFileManifest()); + + // Initialize service map + this.serviceMap = new HashMap<>(); + ObjectNode settings = context.getSettings(); + if (settings.containsMember("c2jMap")) { + Node c2jMapNode = settings.expectMember("c2jMap"); + if (c2jMapNode.isStringNode()) { + String jsonStr = c2jMapNode.expectStringNode().getValue(); + ObjectNode mapNode = Node.parseJsonWithComments(jsonStr).expectObjectNode(); + mapNode.getMembers().forEach((key, value) -> { + this.serviceMap.put(key.getValue(), value.expectStringNode().getValue()); + }); + } + } + } + + public void run(Consumer> generationLogic) { + generationLogic.accept(this); + writerDelegator.flushWriters(); + } + + public void generateClientHeader(String fileName, Consumer generator) { + String serviceName = ServiceNameUtil.getServiceName(service); + String smithyServiceName = ServiceNameUtil.getSmithyServiceName(service, serviceMap); + + writerDelegator.useFileWriter( + "include/aws/" + smithyServiceName + "/" + fileName, + generator + ); + } + + public PluginContext getContext() { return context; } + public ServiceShape getService() { return service; } + public List getOperations() { return operations; } + public String getFeatureName() { return featureName; } + public Map getServiceMap() { return serviceMap; } + public String getServiceName() { return ServiceNameUtil.getServiceName(service); } + public String getSmithyServiceName() { return ServiceNameUtil.getSmithyServiceName(service, serviceMap); } +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/OperationData.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/OperationData.java new file mode 100644 index 00000000000..7d4c3bec889 --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/OperationData.java @@ -0,0 +1,32 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators; + +import software.amazon.smithy.model.shapes.*; +import software.amazon.smithy.model.traits.Trait; + +public class OperationData { + private final OperationShape operation; + private final T trait; + private final ServiceShape service; + + public OperationData(OperationShape operation, T trait, ServiceShape service) { + this.operation = operation; + this.trait = trait; + this.service = service; + } + + public OperationShape getOperation() { + return operation; + } + + public T getTrait() { + return trait; + } + + public ServiceShape getService() { + return service; + } +} diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/ServiceNameUtil.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/ServiceNameUtil.java new file mode 100644 index 00000000000..d97626c7cfc --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/ServiceNameUtil.java @@ -0,0 +1,141 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators; + +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.aws.traits.ServiceTrait; +import software.amazon.smithy.model.traits.TitleTrait; +import java.util.Map; +import java.util.Set; + +public final class ServiceNameUtil { + + // Legacy service IDs that need special handling to match what the legacy c2j code generator produced + // This logic is copied from C2jModelToGeneratorModelTransformer.convertMetadata() method + // Hardcoded mappings for legacy services where C2J used fullServiceName for file naming + // Maps serviceId (lowercase, sanitized) to the sanitized fullServiceName from C2J models + private static final Map LEGACY_FULL_SERVICE_NAME_MAP = Map.ofEntries( + Map.entry("amp", "PrometheusService"), + Map.entry("appintegrations", "AppIntegrationsService"), + Map.entry("billingconductor", "BillingConductor"), + Map.entry("chatbot", "chatbot"), + Map.entry("clouddirectory", "CloudDirectory"), + Map.entry("cloudfront", "CloudFront"), + Map.entry("cloudsearch", "CloudSearch"), + Map.entry("cloudsearchdomain", "CloudSearchDomain"), + Map.entry("codeartifact", "CodeArtifact"), + Map.entry("codestarnotifications", "CodeStarNotifications"), + Map.entry("configservice", "ConfigService"), + Map.entry("databrew", "GlueDataBrew"), + Map.entry("deadline", "deadline"), + Map.entry("elasticache", "ElastiCache"), + Map.entry("emrcontainers", "EMRContainers"), + Map.entry("marketplaceentitlementservice", "MarketplaceEntitlementService"), + Map.entry("cloudwatchevents", "CloudWatchEvents"), + Map.entry("evidently", "CloudWatchEvidently"), + Map.entry("forecast", "ForecastService"), + Map.entry("forecastquery", "ForecastQueryService"), + Map.entry("grafana", "ManagedGrafana"), + Map.entry("importexport", "ImportExport"), + Map.entry("inspector", "Inspector"), + Map.entry("lambda", "Lambda"), + Map.entry("location", "LocationService"), + Map.entry("m2", "MainframeModernization"), + Map.entry("migrationhubstrategy", "MigrationHubStrategyRecommendations"), + Map.entry("mq", "MQ"), + Map.entry("opensearch", "OpenSearchService"), + Map.entry("rbin", "RecycleBin"), + Map.entry("rdsdata", "RDSDataService"), + Map.entry("redshiftdata", "RedshiftDataAPIService"), + Map.entry("resiliencehub", "ResilienceHub"), + Map.entry("rum", "CloudWatchRUM"), + Map.entry("lexruntimeservice", "LexRuntimeService"), + Map.entry("lexruntimev2", "LexRuntimeV2"), + Map.entry("lexmodelsv2", "LexModelsV2"), + Map.entry("sagemakerruntime", "SageMakerRuntime"), + Map.entry("sagemakera2iruntime", "AugmentedAIRuntime"), + Map.entry("sagemakeredge", "SagemakerEdgeManager"), + Map.entry("schemas", "Schemas"), + Map.entry("simpledb", "SimpleDB"), + Map.entry("dynamodbstreams", "DynamoDBStreams"), + Map.entry("transcribe", "TranscribeService"), + Map.entry("transcribestreaming", "TranscribeStreamingService"), + Map.entry("transfer", "TransferFamily"), + Map.entry("wisdom", "ConnectWisdomService"), + Map.entry("marketplaceagreement", "MarketplaceAgreementService") + ); + + // TODO: Remove hardcoded mappings once Smithy models include serviceAbbreviation trait + // Smithy sdkId matches C2J serviceId, but C2J prioritizes serviceAbbreviation (which Smithy lacks). + // 18 of 66 services where C2J abbrev != serviceId require these mappings to match C2J output. + // Hardcoded mappings where Smithy sdkId doesn't match C2J serviceAbbreviation + private static final Map SMITHY_TO_C2J_NAMESPACE = Map.ofEntries( + Map.entry("b2bi", "B2BI"), + Map.entry("cloudcontrol", "CloudControlApi"), + Map.entry("ecrpublic", "ECRPublic"), + Map.entry("evs", "EVS"), + Map.entry("finspacedata", "FinSpaceData"), + Map.entry("fis", "FIS"), + Map.entry("identitystore", "IdentityStore"), + Map.entry("inspectorscan", "inspectorscan"), + Map.entry("iotdeviceadvisor", "IoTDeviceAdvisor"), + Map.entry("ivs", "IVS"), + Map.entry("ivsrealtime", "ivsrealtime"), + Map.entry("kinesisvideosignaling", "KinesisVideoSignalingChannels"), + Map.entry("marketplaceagreement", "AgreementService"), + Map.entry("mediapackagev2", "mediapackagev2"), + Map.entry("savingsplans", "SavingsPlans"), + Map.entry("servicecatalogappregistry", "AppRegistry"), + Map.entry("sesv2", "SESV2"), + Map.entry("synthetics", "Synthetics"), + Map.entry("transfer", "Transfer") + ); + + public static String getServiceName(ServiceShape service) { + String serviceId = service.getTrait(ServiceTrait.class) + .map(ServiceTrait::getSdkId) + .orElse(service.getId().getName()); + + String sanitized = sanitizeServiceAbbreviation(serviceId); + + // Check serviceAbbreviation mappings first (highest priority in C2J) + String mapped = SMITHY_TO_C2J_NAMESPACE.get(sanitized.toLowerCase()); + if (mapped != null) { + return mapped; + } + + // Check legacy fullServiceName mappings (fallback when serviceAbbreviation missing) + String legacyName = LEGACY_FULL_SERVICE_NAME_MAP.get(sanitized.toLowerCase()); + if (legacyName != null) { + return legacyName; + } + + return sanitized; + } + + public static String getServiceNameUpperCamel(ServiceShape service) { + return capitalize(getServiceName(service)); + } + + public static String capitalize(String str) { + return str.substring(0, 1).toUpperCase() + str.substring(1); + } + + // Match C2jModelToGeneratorModelTransformer.sanitizeServiceAbbreviation() exactly + private static String sanitizeServiceAbbreviation(String serviceAbbreviation) { + return serviceAbbreviation.replace(" ", "").replace("-", "").replace("_", "").replace("Amazon", "").replace("AWS", "").replace("/", ""); + } + + public static String getSmithyServiceName(ServiceShape service, Map serviceMap) { + String sdkId = service.getTrait(ServiceTrait.class) + .map(ServiceTrait::getSdkId) + .orElse(service.getId().getName()) + .trim() + .toLowerCase() + .replace(" ", "-"); + + return serviceMap != null ? serviceMap.getOrDefault(sdkId, sdkId) : sdkId; + } +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/ShapeUtil.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/ShapeUtil.java new file mode 100644 index 00000000000..cc9ea2b0ecf --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/ShapeUtil.java @@ -0,0 +1,279 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators; + +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.*; +import software.amazon.smithy.model.traits.Trait; +import java.util.*; + +public class ShapeUtil { + + private static final List RESULT_SUFFIXES = List.of("Result", "SdkResult", "CppSdkResult"); + + /** + * Backward compatibility map for operations that must use "SdkResult" suffix. + * + * Background: + * The C2J code generator's renameShape() tries suffixes in order: Result -> SdkResult -> CppSdkResult. + * It picks the first suffix that doesn't collide with existing shape names in the model. + * + * Problem: + * Some legacy C2J models (e.g., medialive-2017-10-14.normal.json) defined shapes like: + * - ListNodesResult (a domain shape) + * - ListNodesResponse (the operation output shape) + * + * When the C2J generator tried to create the SDK wrapper for ListNodes operation: + * 1. Tried "ListNodesResult" -> COLLISION with existing shape + * 2. Fell back to "ListNodesSdkResult" -> No collision, used this + * + * The Smithy models are cleaner and don't have these collisions, but we must preserve + * the "SdkResult" suffix for these operations to maintain backward compatibility with + * existing C2J-generated code. + * + * Map format: service-name -> Set of operation names requiring "SdkResult" + * + * Example: + * medialive C2J model had both ListNodesResult and ListNodesResponse shapes, + * forcing ListNodesSdkResult. Smithy model only has ListNodesResponse, but we + * preserve SdkResult to avoid breaking changes. + * + */ + private static final Map> LEGACY_SDK_RESULT_OPERATIONS = Map.of( + "medialive", Set.of("ListChannelPlacementGroups", "ListClusters", "ListNetworks", "ListNodes") + ); + + /** + * Legacy operation name version suffixes required for backwards compatibility. + * The legacy C2J code generator appended API version suffixes to operation names for certain services. + * This maintains compatibility with existing generated code that depends on these versioned method names. + * + * CloudFront: Uses "2020_05_31" suffix (the API version when C++ SDK generation was standardized) + * - Example: "ListDistributions" becomes "ListDistributions2020_05_31" + * - Required because existing C++ code expects these versioned method names + * - CloudFront has had multiple API versions, and the C2J generator historically used version suffixes + */ + private static final Map OPERATION_VERSION_SUFFIXES = Map.of( + "cloudfront", "2020_05_31" + ); + + /** + * C2J/Smithy model mismatches: tokens that are integers in C2J but strings in Smithy. + * + * Background: + * Some services have tokens that were modeled as integers in C2J but changed to + * strings in Smithy models. The C2J-generated code uses int types, so traits must + * use `!= 0` checks instead of `.empty()` checks. + * + * Map format: service-name -> Map of operation-name -> token-name + * + * Example: + * S3's ListParts operation has NextPartNumberMarker as integer in C2J but string in Smithy. + */ + private static final Map>> NUMERIC_TOKEN_OVERRIDES = Map.of( + "s3", Map.of("ListParts", Set.of("NextPartNumberMarker")), + "s3-crt", Map.of("ListParts", Set.of("NextPartNumberMarker")) + ); + + /** + * Returns all shapes referenced by a root shape, recursively. + */ + public static Set getReferences(Model model, Shape root) { + Set refs = new HashSet<>(); + refs.add(root); + for (var member : root.members()) { + refs.addAll(getReferences(model, model.expectShape(member.getTarget()))); + } + return refs; + } + + /** + * Gets the operation method name with legacy version suffix if required. + * + * Some services require version suffixes appended to operation names for backwards compatibility + * with the legacy C2J code generator. This ensures existing C++ code continues to work with + * the expected method names. + * + * @param opName The base operation name from the Smithy model + * @param smithyServiceName The Smithy service name (e.g., "cloudfront") + * @return The operation name with version suffix if needed (e.g., "ListDistributions2020_05_31") + */ + public static String getOperationMethodName(String opName, String smithyServiceName) { + String versionSuffix = OPERATION_VERSION_SUFFIXES.get(smithyServiceName); + return versionSuffix != null ? opName + versionSuffix : opName; + } + + /** + * Checks if a token is numeric (integer or long type). + * First checks for C2J/Smithy model mismatch overrides, then inspects the actual shape type. + * + * @param model The Smithy model + * @param op The operation shape + * @param smithyServiceName The Smithy service name + * @param wrapperMember The wrapper member name (null if token is at top level) + * @param tokenName The token name + * @return true if the token is numeric + */ + public static boolean isNumericToken(Model model, OperationShape op, String smithyServiceName, String wrapperMember, String tokenName) { + // Check for C2J/Smithy model mismatch overrides first + Map> serviceOverrides = NUMERIC_TOKEN_OVERRIDES.get(smithyServiceName); + if (serviceOverrides != null) { + Set tokens = serviceOverrides.get(op.getId().getName()); + if (tokens != null && tokens.contains(tokenName)) { + return true; + } + } + + Optional tokenShape = getOutputStructure(model, op) + .flatMap(out -> { + if (wrapperMember == null) { + return out.getMember(tokenName); + } + return out.getMember(wrapperMember) + .flatMap(m -> model.getShape(m.getTarget())) + .flatMap(t -> t.asStructureShape()) + .flatMap(w -> w.getMember(tokenName)); + }) + .flatMap(member -> model.getShape(member.getTarget())); + + return tokenShape.map(ts -> ts instanceof IntegerShape || ts instanceof LongShape).orElse(false); + } + + /** + * Determines the result suffix for an operation based on protocol and naming conflicts. + * Replicates C2J renameShape conflict detection logic. + * + * C2J logic: + * 1. Try "Result" first + * 2. If collision exists (direct name match OR Get/Set accessor conflict), try "SdkResult" + * 3. If still collision, try "CppSdkResult" + * + * @param model The Smithy model + * @param operation The operation shape + * @param smithyServiceName The Smithy service name + * @return "Response" for EC2 protocol, "SdkResult" if naming conflict exists, otherwise "Result" + */ + public static String getResultSuffix(Model model, OperationShape operation, String smithyServiceName) { + // EC2 protocol services rename all Result shapes to Response + if ("ec2".equals(smithyServiceName)) { + return "Response"; + } + + // C2J backward compatibility: preserve SdkResult for operations that had collisions in legacy models + Set legacyOps = LEGACY_SDK_RESULT_OPERATIONS.get(smithyServiceName); + if (legacyOps != null && legacyOps.contains(operation.getId().getName())) { + return "SdkResult"; + } + + String baseName = operation.getId().getName(); + Set allShapeNames = new HashSet<>(); + model.shapes().forEach(s -> allShapeNames.add(s.getId().getName())); + + // Output shape name (used for legacy early-accept behavior) + String outputShapeName = operation.getOutput().isPresent() + ? operation.getOutputShape().getName() + : null; + + // For closer parity with the legacy Get/Set collision rule + Set outputMemberNames = getOutputStructure(model, operation) + .map(struct -> new HashSet<>(struct.getAllMembers().keySet())) + .orElse(new HashSet<>()); + + for (String suffix : RESULT_SUFFIXES) { + String candidate = baseName + suffix; + + // Legacy parity: if the operation's output shape is already named exactly candidate, + // then legacy renameShape() would return immediately (no conflict handling). + if (outputShapeName != null && candidate.equals(outputShapeName)) { + return suffix; + } + + // Legacy: otherwise, direct collision with any existing shape name is a conflict + if (allShapeNames.contains(candidate)) { + continue; + } + + // Closer parity with legacy intent (member-gated Get/Set collisions) + if (!outputMemberNames.isEmpty()) { + boolean hasCollision = false; + for (String shapeName : allShapeNames) { + if (outputMemberNames.contains(shapeName) && + (candidate.equals("Get" + shapeName) || candidate.equals("Set" + shapeName))) { + hasCollision = true; + break; + } + } + if (hasCollision) { + continue; + } + } + + return suffix; + } + + // Legacy would throw if no suffix is available; returning CppSdkResult silently can hide bugs + throw new IllegalStateException("Unhandled result shape name conflict for operation: " + baseName); + } + + /** + * Gets the output structure shape for an operation, or null if not present or not a structure. + */ + private static Optional getOutputStructure(Model model, OperationShape op) { + return op.getOutput() + .flatMap(outputId -> model.getShape(outputId)) + .flatMap(shape -> shape.asStructureShape()); + } + + /** + * Finds the wrapper member in an operation's output that contains the specified token. + * Used for token resolution when tokens are nested in wrapper structures. + * + * @param model The Smithy model + * @param op The operation shape + * @param tokenName The token name to find + * @return The wrapper member name, or null if not found + */ + public static String findWrapperMemberContainingToken(Model model, OperationShape op, String tokenName) { + return getOutputStructure(model, op) + .flatMap(outputShape -> + outputShape.getAllMembers().entrySet().stream() + .filter(entry -> model.getShape(entry.getValue().getTarget()) + .flatMap(t -> t.asStructureShape()) + .map(s -> s.getAllMembers().containsKey(tokenName)) + .orElse(false)) + .map(Map.Entry::getKey) + .findFirst() + ) + .orElse(null); + } + + /** + * Checks if an operation's output has a top-level member with the specified name. + * + * @param model The Smithy model + * @param op The operation shape + * @param memberName The member name to check + * @return true if the member exists at the top level + */ + public static boolean hasTopLevelMember(Model model, OperationShape op, String memberName) { + return getOutputStructure(model, op) + .map(outputShape -> outputShape.getAllMembers().containsKey(memberName)) + .orElse(false); + } + + /** + * Gets service-level token when operation-level traits are missing. + * + * @param service The service shape + * @param traitClass The trait class to look for + * @param tokenExtractor Function to extract the token from the trait + * @return The service-level token, or null if not defined + */ + public static String getServiceLevelToken(ServiceShape service, Class traitClass, java.util.function.Function> tokenExtractor) { + return service.getTrait(traitClass) + .map(trait -> tokenExtractor.apply(trait).orElse(null)) + .orElse(null); + } +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationClientHeaderGenerator.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationClientHeaderGenerator.java new file mode 100644 index 00000000000..15d66d2288d --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationClientHeaderGenerator.java @@ -0,0 +1,76 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators.templates; + +import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter; +import com.amazonaws.util.awsclientsmithygenerator.generators.OperationData; +import com.amazonaws.util.awsclientsmithygenerator.generators.ServiceNameUtil; +import com.amazonaws.util.awsclientsmithygenerator.generators.ShapeUtil; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.aws.traits.ServiceTrait; +import software.amazon.smithy.model.traits.PaginatedTrait; +import com.amazonaws.util.awsclientsmithygenerator.generators.ServiceNameUtil; +import java.util.List; +import java.util.Map; + +public class PaginationClientHeaderGenerator { + private final ServiceShape service; + private final List> paginatedOps; + private final Map serviceMap; + private final String smithyServiceName; + + public PaginationClientHeaderGenerator(ServiceShape service, List> paginatedOps, Map serviceMap) { + this.service = service; + this.paginatedOps = paginatedOps; + this.serviceMap = serviceMap; + this.smithyServiceName = ServiceNameUtil.getSmithyServiceName(service, serviceMap); + } + + public void render(CppWriter writer) { + String serviceName = ServiceNameUtil.getServiceName(service); + String smithyServiceName = ServiceNameUtil.getSmithyServiceName(service, serviceMap); + + // Header and includes + writer.write("/**"); + writer.write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved."); + writer.write(" * SPDX-License-Identifier: Apache-2.0."); + writer.write(" */"); + writer.write(""); + writer.write("#pragma once"); + + renderIncludes(writer, serviceName, smithyServiceName); + renderNamespaces(writer, serviceName); + } + + private void renderIncludes(CppWriter writer, String serviceName, String smithyServiceName) { + String classPrefix = ServiceNameUtil.getServiceNameUpperCamel(service); + writer.writeInclude("aws/" + smithyServiceName + "/" + classPrefix + "Client.h"); + writer.writeInclude("aws/core/utils/pagination/Paginator.h"); + + for (OperationData data : paginatedOps) { + String opName = data.getOperation().getId().getName(); + writer.writeInclude("aws/" + smithyServiceName + "/model/pagination/" + opName + "PaginationTraits.h"); + } + writer.write(""); + } + + private void renderNamespaces(CppWriter writer, String serviceName) { + String classPrefix = ServiceNameUtil.getServiceNameUpperCamel(service); + writer.writeNamespaceOpen("Aws"); + writer.writeNamespaceOpen(serviceName); + writer.write(""); + + for (OperationData data : paginatedOps) { + String opName = data.getOperation().getId().getName(); + String methodName = ShapeUtil.getOperationMethodName(opName, smithyServiceName); + writer.write("using $LPaginator = Aws::Utils::Pagination::PagePaginator<$LClient, Model::$LRequest, Pagination::$LPaginationTraits>;", + opName, classPrefix, methodName, opName); + } + + writer.write(""); + writer.writeNamespaceClose(serviceName); + writer.writeNamespaceClose("Aws"); + } +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationCodegenPlugin.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationCodegenPlugin.java new file mode 100644 index 00000000000..24546026b54 --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationCodegenPlugin.java @@ -0,0 +1,68 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators; + +import software.amazon.smithy.build.PluginContext; +import software.amazon.smithy.build.SmithyBuildPlugin; +import software.amazon.smithy.model.knowledge.TopDownIndex; +import software.amazon.smithy.model.traits.PaginatedTrait; +import software.amazon.smithy.model.traits.DeprecatedTrait; +import software.amazon.smithy.model.shapes.*; +import com.amazonaws.util.awsclientsmithygenerator.generators.OperationData; +import com.amazonaws.util.awsclientsmithygenerator.generators.FeatureParser; +import com.amazonaws.util.awsclientsmithygenerator.generators.templates.PaginationTraitsGenerator; +import com.amazonaws.util.awsclientsmithygenerator.generators.templates.PaginationClientHeaderGenerator; +import com.amazonaws.util.awsclientsmithygenerator.generators.templates.PaginationCompilationTestGenerator; +import java.util.List; +import java.util.stream.Collectors; + +public class PaginationCodegenPlugin implements SmithyBuildPlugin { + + @Override + public String getName() { + return "cpp-codegen-pagination-plugin"; + } + + @Override + public void execute(PluginContext context) { + var model = context.getModel(); + + for (ServiceShape service : model.getServiceShapes()) { + // Find paginated operations using TopDownIndex, excluding deprecated operations + List> paginatedOps = TopDownIndex.of(model).getContainedOperations(service).stream() + .filter(op -> op.hasTrait(PaginatedTrait.class)) + .filter(op -> !op.hasTrait(DeprecatedTrait.class)) + .map(op -> new OperationData<>(op, op.expectTrait(PaginatedTrait.class), service)) + .collect(Collectors.toList()); + + if (!paginatedOps.isEmpty()) { + // Generate pagination files + FeatureParser> parser = new FeatureParser<>(context, service, paginatedOps, "Pagination"); + parser.run(featureParser -> { + String serviceName = ServiceNameUtil.getServiceNameUpperCamel(featureParser.getService()); + + // Generate client pagination header + featureParser.generateClientHeader( + serviceName + "ClientPagination.h", + writer -> new PaginationClientHeaderGenerator(featureParser.getService(), featureParser.getOperations(), featureParser.getServiceMap()).render(writer) + ); + + // Generate pagination traits headers + PaginationTraitsGenerator traitsGenerator = new PaginationTraitsGenerator( + featureParser.getContext(), + featureParser.getService(), + featureParser.getOperations(), + featureParser.getSmithyServiceName() + ); + traitsGenerator.write(); + }); + + // Generate compilation test + PaginationCompilationTestGenerator testGenerator = new PaginationCompilationTestGenerator(context, service, paginatedOps, parser.getServiceMap()); + testGenerator.run(); + } + } + } +} diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationCompilationTestGenerator.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationCompilationTestGenerator.java new file mode 100644 index 00000000000..faafa09db67 --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationCompilationTestGenerator.java @@ -0,0 +1,98 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators.templates; + +import software.amazon.smithy.build.PluginContext; +import software.amazon.smithy.model.shapes.*; +import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter; +import com.amazonaws.util.awsclientsmithygenerator.generators.CompilationTestParser; +import com.amazonaws.util.awsclientsmithygenerator.generators.OperationData; +import com.amazonaws.util.awsclientsmithygenerator.generators.ServiceNameUtil; +import software.amazon.smithy.model.traits.PaginatedTrait; +import java.util.*; + +public class PaginationCompilationTestGenerator { + private final CompilationTestParser> parser; + private final List> allPaginatedOps; + private final Map serviceMap; + + public PaginationCompilationTestGenerator(PluginContext context, ServiceShape service, List> allPaginatedOps, Map serviceMap) { + this.allPaginatedOps = allPaginatedOps; + this.serviceMap = serviceMap; + this.parser = new CompilationTestParser<>( + context, + service, + allPaginatedOps, + "Pagination", + this::render, + serviceMap + ); + } + + public void run() { + parser.run(); + } + + public void render(CppWriter writer) { + // Get service from the first operation since all operations belong to the same service + ServiceShape service = allPaginatedOps.get(0).getService(); + String serviceName = ServiceNameUtil.getServiceName(service); + + writer.write("/**") + .write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.") + .write(" * SPDX-License-Identifier: Apache-2.0.") + .write(" */") + .write("") + .write("// Header compilation test for " + serviceName + " pagination headers") + .write("// This test ensures all generated pagination headers compile successfully") + .write(""); + + // Include all service headers + writeIncludes(writer); + + writer.write("") + .write("#include ") + .write(""); + + writer.openBlock("class " + serviceName + "PaginationCompilationTest : public Aws::Testing::AwsCppSdkGTestSuite\n{", "};", () -> { + // Empty class body + }); + + writer.write(""); + + writer.openBlock("TEST_F(" + serviceName + "PaginationCompilationTest, " + serviceName + "PaginationHeadersCompile)\n{", "}", () -> { + writer.write(" // Test passes if compilation succeeds") + .write(" SUCCEED();"); + }); + } + + private void writeIncludes(CppWriter writer) { + Set clientHeaders = new HashSet<>(); + Set traitHeaders = new HashSet<>(); + + for (OperationData paginationData : allPaginatedOps) { + ServiceShape service = paginationData.getService(); + String serviceName = ServiceNameUtil.getServiceNameUpperCamel(service); + String smithyServiceName = ServiceNameUtil.getSmithyServiceName(service, serviceMap); + + // Collect unique client headers + clientHeaders.add("aws/" + smithyServiceName + "/" + serviceName + "ClientPagination.h"); + + // Collect unique trait headers + String operationName = paginationData.getOperation().getId().getName(); + traitHeaders.add("aws/" + smithyServiceName + "/model/pagination/" + operationName + "PaginationTraits.h"); + } + + // Write unique client headers + for (String header : clientHeaders) { + writer.writeInclude(header); + } + + // Write unique trait headers + for (String header : traitHeaders) { + writer.writeInclude(header); + } + } +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationTraitsGenerator.java b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationTraitsGenerator.java new file mode 100644 index 00000000000..915488e2011 --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationTraitsGenerator.java @@ -0,0 +1,204 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators.templates; + +import software.amazon.smithy.build.PluginContext; +import software.amazon.smithy.model.shapes.*; +import software.amazon.smithy.model.traits.PaginatedTrait; +import software.amazon.smithy.aws.traits.ServiceTrait; +import com.amazonaws.util.awsclientsmithygenerator.generators.OperationData; +import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter; +import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator; +import com.amazonaws.util.awsclientsmithygenerator.generators.ServiceNameUtil; +import com.amazonaws.util.awsclientsmithygenerator.generators.ShapeUtil; +import java.util.*; +import java.util.Arrays; + +public class PaginationTraitsGenerator { + private final PluginContext context; + private final ServiceShape service; + private final List> paginatedOps; + private final String smithyServiceName; + + private final CppWriterDelegator writerDelegator; + + public PaginationTraitsGenerator(PluginContext context, ServiceShape service, List> paginatedOps, String smithyServiceName) { + this.context = context; + this.service = service; + this.paginatedOps = paginatedOps; + this.smithyServiceName = smithyServiceName; + this.writerDelegator = new CppWriterDelegator(context.getFileManifest()); + } + + public void write() { + String serviceName = ServiceNameUtil.getServiceName(service); + + for (OperationData data : paginatedOps) { + String fileName = "include/aws/" + smithyServiceName + "/model/pagination/" + data.getOperation().getId().getName() + "PaginationTraits.h"; + + writerDelegator.useFileWriter(fileName, writer -> { + generateTraitsHeader(writer, data, serviceName); + }); + } + + writerDelegator.flushWriters(); + } + + private void generateTraitsHeader(CppWriter writer, OperationData data, String serviceName) { + OperationShape op = data.getOperation(); + PaginatedTrait trait = data.getTrait(); + String opName = op.getId().getName(); + + // Header comment + writer.write("/**") + .write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.") + .write(" * SPDX-License-Identifier: Apache-2.0.") + .write(" */") + .write("") + .write("#pragma once"); + + // Includes - detect suffix like C2J renameShape logic + String resultSuffix = ShapeUtil.getResultSuffix(context.getModel(), op, smithyServiceName); + String capitalizedServiceName = ServiceNameUtil.getServiceNameUpperCamel(service); + String requestFileName = ShapeUtil.getOperationMethodName(opName, smithyServiceName) + "Request"; + String resultFileName = ShapeUtil.getOperationMethodName(opName, smithyServiceName) + resultSuffix; + writer.writeInclude("aws/" + smithyServiceName + "/" + capitalizedServiceName + "_EXPORTS.h") + .writeInclude("aws/" + smithyServiceName + "/model/" + requestFileName + ".h") + .writeInclude("aws/" + smithyServiceName + "/model/" + resultFileName + ".h") + .writeInclude("aws/" + smithyServiceName + "/" + capitalizedServiceName + "Client.h") + .write(""); + + // Namespaces + writer.writeNamespaceOpen("Aws") + .writeNamespaceOpen(serviceName) + .writeNamespaceOpen("Pagination") + .write(""); + + // Struct definition + writer.openBlock("struct " + opName + "PaginationTraits\n{", "};", () -> { + // Use detected suffix to match C2J renameShape logic + String methodName = ShapeUtil.getOperationMethodName(opName, smithyServiceName); + writer.write(" using RequestType = Model::$LRequest;", methodName) + .write(" using ResultType = Model::$L$L;", methodName, resultSuffix) + .write(" using OutcomeType = Model::$LOutcome;", methodName) + .write(" using ClientType = $LClient;", capitalizedServiceName) + .write(""); + + // Invoke method + writer.openBlock(" static OutcomeType Invoke(ClientType& client, const RequestType& request)\n {", " }", () -> { + writer.write(" return client.$L(request);", methodName); + }); + + writer.write(""); + + // HasMoreResults method + writer.openBlock(" static bool HasMoreResults(const ResultType& result)\n {", " }", () -> { + if (trait.getOutputToken().isPresent()) { + String outToken = trait.getOutputToken().get(); + + // Pattern A: Explicit nested token like "EngineDefaults.Marker" + if (outToken.contains(".")) { + String[] parts = outToken.split("\\.", 2); + String memberName = parts[0]; + String nestedTokenName = parts[1]; + if (ShapeUtil.isNumericToken(context.getModel(), op, smithyServiceName, memberName, nestedTokenName)) { + writer.write(" return result.Get$L().Get$L() != 0;", ServiceNameUtil.capitalize(memberName), ServiceNameUtil.capitalize(nestedTokenName)); + } else { + writer.write(" return !result.Get$L().Get$L().empty();", ServiceNameUtil.capitalize(memberName), ServiceNameUtil.capitalize(nestedTokenName)); + } + } + // Pattern B: Check if token is on top-level output + else if (ShapeUtil.hasTopLevelMember(context.getModel(), op, outToken)) { + if (ShapeUtil.isNumericToken(context.getModel(), op, smithyServiceName, null, outToken)) { + writer.write(" return result.Get$L() != 0;", ServiceNameUtil.capitalize(outToken)); + } else { + writer.write(" return !result.Get$L().empty();", ServiceNameUtil.capitalize(outToken)); + } + } + // Pattern C: Find wrapper member containing the token + else { + String wrapperMember = ShapeUtil.findWrapperMemberContainingToken(context.getModel(), op, outToken); + if (wrapperMember != null) { + if (ShapeUtil.isNumericToken(context.getModel(), op, smithyServiceName, wrapperMember, outToken)) { + writer.write(" return result.Get$L().Get$L() != 0;", ServiceNameUtil.capitalize(wrapperMember), ServiceNameUtil.capitalize(outToken)); + } else { + writer.write(" return !result.Get$L().Get$L().empty();", ServiceNameUtil.capitalize(wrapperMember), ServiceNameUtil.capitalize(outToken)); + } + } else if (ShapeUtil.hasTopLevelMember(context.getModel(), op, "IsTruncated")) { + writer.write(" return result.GetIsTruncated();"); + } else { + writer.write(" return false;"); + } + } + } else { + // Fallback to service-level pagination configuration when operation-level traits are missing. + // Some services like AccessAnalyzer define pagination tokens at the service level rather than per-operation. + String serviceLevelOutputToken = ShapeUtil.getServiceLevelToken(service, software.amazon.smithy.model.traits.PaginatedTrait.class, t -> t.getOutputToken()); + if (serviceLevelOutputToken != null) { + if (ShapeUtil.isNumericToken(context.getModel(), op, smithyServiceName, null, serviceLevelOutputToken)) { + writer.write(" return result.Get$L() != 0;", ServiceNameUtil.capitalize(serviceLevelOutputToken)); + } else { + writer.write(" return !result.Get$L().empty();", ServiceNameUtil.capitalize(serviceLevelOutputToken)); + } + } else { + writer.write(" return result.GetIsTruncated();"); + } + } + }); + + writer.write(""); + + // SetNextRequest method + writer.openBlock(" static void SetNextRequest(const ResultType& result, RequestType& request)\n {", " }", () -> { + String inToken = null; + String outToken = null; + + if (trait.getInputToken().isPresent() && trait.getOutputToken().isPresent()) { + inToken = trait.getInputToken().get(); + outToken = trait.getOutputToken().get(); + } else { + // Fallback to service-level pagination configuration when operation-level traits are missing. + // Some services like AccessAnalyzer define pagination tokens at the service level rather than per-operation. + String serviceLevelInputToken = ShapeUtil.getServiceLevelToken(service, software.amazon.smithy.model.traits.PaginatedTrait.class, t -> t.getInputToken()); + String serviceLevelOutputToken = ShapeUtil.getServiceLevelToken(service, software.amazon.smithy.model.traits.PaginatedTrait.class, t -> t.getOutputToken()); + if (serviceLevelInputToken != null && serviceLevelOutputToken != null) { + inToken = serviceLevelInputToken; + outToken = serviceLevelOutputToken; + } + } + + if (inToken != null && outToken != null) { + // Pattern A: Explicit nested token like "EngineDefaults.Marker" + if (outToken.contains(".")) { + String[] parts = outToken.split("\\.", 2); + String memberName = parts[0]; + String nestedTokenName = parts[1]; + writer.write(" request.Set$L(result.Get$L().Get$L());", ServiceNameUtil.capitalize(inToken), ServiceNameUtil.capitalize(memberName), ServiceNameUtil.capitalize(nestedTokenName)); + } + // Pattern B: Check if token is on top-level output + else if (ShapeUtil.hasTopLevelMember(context.getModel(), op, outToken)) { + writer.write(" request.Set$L(result.Get$L());", ServiceNameUtil.capitalize(inToken), ServiceNameUtil.capitalize(outToken)); + } + // Pattern C: Find wrapper member containing the token + else { + String wrapperMember = ShapeUtil.findWrapperMemberContainingToken(context.getModel(), op, outToken); + if (wrapperMember != null) { + writer.write(" request.Set$L(result.Get$L().Get$L());", ServiceNameUtil.capitalize(inToken), ServiceNameUtil.capitalize(wrapperMember), ServiceNameUtil.capitalize(outToken)); + } else { + writer.write(" (void)result; (void)request; // Token not found"); + } + } + } else { + writer.write(" (void)result; (void)request; // Unused parameters"); + } + }); + }); + + writer.write("") + .writeNamespaceClose("Pagination") + .writeNamespaceClose(serviceName) + .writeNamespaceClose("Aws"); + } +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin new file mode 100644 index 00000000000..f4e06486044 --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin @@ -0,0 +1 @@ +com.amazonaws.util.awsclientsmithygenerator.generators.PaginationCodegenPlugin diff --git a/tools/code-generation/smithy/codegen/cpp-pagination/build.gradle.kts b/tools/code-generation/smithy/codegen/cpp-pagination/build.gradle.kts new file mode 100644 index 00000000000..e6fc1e2a689 --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination/build.gradle.kts @@ -0,0 +1,87 @@ +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.aws.traits.ServiceTrait +import org.gradle.api.logging.Logging +import kotlin.streams.toList + +val logger = Logging.getLogger("MyLogger") + +plugins { + id("java-library") + id("software.amazon.smithy.gradle.smithy-base").version("1.3.0") +} + +repositories { + mavenLocal() + mavenCentral() +} + +buildscript { + dependencies { + classpath(codegen.model) + classpath(codegen.aws.traits) + classpath(codegen.rules.engine) + } +} + +dependencies { + implementation(project(":cpp-pagination-codegen")) + implementation(codegen.aws.traits) + implementation(codegen.aws.cloudformation.traits) + implementation(codegen.aws.iam.traits) + implementation(codegen.aws.endpoints) + implementation(codegen.smoke.test.traits) + implementation(codegen.aws.smoke.test.model) + implementation(codegen.waiters) +} + +tasks.jar { + enabled = false +} + +tasks.register("generate-smithy-build") { + doLast { + val projectionsBuilder = Node.objectNodeBuilder() + val models = project.file("../../api-descriptions") + val filteredServices: String = project.findProperty("servicesFilter")?.toString() ?: "" + val filteredServiceList = filteredServices.split(",").map { it.trim() }.filter { it.isNotEmpty() } + val c2jMapStr: String = project.findProperty("c2jMap")?.toString() ?: "" + + fileTree(models).filter { it.isFile }.files.forEach eachFile@{ file -> + val model = Model.assembler() + .addImport(file.absolutePath) + // Grab the result directly rather than worrying about checking for errors via unwrap. + // All we care about here is the service shape, any unchecked errors will be exposed + // as part of the actual build task done by the smithy gradle plugin. + .assemble().result.get() + val services = model.shapes(ServiceShape::class.java).sorted().toList() + if (services.size != 1) return@eachFile + + val service = services[0] + val serviceTrait = service.getTrait(ServiceTrait::class.java).get() + val sdkId = serviceTrait.sdkId.replace(" ", "-").replace("_", "-").lowercase() + + if (filteredServiceList.isNotEmpty() && sdkId !in filteredServiceList) return@eachFile + + val projectionContents = Node.objectNodeBuilder() + .withMember("imports", Node.fromStrings("${models.absolutePath}${File.separator}${file.name}")) + .withMember("plugins", Node.objectNode() + .withMember("cpp-codegen-pagination-plugin", Node.objectNodeBuilder() + .withMember("c2jMap", Node.from(c2jMapStr)) + .build())) + .build() + + projectionsBuilder.withMember("$sdkId.${service.version.lowercase()}", projectionContents) + } + + val outputDirectoryArg = project.findProperty("outputDirectory")?.toString() ?: "output" + file("smithy-build.json").writeText(Node.prettyPrintJson(Node.objectNodeBuilder() + .withMember("version", "1.0") + .withMember("projections", projectionsBuilder.build()) + .withMember("outputDirectory", outputDirectoryArg) + .build())) + } +} + +tasks["build"].dependsOn(tasks["generate-smithy-build"]) diff --git a/tools/code-generation/smithy/codegen/cpp-pagination/smithy-build.json b/tools/code-generation/smithy/codegen/cpp-pagination/smithy-build.json new file mode 100644 index 00000000000..1008552afa3 --- /dev/null +++ b/tools/code-generation/smithy/codegen/cpp-pagination/smithy-build.json @@ -0,0 +1,186 @@ +{ + "version": "1.0", + "projections": { + "accessanalyzer.2019-11-01": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/accessanalyzer.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "elasticache.2015-02-02": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/elasticache.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "bedrock-agent-runtime.2023-07-26": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/bedrock-agent-runtime.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "amp.2020-08-01": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/amp.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "chatbot.2017-10-11": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/chatbot.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "lex-models-v2.2020-08-07": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/lex-models-v2.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "transfer.2018-11-05": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/transfer.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "ecr.2015-09-21": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/ecr.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "migration-hub.2017-05-31": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/migration-hub.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "ec2.2016-11-15": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/ec2.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "b2bi.2022-06-23": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/b2bi.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "s3.2006-03-01": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/s3.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "appintegrations.2020-07-29": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/appintegrations.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "amplify.2017-07-25": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/amplify.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "codestar-notifications.2019-10-15": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/codestar-notifications.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "cloudfront.2020-05-31": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/cloudfront.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "medialive.2017-10-14": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/medialive.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + }, + "dynamodb.2012-08-10": { + "imports": [ + "/Users/cailinn/Desktop/workspace/aws-sdk-cpp/tools/code-generation/smithy/api-descriptions/dynamodb.json" + ], + "plugins": { + "cpp-codegen-pagination-plugin": { + "c2jMap": "{\"api-gateway\": \"apigateway\", \"application-auto-scaling\": \"application-autoscaling\", \"app-mesh\": \"appmesh\", \"auto-scaling\": \"autoscaling\", \"auto-scaling-plans\": \"autoscaling-plans\", \"cloudhsm-v2\": \"cloudhsmv2\", \"cloudsearch-domain\": \"cloudsearchdomain\", \"config-service\": \"config\", \"cost-and-usage-report-service\": \"cur\", \"data-pipeline\": \"datapipeline\", \"device-farm\": \"devicefarm\", \"direct-connect\": \"directconnect\", \"dynamodb-streams\": \"dynamodbstreams\", \"elastic-beanstalk\": \"elasticbeanstalk\", \"elastic-load-balancing\": \"elasticloadbalancing\", \"elastic-load-balancing-v2\": \"elasticloadbalancingv2\", \"global-accelerator\": \"globalaccelerator\", \"iot-1click-devices-service\": \"iot1click-devices\", \"iot-1click-projects\": \"iot1click-projects\", \"iot-data-plane\": \"iot-data\", \"iot-events-data\": \"iotevents-data\", \"iot-events\": \"iotevents\", \"iot-jobs-data-plane\": \"iot-jobs-data\", \"iot-wireless\": \"iotwireless\", \"kinesis-analytics\": \"kinesisanalytics\", \"kinesis-analytics-v2\": \"kinesisanalyticsv2\", \"kinesis-video\": \"kinesisvideo\", \"lex-models-v2\": \"lexv2-models\", \"lex-runtime-service\": \"lex\", \"lex-runtime-v2\": \"lexv2-runtime\", \"machine-learning\": \"machinelearning\", \"marketplace-commerce-analytics\": \"marketplacecommerceanalytics\", \"marketplace-entitlement-service\": \"marketplace-entitlement\", \"marketplace-metering\": \"meteringmarketplace\", \"migration-hub\": \"AWSMigrationHub\", \"mturk\": \"mturk-requester\", \"pinpoint-sms-voice\": \"sms-voice\", \"resource-groups-tagging-api\": \"resourcegroupstaggingapi\", \"route-53-domains\": \"route53domains\", \"route-53\": \"route53\", \"s3-control\": \"s3control\", \"sagemaker-runtime\": \"sagemaker-runtime\", \"secrets-manager\": \"secretsmanager\", \"serverlessapplicationrepository\": \"serverlessrepo\", \"service-catalog-appregistry\": \"servicecatalog-appregistry\", \"service-catalog\": \"servicecatalog\", \"transfer\": \"awstransfer\", \"cloudwatch\": \"monitoring\", \"cloudwatch-events\": \"events\", \"storage-gateway\": \"storagegateway\", \"efs\": \"elasticfilesystem\", \"emr\": \"elasticmapreduce\", \"ses\": \"email\", \"cognito-identity-provider\": \"cognito-idp\", \"cost-explorer\": \"ce\", \"application-discovery-service\": \"discovery\", \"database-migration-service\": \"dms\", \"sfn\": \"states\", \"lex-model-building-service\": \"lex-models\", \"cloudwatch-logs\": \"logs\", \"directory-service\": \"ds\", \"elasticsearch-service\": \"es\", \"importexport\": \"importexport\", \"sdb\": \"sdb\", \"transcribe-streaming\": \"transcribestreaming\"}" + } + } + } + }, + "outputDirectory": "output" +} \ No newline at end of file diff --git a/tools/code-generation/smithy/codegen/settings.gradle.kts b/tools/code-generation/smithy/codegen/settings.gradle.kts index bc2b770a273..e14d59a763f 100644 --- a/tools/code-generation/smithy/codegen/settings.gradle.kts +++ b/tools/code-generation/smithy/codegen/settings.gradle.kts @@ -2,6 +2,8 @@ rootProject.name = "smithy_code_generation" include("cpp-smoke-tests") include("cpp-smoke-tests-codegen") +include("cpp-pagination-codegen") +include("cpp-pagination") pluginManagement { repositories { diff --git a/tools/code-generation/smithy/codegen/smithy2c2j_service_map.json b/tools/code-generation/smithy/codegen/smithy2c2j_service_map.json index d4865618ff4..1ff0190e0ac 100644 --- a/tools/code-generation/smithy/codegen/smithy2c2j_service_map.json +++ b/tools/code-generation/smithy/codegen/smithy2c2j_service_map.json @@ -60,7 +60,7 @@ "lex-model-building-service":"lex-models", "cloudwatch-logs":"logs", "directory-service":"ds", - "elasticsearch-service ":"es", + "elasticsearch-service":"es", "importexport":"importexport", "sdb":"sdb", "transcribe-streaming":"transcribestreaming" diff --git a/tools/scripts/codegen/legacy_c2j_cpp_gen.py b/tools/scripts/codegen/legacy_c2j_cpp_gen.py index 7e1df6be35f..ee77eaa90c4 100644 --- a/tools/scripts/codegen/legacy_c2j_cpp_gen.py +++ b/tools/scripts/codegen/legacy_c2j_cpp_gen.py @@ -16,7 +16,6 @@ from concurrent.futures import ProcessPoolExecutor, wait, FIRST_COMPLETED, ALL_COMPLETED from pathlib import Path -from codegen.format_util import format_directories from codegen.include_tests_util import IncludeTestsUtil from codegen.model_utils import ServiceModel @@ -149,12 +148,6 @@ def generate(self, executor: ProcessPoolExecutor, max_workers: int, args: dict) print(f"Code generation done, (re)generated {len(done)} packages.") # Including defaults and partitions - # Format generated client code - generated_clients = [service for service in self.c2j_models.keys()] - if generated_clients: - client_dirs = [f"{self.output_location}/src/aws-cpp-sdk-{client}" for client in generated_clients] - format_directories(client_dirs) - return 0 def _init_common_java_cli(self, diff --git a/tools/scripts/codegen/pagination_gen.py b/tools/scripts/codegen/pagination_gen.py new file mode 100644 index 00000000000..5a70e731971 --- /dev/null +++ b/tools/scripts/codegen/pagination_gen.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 + +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0. + +""" +Wrapper for Smithy pagination generator +""" +import json +import os +import shutil +import subprocess +from typing import List + +SMITHY_GENERATOR_LOCATION = "tools/code-generation/smithy/codegen" +SMITHY_TO_C2J_MAP_FILE = "tools/code-generation/smithy/codegen/smithy2c2j_service_map.json" + + +class PaginationGen(object): + """Wrapper for Smithy pagination generator for C++ SDK""" + + def __init__(self, debug: bool, **kwargs): + self.debug = debug + with open(os.path.abspath(SMITHY_TO_C2J_MAP_FILE), 'r') as file: + self.smithy_c2j_data = json.load(file) + self.c2j_smithy_data = {value: key for key, value in self.smithy_c2j_data.items()} + + def generate(self, clients_to_build: set): + """Generate pagination APIs for SDK clients""" + smithy_services = [self.c2j_smithy_data.get(service, service) for service in clients_to_build] + if self.debug: + print(f"Generating pagination for: {','.join(smithy_services)}") + + if self._generate_pagination(smithy_services, json.dumps(self.smithy_c2j_data)): + target_dir = os.path.abspath("generated/src") + self._copy_cpp_codegen_contents( + os.path.abspath("tools/code-generation/smithy/codegen"), + "cpp-codegen-pagination-plugin", + target_dir + ) + return 0 + return -1 + + def _generate_pagination(self, smithy_services: List[str], smithy_c2j_data: str): + smithy_codegen_command = [ + "./gradlew", + ":cpp-pagination:build", + "-PservicesFilter=" + ",".join(smithy_services), + "-Pc2jMap=" + smithy_c2j_data + ] + + try: + if self.debug: + print(f"RUNNING: {' '.join(smithy_codegen_command)}\nFROM: {SMITHY_GENERATOR_LOCATION}") + + process = subprocess.run( + smithy_codegen_command, + timeout=6*60, + check=True, + capture_output=True, + text=True, + cwd=SMITHY_GENERATOR_LOCATION + ) + print("Pagination codegen successful!") + if self.debug: + print(process.stdout) + print(f"Pagination generation done, (re)generated {len(smithy_services)} package(s).") + return True + + except subprocess.CalledProcessError as e: + print(f"Command failed: {e.returncode}\nError: {e.stderr}") + return False + + def _copy_cpp_codegen_contents(self, top_level_dir: str, plugin_name: str, target_dir: str): + for root, dirs, files in os.walk(top_level_dir): + if plugin_name in dirs: + source_dir = os.path.join(root, plugin_name) + + # Extract service name from the projection directory + projection_dir = os.path.basename(os.path.dirname(source_dir)) + smithy_service_name = projection_dir.split('.')[0] # e.g., "api-gateway.2017-11-27" -> "api-gateway" + + # Map to c2j service name + c2j_service_name = self.smithy_c2j_data.get(smithy_service_name, smithy_service_name) + + service_target_dir = os.path.join(target_dir, f"aws-cpp-sdk-{c2j_service_name}") + os.makedirs(service_target_dir, exist_ok=True) + + for item in os.listdir(source_dir): + source_item = os.path.join(source_dir, item) + + # Handle test directories separately - move to generated/tests + if item == "generated" and os.path.isdir(source_item): + generated_source = os.path.join(source_item, "tests") + if os.path.exists(generated_source): + test_target_dir = os.path.abspath("generated/tests") + os.makedirs(test_target_dir, exist_ok=True) + shutil.copytree(generated_source, test_target_dir, dirs_exist_ok=True) + if self.debug: + print(f"Copied tests from '{generated_source}' to '{test_target_dir}'") + continue + + target_item = os.path.join(service_target_dir, item) + if os.path.isdir(source_item): + shutil.copytree(source_item, target_item, dirs_exist_ok=True) + else: + shutil.copy2(source_item, target_item) + + if self.debug: + print(f"Copied from '{source_dir}' to '{service_target_dir}'") + + # Cleanup output directory + output_dir = os.path.join(top_level_dir, "cpp-pagination/output") + if os.path.exists(output_dir): + shutil.rmtree(output_dir) + if self.debug: + print(f"Cleaned up '{output_dir}'") + diff --git a/tools/scripts/run_code_generation.py b/tools/scripts/run_code_generation.py index 014ee39ce3d..b88ee081179 100644 --- a/tools/scripts/run_code_generation.py +++ b/tools/scripts/run_code_generation.py @@ -13,6 +13,8 @@ from codegen.model_utils import ModelUtils from codegen.protocol_tests_gen import ProtocolTestsGen from codegen.smoke_tests_gen import SmokeTestsGen +from codegen.pagination_gen import PaginationGen +from codegen.format_util import format_directories def parse_arguments() -> dict: @@ -154,12 +156,26 @@ def main(): # generate code using smithy for all discoverable clients # clients_to_build check is present because user can generate only defaults or partitions or protocol-tests clients_to_build = model_utils.get_clients_to_build() + + if clients_to_build: + pagination_gen = PaginationGen(args["debug"]) + if pagination_gen.generate(clients_to_build) != 0: + print("ERROR: Failed to generate pagination!") + return -1 + if args["generate_smoke_tests"] and clients_to_build: smoke_tests_gen = SmokeTestsGen(args["debug"]) if smoke_tests_gen.generate(clients_to_build) != 0: print("ERROR: Failed to generate smoke test(s)!") return -1 + # Format all generated C++ code at the end + if clients_to_build: + client_dirs = [f"{args['output_location']}/src/aws-cpp-sdk-{client}" for client in clients_to_build] + existing_dirs = [d for d in client_dirs if os.path.exists(d)] + if existing_dirs: + format_directories(existing_dirs) + return 0