From eccdc7a75dd041f7d0a48d99d4b91743686a6962 Mon Sep 17 00:00:00 2001 From: Mykola Malik Date: Tue, 28 Sep 2021 13:09:23 +0300 Subject: [PATCH] Add RetrySettings for authentication olp::authentication::AuthenticationSettings and olp::authentication::Settingsstructs are extended with RetrySettings. AuthentificationClientImpl now uses user defined retry settings instead of hardcoded values. Fixed double token request bug in TokenProvider. Relates-To: OLPEDGE-2634, OLPSUP-15460 Signed-off-by: Mykola Malik --- .../authentication/AuthenticationSettings.h | 9 +- .../include/olp/authentication/Settings.h | 7 + .../olp/authentication/TokenProvider.h | 8 +- .../src/AuthenticationClientImpl.cpp | 48 +-- .../src/AuthenticationClientUtils.cpp | 5 +- .../src/TokenEndpoint.cpp | 3 +- olp-cpp-sdk-core/CMakeLists.txt | 3 +- .../olp/core/client/OlpClientSettings.h | 66 +--- .../include/olp/core/client/RetrySettings.h | 94 ++++++ ...lpClientSettings.cpp => RetrySettings.cpp} | 2 +- .../AuthenticationClientTest.cpp | 284 +++++++++++++++++- .../TokenProviderTest.cpp | 52 +++- 12 files changed, 466 insertions(+), 115 deletions(-) create mode 100644 olp-cpp-sdk-core/include/olp/core/client/RetrySettings.h rename olp-cpp-sdk-core/src/client/{OlpClientSettings.cpp => RetrySettings.cpp} (97%) diff --git a/olp-cpp-sdk-authentication/include/olp/authentication/AuthenticationSettings.h b/olp-cpp-sdk-authentication/include/olp/authentication/AuthenticationSettings.h index 720a57130..a7f46775e 100644 --- a/olp-cpp-sdk-authentication/include/olp/authentication/AuthenticationSettings.h +++ b/olp-cpp-sdk-authentication/include/olp/authentication/AuthenticationSettings.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 HERE Europe B.V. + * Copyright (C) 2020-2021 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -89,6 +90,12 @@ struct AUTHENTICATION_API AuthenticationSettings { * authentication server. */ bool use_system_time{true}; + + /** + * @brief A collection of settings that controls how failed requests should be + * treated. + */ + client::RetrySettings retry_settings; }; } // namespace authentication diff --git a/olp-cpp-sdk-authentication/include/olp/authentication/Settings.h b/olp-cpp-sdk-authentication/include/olp/authentication/Settings.h index 066b79c73..3b1e6a2b8 100644 --- a/olp-cpp-sdk-authentication/include/olp/authentication/Settings.h +++ b/olp-cpp-sdk-authentication/include/olp/authentication/Settings.h @@ -23,6 +23,7 @@ #include #include +#include #include #include "AuthenticationApi.h" @@ -98,6 +99,12 @@ struct AUTHENTICATION_API Settings { * authentication server. */ bool use_system_time{true}; + + /** + * @brief A collection of settings that controls how failed requests should be + * treated. + */ + client::RetrySettings retry_settings; }; } // namespace authentication diff --git a/olp-cpp-sdk-authentication/include/olp/authentication/TokenProvider.h b/olp-cpp-sdk-authentication/include/olp/authentication/TokenProvider.h index 948c53279..d7817b2c5 100644 --- a/olp-cpp-sdk-authentication/include/olp/authentication/TokenProvider.h +++ b/olp-cpp-sdk-authentication/include/olp/authentication/TokenProvider.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 HERE Europe B.V. + * Copyright (C) 2019-2021 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -119,9 +119,9 @@ class TokenProvider { /// @copydoc TokenProvider::operator()() std::string operator()() const { - return GetResponse().IsSuccessful() - ? GetResponse().GetResult().GetAccessToken() - : ""; + auto response = GetResponse(); + return response.IsSuccessful() ? response.GetResult().GetAccessToken() + : ""; } /// @copydoc TokenProvider::GetErrorResponse() diff --git a/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp b/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp index 9bab45e37..c478711c3 100644 --- a/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp +++ b/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 HERE Europe B.V. + * Copyright (C) 2020-2021 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -99,9 +99,6 @@ constexpr auto kOperator = "operator"; constexpr auto kErrorWrongTimestamp = 401204; constexpr auto kLogTag = "AuthenticationClient"; -constexpr auto kDefaultRetryCount = 3; -constexpr auto kDefaultRetryTime = std::chrono::milliseconds(200); - bool HasWrongTimestamp(olp::authentication::SignInResult& result) { const auto& error_response = result.GetErrorResponse(); const auto status = result.GetStatus(); @@ -109,9 +106,10 @@ bool HasWrongTimestamp(olp::authentication::SignInResult& result) { error_response.code == kErrorWrongTimestamp; } -void RetryDelay(size_t retry) { - client::ExponentialBackdownStrategy retry_delay; - std::this_thread::sleep_for(retry_delay(kDefaultRetryTime, retry)); +void RetryDelay(const client::RetrySettings& retry_settings, size_t retry) { + std::this_thread::sleep_for(retry_settings.backdown_strategy( + std::chrono::milliseconds(retry_settings.initial_backdown_period), + retry)); } client::OlpClient::RequestBodyType GenerateAppleSignInBody( @@ -284,7 +282,9 @@ client::CancellationToken AuthenticationClientImpl::SignInClient( SignInResult response; - for (auto retry = 0; retry < kDefaultRetryCount; ++retry) { + const auto& retry_settings = settings_.retry_settings; + + for (auto retry = 0; retry < retry_settings.max_attempts; ++retry) { if (context.IsCancelled()) { return client::ApiError::Cancelled(); } @@ -310,8 +310,8 @@ client::CancellationToken AuthenticationClientImpl::SignInClient( response = ParseAuthResponse(status, auth_response.response); - if (client::DefaultRetryCondition(auth_response)) { - RetryDelay(retry); + if (retry_settings.retry_condition(auth_response)) { + RetryDelay(retry_settings, retry); continue; } @@ -492,7 +492,9 @@ client::CancellationToken AuthenticationClientImpl::HandleUserRequest( SignInUserResult response; - for (auto retry = 0; retry < kDefaultRetryCount; ++retry) { + const auto& retry_settings = settings_.retry_settings; + + for (auto retry = 0; retry < retry_settings.max_attempts; ++retry) { if (context.IsCancelled()) { return client::ApiError::Cancelled(); } @@ -517,8 +519,8 @@ client::CancellationToken AuthenticationClientImpl::HandleUserRequest( response = ParseUserAuthResponse(status, auth_response.response); - if (client::DefaultRetryCondition(auth_response)) { - RetryDelay(retry); + if (retry_settings.retry_condition(auth_response)) { + RetryDelay(retry_settings, retry); continue; } @@ -560,10 +562,12 @@ client::CancellationToken AuthenticationClientImpl::SignUpHereUser( std::string url = settings_.token_endpoint_url; url.append(kUserEndpoint); http::NetworkRequest request(url); - http::NetworkSettings network_settings; - if (settings_.network_proxy_settings) { - network_settings.WithProxySettings(settings_.network_proxy_settings.get()); - } + auto network_settings = + http::NetworkSettings() + .WithTransferTimeout(settings_.retry_settings.timeout) + .WithConnectionTimeout(settings_.retry_settings.timeout) + .WithProxySettings(settings_.network_proxy_settings.get_value_or({})); + request.WithVerb(http::NetworkRequest::HttpVerb::POST); auto auth_header = GenerateAuthorizationHeader( @@ -637,10 +641,12 @@ client::CancellationToken AuthenticationClientImpl::SignOut( std::string url = settings_.token_endpoint_url; url.append(kSignoutEndpoint); http::NetworkRequest request(url); - http::NetworkSettings network_settings; - if (settings_.network_proxy_settings) { - network_settings.WithProxySettings(settings_.network_proxy_settings.get()); - } + auto network_settings = + http::NetworkSettings() + .WithTransferTimeout(settings_.retry_settings.timeout) + .WithConnectionTimeout(settings_.retry_settings.timeout) + .WithProxySettings(settings_.network_proxy_settings.get_value_or({})); + request.WithVerb(http::NetworkRequest::HttpVerb::POST); request.WithHeader(http::kAuthorizationHeader, GenerateBearerHeader(access_token)); diff --git a/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp b/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp index dd39adf32..36f2762fd 100644 --- a/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp +++ b/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 HERE Europe B.V. + * Copyright (C) 2020-2021 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -264,8 +264,7 @@ client::OlpClient CreateOlpClient( settings.network_request_handler = auth_settings.network_request_handler; settings.authentication_settings = authentication_settings; settings.proxy_settings = auth_settings.network_proxy_settings; - settings.retry_settings.backdown_strategy = - client::ExponentialBackdownStrategy(); + settings.retry_settings = auth_settings.retry_settings; if (!retry) { settings.retry_settings.max_attempts = 0; diff --git a/olp-cpp-sdk-authentication/src/TokenEndpoint.cpp b/olp-cpp-sdk-authentication/src/TokenEndpoint.cpp index 71ba13c84..f95bcb952 100644 --- a/olp-cpp-sdk-authentication/src/TokenEndpoint.cpp +++ b/olp-cpp-sdk-authentication/src/TokenEndpoint.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 HERE Europe B.V. + * Copyright (C) 2019-2021 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,6 +45,7 @@ AuthenticationSettings ConvertSettings(Settings settings) { auth_settings.network_request_handler = settings.network_request_handler; auth_settings.token_endpoint_url = settings.token_endpoint_url; auth_settings.use_system_time = settings.use_system_time; + auth_settings.retry_settings = settings.retry_settings; return auth_settings; } } // namespace diff --git a/olp-cpp-sdk-core/CMakeLists.txt b/olp-cpp-sdk-core/CMakeLists.txt index 1b33f4449..429fa279f 100644 --- a/olp-cpp-sdk-core/CMakeLists.txt +++ b/olp-cpp-sdk-core/CMakeLists.txt @@ -81,6 +81,7 @@ set(OLP_SDK_CLIENT_HEADERS ./include/olp/core/client/OlpClientSettings.h ./include/olp/core/client/OlpClientSettingsFactory.h ./include/olp/core/client/PendingRequests.h + ./include/olp/core/client/RetrySettings.h ./include/olp/core/client/TaskContext.h ) @@ -253,11 +254,11 @@ set(OLP_SDK_CLIENT_SOURCES ./src/client/HRN.cpp ./src/client/OlpClient.cpp ./src/client/OlpClientFactory.cpp - ./src/client/OlpClientSettings.cpp ./src/client/OlpClientSettingsFactory.cpp ./src/client/PendingRequests.cpp ./src/client/PendingUrlRequests.h ./src/client/PendingUrlRequests.cpp + ./src/client/RetrySettings.cpp ./src/client/Tokenizer.h ) diff --git a/olp-cpp-sdk-core/include/olp/core/client/OlpClientSettings.h b/olp-cpp-sdk-core/include/olp/core/client/OlpClientSettings.h index cdd23981f..bf7a1627f 100644 --- a/olp-cpp-sdk-core/include/olp/core/client/OlpClientSettings.h +++ b/olp-cpp-sdk-core/include/olp/core/client/OlpClientSettings.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 HERE Europe B.V. + * Copyright (C) 2019-2021 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,11 +26,11 @@ #include -#include #include #include #include #include +#include #include namespace olp { @@ -61,11 +61,6 @@ using NetworkAsyncCallback = std::function; */ using NetworkAsyncCancel = std::function; -/** - * @brief The default retry condition that disables retries. - */ -CORE_API bool DefaultRetryCondition(const olp::client::HttpResponse& response); - /** * @brief A set of settings that manages the `TokenProviderCallback` and * `TokenProviderCancelCallback` functions. @@ -146,63 +141,6 @@ struct CORE_API AuthenticationSettings { boost::optional cancel; }; -/** - * @brief A collection of settings that controls how failed requests should be - * treated by the Data SDK. - * - * For example, it specifies whether the failed request should be retried, how - * long Data SDK needs to wait for the next retry attempt, the number of maximum - * retries, and so on. - * - * You can customize all of these settings. The settings are used internally by - * the `OlpClient` class. - */ -struct CORE_API RetrySettings { - /** - * @brief Calculates the number of retry timeouts based on - * the initial backdown duration and retries count. - */ - using BackdownStrategy = std::function; - - /** - * @brief Checks whether the retry is desired. - * - * @see `HttpResponse` for more details. - */ - using RetryCondition = std::function; - - /** - * @brief The number of attempts. - * - * The default value is 3. - */ - int max_attempts = 3; - - /** - * @brief The connection timeout limit (in seconds). - */ - int timeout = 60; - - /** - * @brief The period between the error and the first retry attempt (in - * milliseconds). - */ - int initial_backdown_period = 200; - - /** - * @brief The backdown strategy. - * - * Defines the delay between retries on a failed request. - */ - BackdownStrategy backdown_strategy = ExponentialBackdownStrategy(); - - /** - * @brief Evaluates responses to determine if the retry should be attempted. - */ - RetryCondition retry_condition = DefaultRetryCondition; -}; - /** * @brief Settings to provide URLs for API lookup requests. */ diff --git a/olp-cpp-sdk-core/include/olp/core/client/RetrySettings.h b/olp-cpp-sdk-core/include/olp/core/client/RetrySettings.h new file mode 100644 index 000000000..eb842a18c --- /dev/null +++ b/olp-cpp-sdk-core/include/olp/core/client/RetrySettings.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2021 HERE Europe B.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ + +#pragma once + +#include +#include + +#include +#include + +namespace olp { +namespace client { + +/** + * @brief The default retry condition that disables retries. + */ +CORE_API bool DefaultRetryCondition(const olp::client::HttpResponse& response); + +/** + * @brief A collection of settings that controls how failed requests should be + * treated by the Data SDK. + * + * For example, it specifies whether the failed request should be retried, how + * long Data SDK needs to wait for the next retry attempt, the number of maximum + * retries, and so on. + * + * You can customize all of these settings. The settings are used internally by + * the `OlpClient` class. + */ +struct CORE_API RetrySettings { + /** + * @brief Calculates the number of retry timeouts based on + * the initial backdown duration and retries count. + */ + using BackdownStrategy = std::function; + + /** + * @brief Checks whether the retry is desired. + * + * @see `HttpResponse` for more details. + */ + using RetryCondition = std::function; + + /** + * @brief The number of attempts. + * + * The default value is 3. + */ + int max_attempts = 3; + + /** + * @brief The connection timeout limit (in seconds). + */ + int timeout = 60; + + /** + * @brief The period between the error and the first retry attempt (in + * milliseconds). + */ + int initial_backdown_period = 200; + + /** + * @brief The backdown strategy. + * + * Defines the delay between retries on a failed request. + */ + BackdownStrategy backdown_strategy = ExponentialBackdownStrategy(); + + /** + * @brief Evaluates responses to determine if the retry should be attempted. + */ + RetryCondition retry_condition = DefaultRetryCondition; +}; + +} // namespace client +} // namespace olp diff --git a/olp-cpp-sdk-core/src/client/OlpClientSettings.cpp b/olp-cpp-sdk-core/src/client/RetrySettings.cpp similarity index 97% rename from olp-cpp-sdk-core/src/client/OlpClientSettings.cpp rename to olp-cpp-sdk-core/src/client/RetrySettings.cpp index 032596364..22d73c6dc 100644 --- a/olp-cpp-sdk-core/src/client/OlpClientSettings.cpp +++ b/olp-cpp-sdk-core/src/client/RetrySettings.cpp @@ -17,7 +17,7 @@ * License-Filename: LICENSE */ -#include "olp/core/client/OlpClientSettings.h" +#include "olp/core/client/RetrySettings.h" #include diff --git a/tests/integration/olp-cpp-sdk-authentication/AuthenticationClientTest.cpp b/tests/integration/olp-cpp-sdk-authentication/AuthenticationClientTest.cpp index da10db3ab..9f506597c 100644 --- a/tests/integration/olp-cpp-sdk-authentication/AuthenticationClientTest.cpp +++ b/tests/integration/olp-cpp-sdk-authentication/AuthenticationClientTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 HERE Europe B.V. + * Copyright (C) 2019-2021 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,6 +50,9 @@ constexpr unsigned int kMaxExpiryTime = kExpirtyTime + 30; constexpr unsigned int kMinExpiryTime = kExpirtyTime - 10; constexpr unsigned int kExpectedRetryCount = 3; +constexpr auto kMaxRetryAttempts = 5; +constexpr auto kRetryTimeout = 10; + // HTTP errors constexpr auto kErrorOk = "OK"; constexpr auto kErrorSignupCreated = "Created"; @@ -177,7 +180,7 @@ class AuthenticationClientTest : public ::testing::Test { auth::AuthenticationCredentials credentials(key_, secret_); std::promise request; auto request_future = request.get_future(); - olp::authentication::AuthenticationClient::SignUpProperties properties; + auth::AuthenticationClient::SignUpProperties properties; properties.email = email; properties.password = "password123"; properties.date_of_birth = "31/01/1980"; @@ -219,7 +222,7 @@ class AuthenticationClientTest : public ::testing::Test { std::promise request; auto request_future = request.get_future(); - const bool is_retriable = olp::client::DefaultRetryCondition({http}); + const bool is_retriable = client::DefaultRetryCondition({http}); // First is GetTimeFromServer(). Second is actual SignIn. // When the request is retriable, 3 more requests are fired. @@ -270,7 +273,7 @@ class AuthenticationClientTest : public ::testing::Test { protected: std::shared_ptr network_; - std::unique_ptr client_; + std::unique_ptr client_; std::shared_ptr task_scheduler_; const std::string key_; const std::string secret_; @@ -869,7 +872,7 @@ TEST_F(AuthenticationClientTest, SignInApple) { SCOPED_TRACE("Successful response with HTTP error"); auto get_retry_max_attempts_count = [] { - olp::client::RetrySettings retry_settings; + client::RetrySettings retry_settings; return retry_settings.max_attempts; }; @@ -1491,7 +1494,7 @@ TEST_F(AuthenticationClientTest, IntrospectApp) { auto error = response.GetError(); EXPECT_FALSE(response.IsSuccessful()); - EXPECT_EQ(error.GetErrorCode(), olp::client::ErrorCode::Unknown); + EXPECT_EQ(error.GetErrorCode(), client::ErrorCode::Unknown); testing::Mock::VerifyAndClearExpectations(network_.get()); } @@ -1541,14 +1544,14 @@ TEST_F(AuthenticationClientTest, Authorize) { EXPECT_TRUE(response.IsSuccessful()); auto result = response.GetResult(); EXPECT_EQ(result.GetClientId(), "some_id"); - ASSERT_EQ(result.GetDecision(), olp::authentication::DecisionType::kAllow); + ASSERT_EQ(result.GetDecision(), auth::DecisionType::kAllow); auto it = result.GetActionResults().begin(); - ASSERT_EQ(it->GetDecision(), olp::authentication::DecisionType::kAllow); + ASSERT_EQ(it->GetDecision(), auth::DecisionType::kAllow); ASSERT_EQ(it->GetPermissions().front().GetAction(), "read"); ASSERT_EQ(it->GetPermissions().front().GetResource(), "some_resource"); ASSERT_EQ(it->GetPermissions().front().GetDecision(), - olp::authentication::DecisionType::kAllow); + auth::DecisionType::kAllow); testing::Mock::VerifyAndClearExpectations(network_.get()); } @@ -1773,7 +1776,7 @@ TEST_F(AuthenticationClientTest, GetMyAccount) { auto error = response.GetError(); EXPECT_FALSE(response.IsSuccessful()); - EXPECT_EQ(error.GetErrorCode(), olp::client::ErrorCode::Unknown); + EXPECT_EQ(error.GetErrorCode(), client::ErrorCode::Unknown); testing::Mock::VerifyAndClearExpectations(network_.get()); } @@ -1844,3 +1847,264 @@ TEST_F(AuthenticationClientTest, UniqueNonce) { EXPECT_EQ(nonces.size(), kExpectedRetryCount); } + +TEST_F(AuthenticationClientTest, RetrySettings) { + auth::AuthenticationSettings settings; + settings.network_request_handler = network_; + settings.token_endpoint_url = kTokenEndpointUrl; + settings.task_scheduler = task_scheduler_; + settings.use_system_time = true; + settings.retry_settings.max_attempts = kMaxRetryAttempts; + settings.retry_settings.timeout = kRetryTimeout; + + const auto retry_predicate = testing::Property( + &olp::http::NetworkRequest::GetSettings, + testing::AllOf( + testing::Property(&olp::http::NetworkSettings::GetConnectionTimeout, + kRetryTimeout), + testing::Property(&olp::http::NetworkSettings::GetTransferTimeout, + kRetryTimeout))); + + ON_CALL(*network_, Send(retry_predicate, _, _, _, _)) + .WillByDefault(ReturnHttpResponse( + GetResponse(olp::http::HttpStatusCode::TOO_MANY_REQUESTS) + .WithError(kErrorTooManyRequestsMessage), + kResponseTooManyRequests)); + + const auth::AuthenticationCredentials credentials(key_, secret_); + client_ = std::make_unique(std::move(settings)); + + { + SCOPED_TRACE("SignInClient"); + + EXPECT_CALL(*network_, Send(retry_predicate, _, _, _, _)) + .Times(kMaxRetryAttempts); + + std::promise + response_promise; + client_->SignInClient( + credentials, auth::AuthenticationClient::SignInProperties{}, + [&](const auth::AuthenticationClient::SignInClientResponse& value) { + response_promise.set_value(value); + }); + + const auto response = response_promise.get_future().get(); + EXPECT_TRUE(response.IsSuccessful()); + EXPECT_EQ(olp::http::HttpStatusCode::TOO_MANY_REQUESTS, + response.GetResult().GetStatus()); + + testing::Mock::VerifyAndClearExpectations(network_.get()); + } + + { + SCOPED_TRACE("SignInHereUser"); + + EXPECT_CALL(*network_, Send(retry_predicate, _, _, _, _)) + .Times(kMaxRetryAttempts); + + std::promise + response_promise; + client_->SignInHereUser( + credentials, auth::AuthenticationClient::UserProperties{}, + [&](const auth::AuthenticationClient::SignInUserResponse& response) { + response_promise.set_value(response); + }); + + const auto response = response_promise.get_future().get(); + EXPECT_TRUE(response.IsSuccessful()); + EXPECT_EQ(olp::http::HttpStatusCode::TOO_MANY_REQUESTS, + response.GetResult().GetStatus()); + + testing::Mock::VerifyAndClearExpectations(network_.get()); + } + + { + SCOPED_TRACE("SignInFederated"); + + EXPECT_CALL(*network_, Send(retry_predicate, _, _, _, _)) + .Times(kMaxRetryAttempts); + + std::promise + response_promise; + client_->SignInFederated( + credentials, + R"({ "grantType": "xyz", "token": "test_token", "realm": "my_realm" })", + [&](const auth::AuthenticationClient::SignInUserResponse& response) { + response_promise.set_value(response); + }); + + const auto response = response_promise.get_future().get(); + EXPECT_TRUE(response.IsSuccessful()); + EXPECT_EQ(olp::http::HttpStatusCode::TOO_MANY_REQUESTS, + response.GetResult().GetStatus()); + + testing::Mock::VerifyAndClearExpectations(network_.get()); + } + + { + SCOPED_TRACE("SignInApple"); + + EXPECT_CALL(*network_, Send(retry_predicate, _, _, _, _)) + .Times(kMaxRetryAttempts + 1 /* first request */); + + std::promise + response_promise; + client_->SignInApple( + auth::AppleSignInProperties{}, + [&](const auth::AuthenticationClient::SignInUserResponse& response) { + response_promise.set_value(response); + }); + + const auto response = response_promise.get_future().get(); + EXPECT_TRUE(response.IsSuccessful()); + EXPECT_EQ(olp::http::HttpStatusCode::TOO_MANY_REQUESTS, + response.GetResult().GetStatus()); + + testing::Mock::VerifyAndClearExpectations(network_.get()); + } + + { + SCOPED_TRACE("SignInRefresh"); + + EXPECT_CALL(*network_, Send(retry_predicate, _, _, _, _)) + .Times(kMaxRetryAttempts); + + std::promise + response_promise; + client_->SignInRefresh( + credentials, auth::AuthenticationClient::RefreshProperties{}, + [&](const auth::AuthenticationClient::SignInUserResponse& response) { + response_promise.set_value(response); + }); + + const auto response = response_promise.get_future().get(); + EXPECT_TRUE(response.IsSuccessful()); + EXPECT_EQ(olp::http::HttpStatusCode::TOO_MANY_REQUESTS, + response.GetResult().GetStatus()); + + testing::Mock::VerifyAndClearExpectations(network_.get()); + } + + { + SCOPED_TRACE("AcceptTerms"); + + EXPECT_CALL(*network_, Send(retry_predicate, _, _, _, _)) + .Times(kMaxRetryAttempts); + + std::promise + response_promise; + client_->AcceptTerms( + credentials, "reacceptance_token", + [&](const auth::AuthenticationClient::SignInUserResponse& response) { + response_promise.set_value(response); + }); + + const auto response = response_promise.get_future().get(); + EXPECT_TRUE(response.IsSuccessful()); + EXPECT_EQ(olp::http::HttpStatusCode::TOO_MANY_REQUESTS, + response.GetResult().GetStatus()); + + testing::Mock::VerifyAndClearExpectations(network_.get()); + } + + { + SCOPED_TRACE("SignUpHereUser"); + + EXPECT_CALL(*network_, Send(retry_predicate, _, _, _, _)).Times(1); + + std::promise response_promise; + client_->SignUpHereUser( + credentials, auth::AuthenticationClient::SignUpProperties{}, + [&](const auth::AuthenticationClient::SignUpResponse& response) { + response_promise.set_value(response); + }); + + const auto response = response_promise.get_future().get(); + EXPECT_TRUE(response.IsSuccessful()); + EXPECT_EQ(olp::http::HttpStatusCode::TOO_MANY_REQUESTS, + response.GetResult().GetStatus()); + + testing::Mock::VerifyAndClearExpectations(network_.get()); + } + + { + SCOPED_TRACE("SignOut"); + + EXPECT_CALL(*network_, Send(retry_predicate, _, _, _, _)).Times(1); + + std::promise + response_promise; + client_->SignOut( + credentials, kResponseToken, + [&](const auth::AuthenticationClient::SignOutUserResponse& response) { + response_promise.set_value(response); + }); + + const auto response = response_promise.get_future().get(); + EXPECT_TRUE(response.IsSuccessful()); + EXPECT_EQ(olp::http::HttpStatusCode::TOO_MANY_REQUESTS, + response.GetResult().GetStatus()); + + testing::Mock::VerifyAndClearExpectations(network_.get()); + } + + { + SCOPED_TRACE("IntrospectApp"); + + EXPECT_CALL(*network_, Send(retry_predicate, _, _, _, _)) + .Times(kMaxRetryAttempts + 1 /* first request */); + + std::promise response_promise; + client_->IntrospectApp(kResponseToken, + [&](const auth::IntrospectAppResponse& response) { + response_promise.set_value(response); + }); + + const auto response = response_promise.get_future().get(); + EXPECT_FALSE(response.IsSuccessful()); + EXPECT_EQ(olp::http::HttpStatusCode::TOO_MANY_REQUESTS, + response.GetError().GetHttpStatusCode()); + + testing::Mock::VerifyAndClearExpectations(network_.get()); + } + + { + SCOPED_TRACE("Authorize"); + + EXPECT_CALL(*network_, Send(retry_predicate, _, _, _, _)) + .Times(kMaxRetryAttempts + 1 /* first request */); + + std::promise response_promise; + client_->Authorize(kResponseToken, auth::AuthorizeRequest{}, + [&](const auth::AuthorizeResponse& response) { + response_promise.set_value(response); + }); + + const auto response = response_promise.get_future().get(); + EXPECT_FALSE(response.IsSuccessful()); + EXPECT_EQ(olp::http::HttpStatusCode::TOO_MANY_REQUESTS, + response.GetError().GetHttpStatusCode()); + + testing::Mock::VerifyAndClearExpectations(network_.get()); + } + + { + SCOPED_TRACE("GetMyAccount"); + + EXPECT_CALL(*network_, Send(retry_predicate, _, _, _, _)) + .Times(kMaxRetryAttempts + 1 /* first request */); + + std::promise response_promise; + client_->GetMyAccount(kResponseToken, + [&](const auth::UserAccountInfoResponse& response) { + response_promise.set_value(response); + }); + + const auto response = response_promise.get_future().get(); + EXPECT_FALSE(response.IsSuccessful()); + EXPECT_EQ(olp::http::HttpStatusCode::TOO_MANY_REQUESTS, + response.GetError().GetHttpStatusCode()); + + testing::Mock::VerifyAndClearExpectations(network_.get()); + } +} diff --git a/tests/integration/olp-cpp-sdk-authentication/TokenProviderTest.cpp b/tests/integration/olp-cpp-sdk-authentication/TokenProviderTest.cpp index 6cbe0925c..c0ae0fe29 100644 --- a/tests/integration/olp-cpp-sdk-authentication/TokenProviderTest.cpp +++ b/tests/integration/olp-cpp-sdk-authentication/TokenProviderTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 HERE Europe B.V. + * Copyright (C) 2019-2021 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ #include #include #include "../olp-cpp-sdk-dataservice-read/HttpResponses.h" +#include "AuthenticationMockedResponses.h" namespace http = olp::http; namespace client = olp::client; @@ -45,6 +46,8 @@ static constexpr int64_t KVersion = 108; static constexpr auto kLayer = "testlayer"; static constexpr auto kPartition = "269"; constexpr auto kWaitTimeout = std::chrono::seconds(3); +constexpr auto kMaxRetryAttempts = 5; +constexpr auto kRetryTimeout = 10; // Request defines static const std::string kTimestampUrl = @@ -99,7 +102,7 @@ class TokenProviderTest : public ::testing::Test { network_mock_ = std::make_shared(); settings_.network_request_handler = network_mock_; settings_.task_scheduler = - olp::client::OlpClientSettingsFactory::CreateDefaultTaskScheduler(1); + client::OlpClientSettingsFactory::CreateDefaultTaskScheduler(1); } void TearDown() override { @@ -110,30 +113,29 @@ class TokenProviderTest : public ::testing::Test { template client::OlpClientSettings GetSettings(bool use_system_time = false) const { - olp::client::AuthenticationSettings auth_settings; - olp::authentication::Settings token_provider_settings( + client::AuthenticationSettings auth_settings; + authentication::Settings token_provider_settings( {"fake.key.id", "fake.key.secret"}); token_provider_settings.task_scheduler = settings_.task_scheduler; token_provider_settings.network_request_handler = settings_.network_request_handler; token_provider_settings.use_system_time = use_system_time; auth_settings.provider = - olp::authentication::TokenProvider( - token_provider_settings); + authentication::TokenProvider(token_provider_settings); - olp::client::OlpClientSettings settings = settings_; + client::OlpClientSettings settings = settings_; settings.authentication_settings = auth_settings; return settings; } - olp::client::OlpClientSettings settings_; + client::OlpClientSettings settings_; std::shared_ptr network_mock_; }; TEST_F(TokenProviderTest, SingleTokenMultipleUsers) { constexpr size_t kCount = 3u; auto settings = GetSettings(); - auto catalog = olp::client::HRN::FromString(kCatalog); + auto catalog = client::HRN::FromString(kCatalog); { SCOPED_TRACE("Request token once"); @@ -267,4 +269,36 @@ TEST_F(TokenProviderTest, ConcurrentRequests) { testing::Mock::VerifyAndClearExpectations(network_mock_.get()); } +TEST_F(TokenProviderTest, RetrySettings) { + authentication::Settings token_provider_settings( + {"fake.key.id", "fake.key.secret"}); + token_provider_settings.task_scheduler = settings_.task_scheduler; + token_provider_settings.network_request_handler = + settings_.network_request_handler; + token_provider_settings.use_system_time = true; + token_provider_settings.retry_settings.max_attempts = kMaxRetryAttempts; + token_provider_settings.retry_settings.timeout = kRetryTimeout; + + const auto retry_predicate = testing::Property( + &http::NetworkRequest::GetSettings, + testing::AllOf( + testing::Property(&http::NetworkSettings::GetConnectionTimeout, + kRetryTimeout), + testing::Property(&http::NetworkSettings::GetTransferTimeout, + kRetryTimeout))); + + EXPECT_CALL(*network_mock_, Send(retry_predicate, _, _, _, _)) + .Times(kMaxRetryAttempts) + .WillRepeatedly(ReturnHttpResponse( + GetResponse(olp::http::HttpStatusCode::TOO_MANY_REQUESTS) + .WithError("Too many requests"), + kResponseTooManyRequests)); + + const authentication::TokenProvider + token_provider(token_provider_settings); + + const auto token = token_provider(); + EXPECT_TRUE(token.empty()); +} + } // namespace