2020#include " ApiClientLookup.h"
2121
2222#include < olp/core/client/ApiError.h>
23+ #include < olp/core/client/Condition.h>
2324#include < olp/core/client/HRN.h>
2425#include < olp/core/client/OlpClient.h>
26+ #include < olp/core/cache/KeyValueCache.h>
2527#include < olp/core/logging/Log.h>
2628#include " generated/PlatformApi.h"
2729#include " generated/ResourcesApi.h"
@@ -45,6 +47,13 @@ std::string GetDatastoreServerUrl(const std::string& partition) {
4547 ? " https://api-lookup." + server_url->second + " /lookup/v1"
4648 : " " ;
4749}
50+
51+ std::string CreateKeyForCache (const std::string& hrn,
52+ const std::string& service,
53+ const std::string& service_version) {
54+ return hrn + " ::" + service + " ::" + service_version + " ::api" ;
55+ }
56+
4857} // namespace
4958
5059client::CancellationToken ApiClientLookup::LookupApi (
@@ -126,6 +135,110 @@ client::CancellationToken ApiClientLookup::LookupApiClient(
126135 });
127136}
128137
138+ ApiClientLookup::ApiClientResponse ApiClientLookup::LookupApiClient (
139+ const client::HRN& catalog,
140+ client::CancellationContext cancellation_context, std::string service,
141+ std::string service_version, client::OlpClientSettings settings) {
142+ auto cache_key =
143+ CreateKeyForCache (catalog.ToCatalogHRNString (), service, service_version);
144+ // first, try to find the corresponding Base URL from cache
145+ auto cache = settings.cache ;
146+ if (cache) {
147+ auto url =
148+ cache->Get (cache_key, [](const std::string& value) { return value; });
149+ if (!url.empty ()) {
150+ auto base_url = boost::any_cast<std::string>(url);
151+ OLP_SDK_LOG_INFO_F (kLogTag , " LookupApiClient(%s, %s) -> from cache" ,
152+ service.c_str (), service_version.c_str ());
153+ client::OlpClient client;
154+ client.SetSettings (settings);
155+ client.SetBaseUrl (base_url);
156+ return ApiClientResponse{client};
157+ }
158+ }
159+
160+ client::Condition condition;
161+ // when the network operation took too much time we cancel it and exit
162+ // execution, to make sure that network callback will not access dangling
163+ // references we protect them with atomic bool flag.
164+ auto flag = std::make_shared<std::atomic_bool>(true );
165+
166+ ApiClientResponse api_response;
167+ auto api_callback = [&, flag](ApiClientResponse response) {
168+ if (flag->exchange (false )) {
169+ api_response = std::move (response);
170+ condition.Notify ();
171+ }
172+ };
173+
174+ cancellation_context.ExecuteOrCancelled (
175+ [&, flag]() {
176+ auto client_ptr = std::make_shared<olp::client::OlpClient>();
177+ client_ptr->SetSettings (settings);
178+
179+ auto token = ApiClientLookup::LookupApiClient (
180+ client_ptr, service, service_version, catalog, api_callback);
181+ return client::CancellationToken ([&, token, flag]() {
182+ if (flag->exchange (false )) {
183+ token.Cancel ();
184+ condition.Notify ();
185+ }
186+ });
187+ },
188+ [&]() {
189+ // if context was cancelled before the execution setup, unblock the
190+ // upcoming wait routine.
191+ condition.Notify ();
192+ });
193+ if (!condition.Wait (std::chrono::seconds (settings.retry_settings .timeout ))) {
194+ cancellation_context.CancelOperation ();
195+ OLP_SDK_LOG_INFO_F (kLogTag , " LookupApi(%s/%s): %s - timeout" ,
196+ service.c_str (), service_version.c_str (),
197+ catalog.partition .c_str ());
198+ return client::ApiError (client::ErrorCode::RequestTimeout,
199+ " Network request timed out." );
200+ }
201+
202+ flag->store (false );
203+
204+ if (cancellation_context.IsCancelled ()) {
205+ // We can't use api response here because it could potentially be
206+ // uninitialized.
207+ OLP_SDK_LOG_INFO_F (kLogTag , " LookupApi(%s/%s): %s - cancelled" ,
208+ service.c_str (), service_version.c_str (),
209+ catalog.partition .c_str ());
210+ return client::ApiError (client::ErrorCode::Cancelled,
211+ " Operation cancelled." );
212+ }
213+
214+ if (!api_response.IsSuccessful ()) {
215+ return api_response.GetError ();
216+ }
217+
218+ auto client = api_response.GetResult ();
219+ if (client.GetBaseUrl ().empty ()) {
220+ OLP_SDK_LOG_WARNING_F (kLogTag , " LookupApi(%s/%s): %s - empty base URL" ,
221+ service.c_str (), service_version.c_str (),
222+ catalog.partition .c_str ());
223+ }
224+
225+ if (cache) {
226+ const auto & base_url = client.GetBaseUrl ();
227+
228+ constexpr time_t kExpiryTimeInSecs = 3600 ;
229+ if (cache->Put (cache_key, base_url, [base_url]() { return base_url; },
230+ kExpiryTimeInSecs )) {
231+ OLP_SDK_LOG_TRACE_F (kLogTag , " Put '%s' to cache" , cache_key.c_str ());
232+ } else {
233+ OLP_SDK_LOG_WARNING_F (kLogTag , " Failed to put '%s' to cache" ,
234+ cache_key.c_str ());
235+ }
236+ }
237+
238+ client.SetSettings (settings);
239+ return ApiClientResponse{std::move (client)};
240+ }
241+
129242} // namespace write
130243} // namespace dataservice
131244} // namespace olp
0 commit comments