From d8766e4bafa518cdd1c34d43e5fd16b4c5311e57 Mon Sep 17 00:00:00 2001 From: kai lin Date: Wed, 7 Jan 2026 13:23:07 -0500 Subject: [PATCH 1/5] paginator template and unit test --- .../aws/core/utils/pagination/Paginator.h | 97 +++++++++ tests/aws-cpp-sdk-core-tests/CMakeLists.txt | 3 + .../utils/pagination/PaginatorTest.cpp | 198 ++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h create mode 100644 tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp diff --git a/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h b/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h new file mode 100644 index 00000000000..3fa67d82cde --- /dev/null +++ b/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h @@ -0,0 +1,97 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once + +namespace Aws +{ +namespace Utils +{ +namespace Pagination +{ + +template +class PagePaginator +{ +public: + using OutcomeType = typename OperationTraits::OutcomeType; + using ResultType = typename OperationTraits::ResultType; + + class PageIterator + { + public: + PageIterator() = default; // end iterator + + PageIterator(ServiceClient* client, const OperationRequest& firstReq) + : m_client(client), + m_request(firstReq), + m_atEnd(false) + { + FetchPage(); // load first page eagerly so * is valid after begin() + } + + const OutcomeType& operator*() const { return m_currentOutcome; } + + PageIterator& operator++() + { + if (m_atEnd) return *this; + + // If last fetch failed, end iteration + if (!m_currentOutcome.IsSuccess()) + { + m_atEnd = true; + return *this; + } + + // If no more results, end iteration + if (!OperationTraits::HasMoreResults(m_currentOutcome.GetResult())) + { + m_atEnd = true; + return *this; + } + + // Mutate iterator-owned request for next page + OperationTraits::SetNextRequest(m_currentOutcome.GetResult(), m_request); + FetchPage(); + return *this; + } + + bool operator!=(const PageIterator& other) const + { + return !(m_atEnd && other.m_atEnd); + } + + bool operator==(const PageIterator& other) const + { + return !(*this != other); + } + + private: + void FetchPage() + { + m_currentOutcome = OperationTraits::Invoke(*m_client, m_request); + } + + ServiceClient* m_client{}; + OperationRequest m_request{}; + OutcomeType m_currentOutcome{}; + bool m_atEnd{true}; + }; + + PagePaginator(ServiceClient* client, const OperationRequest& firstReq) + : m_client(client), + m_firstRequest(firstReq) {} + + PageIterator begin() const { return PageIterator(m_client, m_firstRequest); } + PageIterator end() const { return PageIterator(); } + +private: + ServiceClient* m_client{}; + OperationRequest m_firstRequest{}; +}; + +} // namespace Pagination +} // namespace Utils +} // namespace Aws \ No newline at end of file diff --git a/tests/aws-cpp-sdk-core-tests/CMakeLists.txt b/tests/aws-cpp-sdk-core-tests/CMakeLists.txt index 90406667b9a..c4c997a1bf0 100644 --- a/tests/aws-cpp-sdk-core-tests/CMakeLists.txt +++ b/tests/aws-cpp-sdk-core-tests/CMakeLists.txt @@ -12,6 +12,7 @@ file(GLOB AWS_CLIENT_SRC "${CMAKE_CURRENT_SOURCE_DIR}/aws/client/*.cpp") file(GLOB AWS_NET_SRC "${CMAKE_CURRENT_SOURCE_DIR}/aws/net/*.cpp") file(GLOB HTTP_SRC "${CMAKE_CURRENT_SOURCE_DIR}/http/*.cpp") file(GLOB UTILS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/utils/*.cpp") +file(GLOB UTILS_PAGINATION_SRC "${CMAKE_CURRENT_SOURCE_DIR}/utils/pagination/*.cpp") file(GLOB UTILS_CRYPTO_SRC "${CMAKE_CURRENT_SOURCE_DIR}/utils/crypto/*.cpp") file(GLOB UTILS_EVENT_SRC "${CMAKE_CURRENT_SOURCE_DIR}/utils/event/*.cpp") file(GLOB UTILS_JSON_SRC "${CMAKE_CURRENT_SOURCE_DIR}/utils/json/*.cpp") @@ -41,6 +42,7 @@ file(GLOB AWS_CPP_SDK_CORE_TESTS_SRC ${AWS_NET_SRC} ${HTTP_SRC} ${UTILS_SRC} + ${UTILS_PAGINATION_SRC} ${UTILS_CRYPTO_SRC} ${UTILS_EVENT_SRC} ${UTILS_JSON_SRC} @@ -69,6 +71,7 @@ if(PLATFORM_WINDOWS) source_group("Source Files\\http" FILES ${HTTP_SRC}) source_group("Source Files\\monitoring" FILES ${MONITORING_SRC}) source_group("Source Files\\utils" FILES ${UTILS_SRC}) + source_group("Source Files\\utils\\pagination" FILES ${UTILS_PAGINATION_SRC}) source_group("Source Files\\utils\\crypto" FILES ${UTILS_CRYPTO_SRC}) source_group("Source Files\\utils\\event" FILES ${UTILS_EVENT_SRC}) source_group("Source Files\\utils\\json" FILES ${UTILS_JSON_SRC}) diff --git a/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp b/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp new file mode 100644 index 00000000000..c173e694d40 --- /dev/null +++ b/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp @@ -0,0 +1,198 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include + +using namespace Aws::Utils::Pagination; +using namespace Aws::Utils; + +// Test types for pagination +struct TestResult { + Aws::Vector items; + Aws::String outputToken; + bool moreResults; + + const Aws::Vector& GetItems() const { return items; } + const Aws::String& GetOutputToken() const { return outputToken; } + bool GetMoreResults() const { return moreResults; } +}; + +struct TestOutcome { + bool success; + Aws::String error; + TestResult result; + + bool IsSuccess() const { return success; } + const Aws::String& GetError() const { return error; } + const TestResult& GetResult() const { return result; } +}; + +struct TestRequest { + Aws::String inputToken; + int limitKey = 3; + + void SetInputToken(const Aws::String& t) { inputToken = t; } + const Aws::String& GetInputToken() const { return inputToken; } +}; + +class TestClient { +private: + Aws::Vector allData; + bool shouldFail = false; + +public: + TestClient() : allData({"apple", "banana", "cherry", "dragon-fruit", "elderberry", "fig", "grape"}) {} + + void SetData(const Aws::Vector& data) { allData = data; } + void SetShouldFail(bool fail) { shouldFail = fail; } + void Reset() { + allData = {"apple", "banana", "cherry", "dragon-fruit", "elderberry", "fig", "grape"}; + shouldFail = false; + } + + TestOutcome ListItems(const TestRequest& request) { + if (shouldFail) { + return {false, "Request failed", {}}; + } + + int startIdx = 0; + if (!request.GetInputToken().empty()) { + startIdx = StringUtils::ConvertToInt32(request.GetInputToken().c_str()); + } + + TestResult result; + for (size_t i = 0; i < static_cast(request.limitKey) && startIdx + i < allData.size(); ++i) { + result.items.push_back(allData[startIdx + i]); + } + + int nextIdx = startIdx + request.limitKey; + if (static_cast(nextIdx) < allData.size()) { + result.outputToken = StringUtils::to_string(nextIdx); + result.moreResults = true; + } else { + result.moreResults = false; + } + + return {true, "", result}; + } +}; + +struct ListItemsTraits { + using OutcomeType = TestOutcome; + using ResultType = TestResult; + + static OutcomeType Invoke(TestClient& client, const TestRequest& request) { + return client.ListItems(request); + } + + static bool HasMoreResults(const TestResult& result) { + return result.GetMoreResults(); + } + + static void SetNextRequest(const TestResult& result, TestRequest& request) { + request.SetInputToken(result.GetOutputToken()); + } +}; + +class PaginatorTest : public Aws::Testing::AwsCppSdkGTestSuite +{ +protected: + void SetUp() override + { + client.Reset(); + } + + void TearDown() override + { + client.Reset(); + } + + TestClient client; + TestRequest request; +}; + +TEST_F(PaginatorTest, TestIteratesThroughAllPages) +{ + PagePaginator paginator(&client, request); + + Aws::Vector allItems; + int pageCount = 0; + + for (const auto& outcome : paginator) + { + ASSERT_TRUE(outcome.IsSuccess()); + pageCount++; + + const auto& page = outcome.GetResult(); + for (const auto& item : page.GetItems()) + { + allItems.push_back(item); + } + } + + EXPECT_EQ(pageCount, 3); // 7 items / 3 per page = 3 pages + EXPECT_EQ(allItems.size(), 7u); + EXPECT_STREQ(allItems[0].c_str(), "apple"); + EXPECT_STREQ(allItems[6].c_str(), "grape"); +} + +TEST_F(PaginatorTest, TestHandlesErrorGracefully) +{ + client.SetShouldFail(true); + PagePaginator paginator(&client, request); + + auto it = paginator.begin(); + ASSERT_TRUE(it != paginator.end()); + + const auto& outcome = *it; + EXPECT_FALSE(outcome.IsSuccess()); + EXPECT_STREQ(outcome.GetError().c_str(), "Request failed"); + + // Iterator should terminate after error + ++it; + EXPECT_TRUE(it == paginator.end()); +} + +TEST_F(PaginatorTest, TestEmptyResultSet) +{ + client.SetData({}); + PagePaginator paginator(&client, request); + + auto it = paginator.begin(); + ASSERT_TRUE(it != paginator.end()); + + const auto& outcome = *it; + EXPECT_TRUE(outcome.IsSuccess()); + EXPECT_TRUE(outcome.GetResult().GetItems().empty()); + EXPECT_FALSE(outcome.GetResult().GetMoreResults()); + + // Should terminate after first empty page + ++it; + EXPECT_TRUE(it == paginator.end()); +} + +TEST_F(PaginatorTest, TestBeginEndIteratorComparison) +{ + PagePaginator paginator(&client, request); + + auto begin = paginator.begin(); + auto end = paginator.end(); + + EXPECT_TRUE(begin != end); + EXPECT_FALSE(begin == end); + + // Iterate to end + while (begin != end) + { + ++begin; + } + + EXPECT_TRUE(begin == end); + EXPECT_FALSE(begin != end); +} \ No newline at end of file From fac1fbadc4808b09039bf1e1af5820dd5be8c25e Mon Sep 17 00:00:00 2001 From: kai lin Date: Wed, 7 Jan 2026 13:26:53 -0500 Subject: [PATCH 2/5] comment changes --- .../include/aws/core/utils/pagination/Paginator.h | 4 ++-- .../utils/pagination/PaginatorTest.cpp | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h b/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h index 3fa67d82cde..2e3a4f0a617 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h +++ b/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h @@ -22,14 +22,14 @@ class PagePaginator class PageIterator { public: - PageIterator() = default; // end iterator + PageIterator() = default; PageIterator(ServiceClient* client, const OperationRequest& firstReq) : m_client(client), m_request(firstReq), m_atEnd(false) { - FetchPage(); // load first page eagerly so * is valid after begin() + FetchPage(); } const OutcomeType& operator*() const { return m_currentOutcome; } diff --git a/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp b/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp index c173e694d40..e89c4770001 100644 --- a/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp @@ -12,7 +12,6 @@ using namespace Aws::Utils::Pagination; using namespace Aws::Utils; -// Test types for pagination struct TestResult { Aws::Vector items; Aws::String outputToken; @@ -153,8 +152,7 @@ TEST_F(PaginatorTest, TestHandlesErrorGracefully) const auto& outcome = *it; EXPECT_FALSE(outcome.IsSuccess()); EXPECT_STREQ(outcome.GetError().c_str(), "Request failed"); - - // Iterator should terminate after error + ++it; EXPECT_TRUE(it == paginator.end()); } @@ -171,8 +169,7 @@ TEST_F(PaginatorTest, TestEmptyResultSet) EXPECT_TRUE(outcome.IsSuccess()); EXPECT_TRUE(outcome.GetResult().GetItems().empty()); EXPECT_FALSE(outcome.GetResult().GetMoreResults()); - - // Should terminate after first empty page + ++it; EXPECT_TRUE(it == paginator.end()); } @@ -186,8 +183,7 @@ TEST_F(PaginatorTest, TestBeginEndIteratorComparison) EXPECT_TRUE(begin != end); EXPECT_FALSE(begin == end); - - // Iterate to end + while (begin != end) { ++begin; From de020032f240915e77020520fe522b809fc3ce86 Mon Sep 17 00:00:00 2001 From: kai lin Date: Wed, 7 Jan 2026 13:26:53 -0500 Subject: [PATCH 3/5] change testclient to paginatorclient --- .../utils/pagination/PaginatorTest.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp b/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp index e89c4770001..1963eb544f2 100644 --- a/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp @@ -16,7 +16,7 @@ struct TestResult { Aws::Vector items; Aws::String outputToken; bool moreResults; - + const Aws::Vector& GetItems() const { return items; } const Aws::String& GetOutputToken() const { return outputToken; } bool GetMoreResults() const { return moreResults; } @@ -26,7 +26,7 @@ struct TestOutcome { bool success; Aws::String error; TestResult result; - + bool IsSuccess() const { return success; } const Aws::String& GetError() const { return error; } const TestResult& GetResult() const { return result; } @@ -40,13 +40,13 @@ struct TestRequest { const Aws::String& GetInputToken() const { return inputToken; } }; -class TestClient { +class PaginatorTestClient { private: Aws::Vector allData; bool shouldFail = false; public: - TestClient() : allData({"apple", "banana", "cherry", "dragon-fruit", "elderberry", "fig", "grape"}) {} + PaginatorTestClient() : allData({"apple", "banana", "cherry", "dragon-fruit", "elderberry", "fig", "grape"}) {} void SetData(const Aws::Vector& data) { allData = data; } void SetShouldFail(bool fail) { shouldFail = fail; } @@ -86,7 +86,7 @@ struct ListItemsTraits { using OutcomeType = TestOutcome; using ResultType = TestResult; - static OutcomeType Invoke(TestClient& client, const TestRequest& request) { + static OutcomeType Invoke(PaginatorTestClient& client, const TestRequest& request) { return client.ListItems(request); } @@ -112,13 +112,13 @@ class PaginatorTest : public Aws::Testing::AwsCppSdkGTestSuite client.Reset(); } - TestClient client; + PaginatorTestClient client; TestRequest request; }; TEST_F(PaginatorTest, TestIteratesThroughAllPages) { - PagePaginator paginator(&client, request); + PagePaginator paginator(&client, request); Aws::Vector allItems; int pageCount = 0; @@ -144,7 +144,7 @@ TEST_F(PaginatorTest, TestIteratesThroughAllPages) TEST_F(PaginatorTest, TestHandlesErrorGracefully) { client.SetShouldFail(true); - PagePaginator paginator(&client, request); + PagePaginator paginator(&client, request); auto it = paginator.begin(); ASSERT_TRUE(it != paginator.end()); @@ -160,7 +160,7 @@ TEST_F(PaginatorTest, TestHandlesErrorGracefully) TEST_F(PaginatorTest, TestEmptyResultSet) { client.SetData({}); - PagePaginator paginator(&client, request); + PagePaginator paginator(&client, request); auto it = paginator.begin(); ASSERT_TRUE(it != paginator.end()); @@ -176,7 +176,7 @@ TEST_F(PaginatorTest, TestEmptyResultSet) TEST_F(PaginatorTest, TestBeginEndIteratorComparison) { - PagePaginator paginator(&client, request); + PagePaginator paginator(&client, request); auto begin = paginator.begin(); auto end = paginator.end(); From 623012f1602d6e66ff39831bb220c3768f3dc442 Mon Sep 17 00:00:00 2001 From: kai lin Date: Fri, 9 Jan 2026 11:30:47 -0500 Subject: [PATCH 4/5] added iterator trait --- .../aws/core/utils/pagination/Paginator.h | 22 +++++++++--- .../utils/pagination/PaginatorTest.cpp | 36 +++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h b/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h index 2e3a4f0a617..a7c4d64f2f0 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h +++ b/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h @@ -5,6 +5,9 @@ #pragma once +#include +#include + namespace Aws { namespace Utils @@ -22,6 +25,12 @@ class PagePaginator class PageIterator { public: + using iterator_category = std::input_iterator_tag; + using value_type = OutcomeType; + using difference_type = std::ptrdiff_t; + using pointer = const OutcomeType*; + using reference = const OutcomeType&; + PageIterator() = default; PageIterator(ServiceClient* client, const OperationRequest& firstReq) @@ -55,17 +64,22 @@ class PagePaginator // Mutate iterator-owned request for next page OperationTraits::SetNextRequest(m_currentOutcome.GetResult(), m_request); FetchPage(); + + // If the fetch we just did failed, don't end iteration yet + // Let the customer see the error on the next dereference + // The check at the top will end iteration on the next ++ + return *this; } - bool operator!=(const PageIterator& other) const + bool operator==(const PageIterator& other) const { - return !(m_atEnd && other.m_atEnd); + return m_atEnd && other.m_atEnd; } - bool operator==(const PageIterator& other) const + bool operator!=(const PageIterator& other) const { - return !(*this != other); + return !(*this == other); } private: diff --git a/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp b/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp index 1963eb544f2..edfab316b33 100644 --- a/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp @@ -44,15 +44,20 @@ class PaginatorTestClient { private: Aws::Vector allData; bool shouldFail = false; + int failOnPage = -1; + int currentPage = 0; public: PaginatorTestClient() : allData({"apple", "banana", "cherry", "dragon-fruit", "elderberry", "fig", "grape"}) {} void SetData(const Aws::Vector& data) { allData = data; } void SetShouldFail(bool fail) { shouldFail = fail; } + void SetFailOnPage(int page) { failOnPage = page; } void Reset() { allData = {"apple", "banana", "cherry", "dragon-fruit", "elderberry", "fig", "grape"}; shouldFail = false; + failOnPage = -1; + currentPage = 0; } TestOutcome ListItems(const TestRequest& request) { @@ -60,6 +65,12 @@ class PaginatorTestClient { return {false, "Request failed", {}}; } + if (failOnPage >= 0 && currentPage == failOnPage) { + currentPage++; + return {false, "Page " + StringUtils::to_string(failOnPage) + " failed", {}}; + } + currentPage++; + int startIdx = 0; if (!request.GetInputToken().empty()) { startIdx = StringUtils::ConvertToInt32(request.GetInputToken().c_str()); @@ -191,4 +202,29 @@ TEST_F(PaginatorTest, TestBeginEndIteratorComparison) EXPECT_TRUE(begin == end); EXPECT_FALSE(begin != end); +} + +TEST_F(PaginatorTest, TestHandlesErrorOnSecondPage) +{ + client.SetFailOnPage(1); // Fail on second page (0-indexed) + PagePaginator paginator(&client, request); + + auto it = paginator.begin(); + + // First page should succeed + ASSERT_TRUE(it != paginator.end()); + EXPECT_TRUE((*it).IsSuccess()); + EXPECT_EQ((*it).GetResult().GetItems().size(), 3u); + + // Move to second page + ++it; + + // Second page should have the error + ASSERT_TRUE(it != paginator.end()); + EXPECT_FALSE((*it).IsSuccess()); + EXPECT_TRUE((*it).GetError().find("Page 1 failed") != Aws::String::npos); + + // After error, iteration should end + ++it; + EXPECT_TRUE(it == paginator.end()); } \ No newline at end of file From 7e85d39884be3dcde28b871e2109057126b853b9 Mon Sep 17 00:00:00 2001 From: kai lin Date: Fri, 9 Jan 2026 13:28:50 -0500 Subject: [PATCH 5/5] added endSentinel to remove default ctor. changed wild pointer to shared pointer --- .../aws/core/utils/pagination/Paginator.h | 30 +++++++------------ .../utils/pagination/PaginatorTest.cpp | 21 +++++++++---- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h b/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h index a7c4d64f2f0..8ff25228129 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h +++ b/src/aws-cpp-sdk-core/include/aws/core/utils/pagination/Paginator.h @@ -7,6 +7,7 @@ #include #include +#include namespace Aws { @@ -22,6 +23,8 @@ class PagePaginator using OutcomeType = typename OperationTraits::OutcomeType; using ResultType = typename OperationTraits::ResultType; + struct EndSentinel {}; + class PageIterator { public: @@ -31,9 +34,7 @@ class PagePaginator using pointer = const OutcomeType*; using reference = const OutcomeType&; - PageIterator() = default; - - PageIterator(ServiceClient* client, const OperationRequest& firstReq) + PageIterator(std::shared_ptr client, const OperationRequest& firstReq) : m_client(client), m_request(firstReq), m_atEnd(false) @@ -65,22 +66,11 @@ class PagePaginator OperationTraits::SetNextRequest(m_currentOutcome.GetResult(), m_request); FetchPage(); - // If the fetch we just did failed, don't end iteration yet - // Let the customer see the error on the next dereference - // The check at the top will end iteration on the next ++ - return *this; } - bool operator==(const PageIterator& other) const - { - return m_atEnd && other.m_atEnd; - } - - bool operator!=(const PageIterator& other) const - { - return !(*this == other); - } + bool operator==(EndSentinel) const { return m_atEnd; } + bool operator!=(EndSentinel) const { return !m_atEnd; } private: void FetchPage() @@ -88,21 +78,21 @@ class PagePaginator m_currentOutcome = OperationTraits::Invoke(*m_client, m_request); } - ServiceClient* m_client{}; + std::shared_ptr m_client; OperationRequest m_request{}; OutcomeType m_currentOutcome{}; bool m_atEnd{true}; }; - PagePaginator(ServiceClient* client, const OperationRequest& firstReq) + PagePaginator(std::shared_ptr client, const OperationRequest& firstReq) : m_client(client), m_firstRequest(firstReq) {} PageIterator begin() const { return PageIterator(m_client, m_firstRequest); } - PageIterator end() const { return PageIterator(); } + EndSentinel end() const { return {}; } private: - ServiceClient* m_client{}; + std::shared_ptr m_client; OperationRequest m_firstRequest{}; }; diff --git a/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp b/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp index edfab316b33..bcab9afe15a 100644 --- a/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/utils/pagination/PaginatorTest.cpp @@ -129,13 +129,17 @@ class PaginatorTest : public Aws::Testing::AwsCppSdkGTestSuite TEST_F(PaginatorTest, TestIteratesThroughAllPages) { - PagePaginator paginator(&client, request); + auto clientPtr = std::make_shared(client); + PagePaginator paginator(clientPtr, request); Aws::Vector allItems; int pageCount = 0; - for (const auto& outcome : paginator) + auto it = paginator.begin(); + auto end = paginator.end(); + while (it != end) { + const auto& outcome = *it; ASSERT_TRUE(outcome.IsSuccess()); pageCount++; @@ -144,6 +148,7 @@ TEST_F(PaginatorTest, TestIteratesThroughAllPages) { allItems.push_back(item); } + ++it; } EXPECT_EQ(pageCount, 3); // 7 items / 3 per page = 3 pages @@ -155,7 +160,8 @@ TEST_F(PaginatorTest, TestIteratesThroughAllPages) TEST_F(PaginatorTest, TestHandlesErrorGracefully) { client.SetShouldFail(true); - PagePaginator paginator(&client, request); + auto clientPtr = std::make_shared(client); + PagePaginator paginator(clientPtr, request); auto it = paginator.begin(); ASSERT_TRUE(it != paginator.end()); @@ -171,7 +177,8 @@ TEST_F(PaginatorTest, TestHandlesErrorGracefully) TEST_F(PaginatorTest, TestEmptyResultSet) { client.SetData({}); - PagePaginator paginator(&client, request); + auto clientPtr = std::make_shared(client); + PagePaginator paginator(clientPtr, request); auto it = paginator.begin(); ASSERT_TRUE(it != paginator.end()); @@ -187,7 +194,8 @@ TEST_F(PaginatorTest, TestEmptyResultSet) TEST_F(PaginatorTest, TestBeginEndIteratorComparison) { - PagePaginator paginator(&client, request); + auto clientPtr = std::make_shared(client); + PagePaginator paginator(clientPtr, request); auto begin = paginator.begin(); auto end = paginator.end(); @@ -207,7 +215,8 @@ TEST_F(PaginatorTest, TestBeginEndIteratorComparison) TEST_F(PaginatorTest, TestHandlesErrorOnSecondPage) { client.SetFailOnPage(1); // Fail on second page (0-indexed) - PagePaginator paginator(&client, request); + auto clientPtr = std::make_shared(client); + PagePaginator paginator(clientPtr, request); auto it = paginator.begin();