From c99adbfb29a68904084bbb17865b63a653332057 Mon Sep 17 00:00:00 2001 From: Arsentiy Milchakov Date: Thu, 22 Jun 2017 18:20:54 +0300 Subject: [PATCH] [partners_api] yandex + taxi engine --- android/jni/com/mapswithme/maps/Framework.cpp | 22 +- android/jni/com/mapswithme/maps/Framework.hpp | 7 +- android/jni/com/mapswithme/maps/uber/Uber.cpp | 23 +- .../maps/routing/RoutingController.java | 4 +- .../src/com/mapswithme/maps/uber/Uber.java | 4 +- .../Views/RoutePreview/MWMTaxiPreviewCell.h | 7 +- .../Views/RoutePreview/MWMTaxiPreviewCell.mm | 4 +- .../RoutePreview/MWMTaxiPreviewDataSource.mm | 53 ++-- map/framework.cpp | 12 +- map/framework.hpp | 7 +- partners_api/partners_api.pro | 8 +- .../partners_api_tests/partners_api_tests.pro | 2 + .../partners_api_tests/taxi_engine_tests.cpp | 253 ++++++++++++++++++ .../partners_api_tests/uber_tests.cpp | 129 +++------ .../partners_api_tests/yandex_tests.cpp | 55 ++++ partners_api/taxi_base.hpp | 53 ++++ partners_api/taxi_engine.cpp | 203 ++++++++++++++ partners_api/taxi_engine.hpp | 87 ++++++ partners_api/taxi_provider.hpp | 72 +++++ partners_api/uber_api.cpp | 44 ++- partners_api/uber_api.hpp | 48 +--- partners_api/yandex_api.cpp | 159 +++++++++++ partners_api/yandex_api.hpp | 38 +++ tools/python/ResponseProvider.py | 4 + tools/python/jsons.py | 40 +++ traffic/traffic_info.cpp | 2 +- .../partners_api.xcodeproj/project.pbxproj | 35 ++- 27 files changed, 1163 insertions(+), 212 deletions(-) create mode 100644 partners_api/partners_api_tests/taxi_engine_tests.cpp create mode 100644 partners_api/partners_api_tests/yandex_tests.cpp create mode 100644 partners_api/taxi_base.hpp create mode 100644 partners_api/taxi_engine.cpp create mode 100644 partners_api/taxi_engine.hpp create mode 100644 partners_api/taxi_provider.hpp create mode 100644 partners_api/yandex_api.cpp create mode 100644 partners_api/yandex_api.hpp diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index 8ac1cfa7a3..5cc23efbc3 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -545,19 +545,27 @@ void Framework::EnableDownloadOn3g() } uint64_t Framework::RequestUberProducts(JNIEnv * env, jobject policy, ms::LatLon const & from, ms::LatLon const & to, - uber::ProductsCallback const & callback, - uber::ErrorCallback const & errorCallback) + taxi::SuccessfullCallback const & callback, + taxi::ErrorCallback const & errorCallback) { - auto const uberApi = m_work.GetUberApi(ToNativeNetworkPolicy(env, policy)); - if (!uberApi) + auto const taxiEngine = m_work.GetTaxiEngine(ToNativeNetworkPolicy(env, policy)); + if (!taxiEngine) return 0; - return uberApi->GetAvailableProducts(from, to, callback, errorCallback); + auto const mercatorPoint = MercatorBounds::FromLatLon(from); + auto const topmostCountryIds = m_work.GetTopmostCountries(mercatorPoint); + return taxiEngine->GetAvailableProducts(from, to, topmostCountryIds, callback, errorCallback); } -uber::RideRequestLinks Framework::GetUberLinks(string const & productId, ms::LatLon const & from, ms::LatLon const & to) +taxi::RideRequestLinks Framework::GetUberLinks(JNIEnv * env, jobject policy, + string const & productId, ms::LatLon const & from, + ms::LatLon const & to) { - return uber::Api::GetRideRequestLinks(productId, from, to); + auto const taxiEngine = m_work.GetTaxiEngine(ToNativeNetworkPolicy(env, policy)); + if (!taxiEngine) + return {}; + + return taxiEngine->GetRideRequestLinks(taxi::Provider::Type::Uber, productId, from, to); } void Framework::RequestViatorProducts(JNIEnv * env, jobject policy, std::string const & destId, diff --git a/android/jni/com/mapswithme/maps/Framework.hpp b/android/jni/com/mapswithme/maps/Framework.hpp index a1f79107ba..c622eba82b 100644 --- a/android/jni/com/mapswithme/maps/Framework.hpp +++ b/android/jni/com/mapswithme/maps/Framework.hpp @@ -183,9 +183,10 @@ namespace android void EnableDownloadOn3g(); uint64_t RequestUberProducts(JNIEnv * env, jobject policy, ms::LatLon const & from, - ms::LatLon const & to, uber::ProductsCallback const & callback, - uber::ErrorCallback const & errorCallback); - static uber::RideRequestLinks GetUberLinks(std::string const & productId, ms::LatLon const & from, ms::LatLon const & to); + ms::LatLon const & to, taxi::SuccessfullCallback const & callback, + taxi::ErrorCallback const & errorCallback); + taxi::RideRequestLinks GetUberLinks(JNIEnv * env, jobject policy, std::string const & productId, + ms::LatLon const & from, ms::LatLon const & to); void RequestViatorProducts(JNIEnv * env, jobject policy, std::string const & destId, std::string const & currency, diff --git a/android/jni/com/mapswithme/maps/uber/Uber.cpp b/android/jni/com/mapswithme/maps/uber/Uber.cpp index 6cb45241ab..33a233341d 100644 --- a/android/jni/com/mapswithme/maps/uber/Uber.cpp +++ b/android/jni/com/mapswithme/maps/uber/Uber.cpp @@ -1,7 +1,7 @@ #include "../Framework.hpp" #include "../../core/jni_helper.hpp" -#include "partners_api/uber_api.hpp" +#include "partners_api/taxi_provider.hpp" namespace { @@ -48,7 +48,7 @@ void PrepareClassRefs(JNIEnv * env) jni::GetConstructorID(env, g_uberLinksClass, "(Ljava/lang/String;Ljava/lang/String;)V"); } -void OnUberInfoReceived(vector const & products, uint64_t const requestId) +void OnUberInfoReceived(taxi::ProvidersContainer const & products, uint64_t const requestId) { GetPlatform().RunOnGuiThread([=]() { if (g_lastRequestId != requestId) @@ -59,7 +59,8 @@ void OnUberInfoReceived(vector const & products, uint64_t const r JNIEnv * env = jni::GetEnv(); auto const uberProducts = jni::ToJavaArray( - env, g_productClass, products, [](JNIEnv * env, uber::Product const & item) { + env, g_productClass, products[0].GetProducts(), + [](JNIEnv * env, taxi::Product const & item) { return env->NewObject( g_productClass, g_productConstructor, jni::ToJavaString(env, item.m_productId), jni::ToJavaString(env, item.m_name), jni::ToJavaString(env, item.m_time), @@ -72,12 +73,13 @@ void OnUberInfoReceived(vector const & products, uint64_t const r }); } -void OnUberError(uber::ErrorCode const code, uint64_t const requestId) +void OnUberError(taxi::ErrorsContainer const & errors, uint64_t const requestId) { GetPlatform().RunOnGuiThread([=]() { if (g_lastRequestId != requestId) return; - +// Dummy, must be changed by android developer. +/* JNIEnv * env = jni::GetEnv(); static jclass const errCodeClass = env->FindClass("com/mapswithme/maps/uber/Uber$ErrorCode"); @@ -87,7 +89,8 @@ void OnUberError(uber::ErrorCode const code, uint64_t const requestId) env->CallStaticObjectMethod(g_routingControllerClass, g_routingControllerGetMethod); env->CallVoidMethod(routingControllerInstance, g_uberErrorCallbackMethod, - jni::ToJavaString(env, uber::DebugPrint(code))); + jni::ToJavaString(env, taxi::DebugPrint(code))); +*/ }); } } // namespace @@ -108,16 +111,16 @@ JNIEXPORT void JNICALL Java_com_mapswithme_maps_uber_Uber_nativeRequestUberProdu } JNIEXPORT jobject JNICALL Java_com_mapswithme_maps_uber_Uber_nativeGetUberLinks( - JNIEnv * env, jclass clazz, jstring productId, jdouble srcLat, jdouble srcLon, jdouble dstLat, - jdouble dstLon) + JNIEnv * env, jclass clazz, jobject policy, jstring productId, jdouble srcLat, jdouble srcLon, + jdouble dstLat, jdouble dstLon) { PrepareClassRefs(env); ms::LatLon const from(srcLat, srcLon); ms::LatLon const to(dstLat, dstLon); - uber::RideRequestLinks const links = - android::Framework::GetUberLinks(jni::ToNativeString(env, productId), from, to); + taxi::RideRequestLinks const links = + g_framework->GetUberLinks(env, policy, jni::ToNativeString(env, productId), from, to); return env->NewObject(g_uberLinksClass, g_uberLinksConstructor, jni::ToJavaString(env, links.m_deepLink), jni::ToJavaString(env, links.m_universalLink)); diff --git a/android/src/com/mapswithme/maps/routing/RoutingController.java b/android/src/com/mapswithme/maps/routing/RoutingController.java index 9836220db2..1990044236 100644 --- a/android/src/com/mapswithme/maps/routing/RoutingController.java +++ b/android/src/com/mapswithme/maps/routing/RoutingController.java @@ -916,8 +916,8 @@ public class RoutingController if (mStartPoint == null || mEndPoint == null) return null; - return Uber.nativeGetUberLinks(productId, mStartPoint.getLat(), mStartPoint.getLon(), - mEndPoint.getLat(), mEndPoint.getLon()); + return Uber.nativeGetUberLinks(NetworkPolicy.newInstance(true), productId, mStartPoint.getLat(), + mStartPoint.getLon(), mEndPoint.getLat(), mEndPoint.getLon()); } /** diff --git a/android/src/com/mapswithme/maps/uber/Uber.java b/android/src/com/mapswithme/maps/uber/Uber.java index 19dcdfc8ff..4a3c715303 100644 --- a/android/src/com/mapswithme/maps/uber/Uber.java +++ b/android/src/com/mapswithme/maps/uber/Uber.java @@ -10,8 +10,8 @@ public class Uber double srcLon, double dstLat, double dstLon); @NonNull - public static native UberLinks nativeGetUberLinks(@NonNull String productId, double srcLon, double srcLat, - double dstLat, double dstLon); + public static native UberLinks nativeGetUberLinks(@NonNull NetworkPolicy policy, + @NonNull String productId, double srcLon, double srcLat, double dstLat, double dstLon); public enum ErrorCode { diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewCell.h b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewCell.h index 32ef674ce3..486840a294 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewCell.h +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewCell.h @@ -1,10 +1,9 @@ -namespace uber -{ - struct Product; +namespace taxi { +struct Product; } // namespace uber; @interface MWMTaxiPreviewCell : UICollectionViewCell -- (void)configWithProduct:(uber::Product const &)product; +- (void)configWithProduct:(taxi::Product const &)product; @end diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewCell.mm b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewCell.mm index 157d6f0308..9b32a9319b 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewCell.mm +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewCell.mm @@ -1,7 +1,7 @@ #import "MWMTaxiPreviewCell.h" #import "SwiftBridge.h" -#include "partners_api/uber_api.hpp" +#include "partners_api/taxi_provider.hpp" #include "base/string_utils.hpp" @@ -22,7 +22,7 @@ [self.icon layoutIfNeeded]; } -- (void)configWithProduct:(uber::Product const &)product; +- (void)configWithProduct:(taxi::Product const &)product; { self.product.text = @(product.m_name.c_str()); NSTimeInterval time; diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewDataSource.mm b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewDataSource.mm index f9bd8504c9..d6fdba48d5 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewDataSource.mm +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMTaxiPreviewDataSource.mm @@ -8,7 +8,7 @@ #include "geometry/mercator.hpp" -#include "partners_api/uber_api.hpp" +#include "partners_api/taxi_provider.hpp" namespace { @@ -56,7 +56,7 @@ CGFloat const kPageControlHeight = 6; @end -using namespace uber; +using namespace taxi; @interface MWMTaxiPreviewDataSource() { @@ -110,18 +110,18 @@ using namespace uber; cv.pageControl.hidden = YES; network_policy::CallPartnersApi( - [self, completion, failure](platform::NetworkPolicy const & canUseNetwork) { - auto const api = GetFramework().GetUberApi(canUseNetwork); - if (!api) - { + [self, completion, failure](platform::NetworkPolicy const &canUseNetwork) { + auto const engine = GetFramework().GetTaxiEngine(canUseNetwork); + if (!engine) { failure(L(@"dialog_taxi_error")); return; } - auto success = [self, completion](vector const & products, + auto success = [self, completion](taxi::ProvidersContainer const &providers, uint64_t const requestId) { if (self->m_requestId != requestId) return; + auto const &products = providers[0].GetProducts(); runAsyncOnMainQueue([self, completion, products] { self->m_products = products; @@ -136,18 +136,26 @@ using namespace uber; }); }; - auto error = [self, failure](uber::ErrorCode const code, uint64_t const requestId) { + auto error = [self, failure](taxi::ErrorsContainer const & errors, uint64_t const requestId) { if (self->m_requestId != requestId) return; - runAsyncOnMainQueue(^{ - switch (code) - { - case uber::ErrorCode::NoProducts: failure(L(@"taxi_not_found")); break; - case uber::ErrorCode::RemoteError: failure(L(@"dialog_taxi_error")); break; - } - }); +// Dummy, must be changed by IOS developer +// runAsyncOnMainQueue(^{ +// switch (code) +// { +// case taxi::ErrorCode::NoProducts: +// failure(L(@"taxi_not_found")); +// break; +// case taxi::ErrorCode::RemoteError: +// failure(L(@"dialog_taxi_error")); +// break; +// } +// }); }; - m_requestId = api->GetAvailableProducts(m_from, m_to, success, error); + + auto const mercatorPoint = MercatorBounds::FromLatLon(m_from); + auto const topmostCountryIds = GetFramework().GetTopmostCountries(mercatorPoint); + m_requestId = engine->GetAvailableProducts(m_from, m_to, topmostCountryIds, success, error); }, true /* force */); } @@ -167,7 +175,18 @@ using namespace uber; auto const index = [self.collectionView indexPathsForVisibleItems].firstObject.row; auto const productId = m_products[index].m_productId; - auto const links = Api::GetRideRequestLinks(productId, m_from, m_to); + RideRequestLinks links; + network_policy::CallPartnersApi( + [self, &productId, &links](platform::NetworkPolicy const &canUseNetwork) { + auto const engine = GetFramework().GetTaxiEngine(canUseNetwork); + if (!engine) { + // Dummy, should be implemented + return; + } + + links = engine->GetRideRequestLinks(taxi::Provider::Type::Uber, productId, m_from, m_to); + }, + true /* force */); return [NSURL URLWithString:self.isTaxiInstalled ? @(links.m_deepLink.c_str()) : @(links.m_universalLink.c_str())]; diff --git a/map/framework.cpp b/map/framework.cpp index dc48d0a253..8d000dd659 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -501,10 +501,10 @@ booking::Api const * Framework::GetBookingApi(platform::NetworkPolicy const & po return nullptr; } -uber::Api * Framework::GetUberApi(platform::NetworkPolicy const & policy) +taxi::Engine * Framework::GetTaxiEngine(platform::NetworkPolicy const & policy) { if (policy.CanUse()) - return m_uberApi.get(); + return m_taxiEngine.get(); return nullptr; } @@ -3183,6 +3183,14 @@ bool Framework::OriginalFeatureHasDefaultName(FeatureID const & fid) const return osm::Editor::Instance().OriginalFeatureHasDefaultName(fid); } +storage::TCountriesVec Framework::GetTopmostCountries(m2::PointD point) const +{ + auto const countryId = m_infoGetter->GetRegionCountryId(point); + storage::TCountriesVec topmostCountryIds; + GetStorage().GetTopmostNodesFor(countryId, topmostCountryIds); + return topmostCountryIds; +} + namespace { vector colorList = { dp::Color(255, 0, 0, 255), dp::Color(0, 255, 0, 255), dp::Color(0, 0, 255, 255), diff --git a/map/framework.hpp b/map/framework.hpp index b14ba4715c..36209a4128 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -44,7 +44,7 @@ #include "tracking/reporter.hpp" #include "partners_api/booking_api.hpp" -#include "partners_api/uber_api.hpp" +#include "partners_api/taxi_engine.hpp" #include "partners_api/viator_api.hpp" #include "platform/country_defines.hpp" @@ -180,8 +180,8 @@ protected: BookmarkManager m_bmManager; unique_ptr m_bookingApi = make_unique(); - unique_ptr m_uberApi = make_unique(); unique_ptr m_viatorApi = make_unique(); + unique_ptr m_taxiEngine = make_unique(); df::DrapeApi m_drapeApi; @@ -222,8 +222,8 @@ public: /// Get access to booking api helpers booking::Api * GetBookingApi(platform::NetworkPolicy const & policy); booking::Api const * GetBookingApi(platform::NetworkPolicy const & policy) const; - uber::Api * GetUberApi(platform::NetworkPolicy const & policy); viator::Api * GetViatorApi(platform::NetworkPolicy const & policy); + taxi::Engine * GetTaxiEngine(platform::NetworkPolicy const & policy); df::DrapeApi & GetDrapeApi() { return m_drapeApi; } @@ -831,6 +831,7 @@ private: public: bool OriginalFeatureHasDefaultName(FeatureID const & fid) const; + storage::TCountriesVec GetTopmostCountries(m2::PointD point) const; private: std::unique_ptr m_cityFinder; diff --git a/partners_api/partners_api.pro b/partners_api/partners_api.pro index 178ffbf797..2f3e917bd3 100644 --- a/partners_api/partners_api.pro +++ b/partners_api/partners_api.pro @@ -18,6 +18,8 @@ SOURCES += \ rb_ads.cpp \ uber_api.cpp \ viator_api.cpp \ + yandex_api.cpp \ + taxi_engine.cpp HEADERS += \ ads_base.hpp \ @@ -29,4 +31,8 @@ HEADERS += \ opentable_api.hpp \ rb_ads.hpp \ uber_api.hpp \ - viator_api.hpp \ + viator_api.cpp \ + yandex_api.hpp \ + taxi_engine.hpp \ + taxi_base.hpp \ + taxi_provider.hpp diff --git a/partners_api/partners_api_tests/partners_api_tests.pro b/partners_api/partners_api_tests/partners_api_tests.pro index c819af6212..afdf28f926 100644 --- a/partners_api/partners_api_tests/partners_api_tests.pro +++ b/partners_api/partners_api_tests/partners_api_tests.pro @@ -31,5 +31,7 @@ SOURCES += \ facebook_tests.cpp \ mopub_tests.cpp \ rb_tests.cpp \ + taxi_engine_tests.cpp \ uber_tests.cpp \ viator_tests.cpp \ + yandex_tests.cpp \ diff --git a/partners_api/partners_api_tests/taxi_engine_tests.cpp b/partners_api/partners_api_tests/taxi_engine_tests.cpp new file mode 100644 index 0000000000..65d3577141 --- /dev/null +++ b/partners_api/partners_api_tests/taxi_engine_tests.cpp @@ -0,0 +1,253 @@ +#include "testing/testing.hpp" + +#include "partners_api/taxi_engine.hpp" +#include "partners_api/uber_api.hpp" +#include "partners_api/yandex_api.hpp" + +#include "geometry/latlon.hpp" + +#include "base/scope_guard.hpp" + +namespace +{ +std::vector GetUberSynchronous(ms::LatLon const & from, ms::LatLon const & to) +{ + std::string times; + std::string prices; + + TEST(taxi::uber::RawApi::GetEstimatedTime(from, times), ()); + TEST(taxi::uber::RawApi::GetEstimatedPrice(from, to, prices), ()); + + size_t reqId = 0; + taxi::uber::ProductMaker maker; + std::vector uberProducts; + + maker.Reset(reqId); + maker.SetTimes(reqId, times); + maker.SetPrices(reqId, prices); + maker.MakeProducts( + reqId, [&uberProducts](vector const & products) { uberProducts = products; }, + [](taxi::ErrorCode const code) { TEST(false, ()); }); + + return uberProducts; +} + +std::vector GetYandexSynchronous(ms::LatLon const & from, ms::LatLon const & to) +{ + std::string yandexAnswer; + std::vector yandexProducts; + + TEST(taxi::yandex::RawApi::GetTaxiInfo(from, to, yandexAnswer), ()); + + taxi::yandex::MakeFromJson(yandexAnswer, yandexProducts); + + return yandexProducts; +} + +taxi::ProvidersContainer GetProvidersSynchronous(ms::LatLon const & from, ms::LatLon const & to) +{ + taxi::ProvidersContainer providers; + + providers.emplace_back(taxi::Provider::Type::Uber, GetUberSynchronous(from, to)); + providers.emplace_back(taxi::Provider::Type::Yandex, GetYandexSynchronous(from, to)); + + return providers; +} + +void CompareProviders(taxi::ProvidersContainer const & providersContainer, + taxi::ProvidersContainer const & synchronousProviders) +{ + TEST_EQUAL(synchronousProviders.size(), providersContainer.size(), ()); + + for (auto const & sp : synchronousProviders) + { + auto const it = std::find_if( + providersContainer.cbegin(), providersContainer.cend(), [&sp](taxi::Provider const & p) { + if (p.GetType() != sp.GetType()) + return false; + + auto const & spp = sp.GetProducts(); + auto const & pp = p.GetProducts(); + + TEST_EQUAL(spp.size(), pp.size(), ()); + + for (auto const & sprod : spp) + { + auto const prodIt = + std::find_if(pp.cbegin(), pp.cend(), [&sprod](taxi::Product const & prod) { + return sprod.m_productId == prod.m_productId && sprod.m_name == prod.m_name && + sprod.m_price == prod.m_price; + }); + + if (prodIt == pp.cend()) + return false; + } + + return true; + }); + + TEST(it != providersContainer.cend(), ()); + } +} + +UNIT_TEST(TaxiEngine_ResultMaker) +{ + taxi::ResultMaker maker; + uint64_t reqId = 1; + taxi::ProvidersContainer providers; + taxi::ErrorsContainer errors; + + auto const successCallback = [&reqId, &providers](taxi::ProvidersContainer const & products, + uint64_t const requestId) { + TEST_EQUAL(reqId, requestId, ()); + providers = products; + }; + + auto const successNotPossibleCallback = [&reqId, &providers]( + taxi::ProvidersContainer const & products, + uint64_t const requestId) { TEST(false, ()); }; + + auto const errorCallback = [&reqId, &errors](taxi::ErrorsContainer const e, + uint64_t const requestId) { + TEST_EQUAL(reqId, requestId, ()); + errors = e; + }; + + auto const errorNotPossibleCallback = [&reqId](taxi::ErrorsContainer const errors, + uint64_t const requestId) { TEST(false, ()); }; + + std::vector products1 = + { + {"1", "", "", "", ""}, + {"2", "", "", "", ""}, + {"3", "", "", "", ""}, + }; + + std::vector products2 = + { + {"4", "", "", "", ""}, + {"5", "", "", "", ""}, + {"6", "", "", "", ""}, + }; + + maker.Reset(reqId, 2, successCallback, errorNotPossibleCallback); + maker.ProcessProducts(reqId, taxi::Provider::Type::Uber, products1); + maker.ProcessProducts(reqId, taxi::Provider::Type::Yandex, products2); + + TEST(providers.empty(), ()); + TEST(errors.empty(), ()); + + maker.Reset(reqId, 3, successCallback, errorNotPossibleCallback); + maker.ProcessProducts(reqId, taxi::Provider::Type::Uber, products1); + maker.ProcessProducts(reqId, taxi::Provider::Type::Yandex, products2); + maker.DecrementRequestCount(reqId); + maker.MakeResult(reqId); + + TEST_EQUAL(providers.size(), 2, ()); + TEST_EQUAL(providers[0].GetType(), taxi::Provider::Type::Uber, ()); + TEST_EQUAL(providers[1].GetType(), taxi::Provider::Type::Yandex, ()); + TEST_EQUAL(providers[0][0].m_productId, "1", ()); + TEST_EQUAL(providers[0][1].m_productId, "2", ()); + TEST_EQUAL(providers[0][2].m_productId, "3", ()); + TEST_EQUAL(providers[1][0].m_productId, "4", ()); + TEST_EQUAL(providers[1][1].m_productId, "5", ()); + TEST_EQUAL(providers[1][2].m_productId, "6", ()); + + maker.Reset(reqId, 2, successCallback, errorNotPossibleCallback); + maker.ProcessError(reqId, taxi::Provider::Type::Uber, taxi::ErrorCode::NoProducts); + maker.ProcessProducts(reqId, taxi::Provider::Type::Yandex, products2); + maker.MakeResult(reqId); + + TEST_EQUAL(providers.size(), 1, ()); + TEST_EQUAL(providers[0].GetType(), taxi::Provider::Type::Yandex, ()); + TEST_EQUAL(providers[0][0].m_productId, "4", ()); + TEST_EQUAL(providers[0][1].m_productId, "5", ()); + TEST_EQUAL(providers[0][2].m_productId, "6", ()); + + maker.Reset(reqId, 2, successNotPossibleCallback, errorCallback); + maker.ProcessError(reqId, taxi::Provider::Type::Uber, taxi::ErrorCode::NoProducts); + maker.ProcessError(reqId, taxi::Provider::Type::Yandex, taxi::ErrorCode::RemoteError); + maker.MakeResult(reqId); + + TEST_EQUAL(errors.size(), 2, ()); + TEST_EQUAL(errors[0].m_type, taxi::Provider::Type::Uber, ()); + TEST_EQUAL(errors[0].m_code, taxi::ErrorCode::NoProducts, ()); + TEST_EQUAL(errors[1].m_type, taxi::Provider::Type::Yandex, ()); + TEST_EQUAL(errors[1].m_code, taxi::ErrorCode::RemoteError, ()); +} + +UNIT_TEST(TaxiEngine_Smoke) +{ + // Used to synchronize access into GetAvailableProducts callback method. + std::mutex resultsMutex; + size_t reqId = 1; + taxi::ProvidersContainer providersContainer; + ms::LatLon const from(38.897724, -77.036531); + ms::LatLon const to(38.862416, -76.883316); + + taxi::uber::SetUberUrlForTesting("http://localhost:34568/partners"); + taxi::yandex::SetYandexUrlForTesting("http://localhost:34568/partners"); + + MY_SCOPE_GUARD(cleanupUber, []() { taxi::uber::SetUberUrlForTesting(""); }); + MY_SCOPE_GUARD(cleanupYandex, []() { taxi::yandex::SetYandexUrlForTesting(""); }); + + auto const errorCallback = [](taxi::ErrorsContainer const & errors, uint64_t const requestId) { + TEST(false, ()); + }; + + auto const errorPossibleCallback = [](taxi::ErrorsContainer const & errors, + uint64_t const requestId) { + for (auto const & error : errors) + TEST(error.m_code == taxi::ErrorCode::NoProducts, ()); + }; + + auto const standardCallback = [&reqId, &providersContainer, &resultsMutex]( + taxi::ProvidersContainer const & providers, + uint64_t const requestId) { + std::lock_guard lock(resultsMutex); + + if (reqId == requestId) + providersContainer = providers; + }; + + auto const lastCallback = [&standardCallback](taxi::ProvidersContainer const & products, + uint64_t const requestId) { + standardCallback(products, requestId); + testing::StopEventLoop(); + }; + + taxi::ProvidersContainer const synchronousProviders = GetProvidersSynchronous(from, to); + + { + taxi::Engine engine; + + { + lock_guard lock(resultsMutex); + reqId = engine.GetAvailableProducts( + ms::LatLon(55.753960, 37.624513), ms::LatLon(55.765866, 37.661270), + {"Brazil", "Russian Federation"}, standardCallback, errorPossibleCallback); + } + { + lock_guard lock(resultsMutex); + reqId = engine.GetAvailableProducts( + ms::LatLon(59.922445, 30.367201), ms::LatLon(59.943675, 30.361123), + {"Brazil", "Russian Federation"}, standardCallback, errorPossibleCallback); + } + { + lock_guard lock(resultsMutex); + reqId = engine.GetAvailableProducts( + ms::LatLon(52.509621, 13.450067), ms::LatLon(52.510811, 13.409490), + {"Brazil", "Russian Federation"}, standardCallback, errorPossibleCallback); + } + { + lock_guard lock(resultsMutex); + reqId = engine.GetAvailableProducts(from, to, {"Brazil", "Russian Federation"}, lastCallback, + errorCallback); + } + } + + testing::RunEventLoop(); + + CompareProviders(providersContainer, synchronousProviders); +} +} // namespace diff --git a/partners_api/partners_api_tests/uber_tests.cpp b/partners_api/partners_api_tests/uber_tests.cpp index 11b9a79828..2a71fbecbb 100644 --- a/partners_api/partners_api_tests/uber_tests.cpp +++ b/partners_api/partners_api_tests/uber_tests.cpp @@ -12,9 +12,11 @@ #include "3party/jansson/myjansson.hpp" +using namespace taxi; + namespace { -bool IsComplete(uber::Product const & product) +bool IsComplete(Product const & product) { return !product.m_productId.empty() && !product.m_name.empty() && !product.m_time.empty() && !product.m_price.empty(); @@ -122,15 +124,14 @@ UNIT_TEST(Uber_ProductMaker) ms::LatLon const from(38.897724, -77.036531); ms::LatLon const to(38.862416, -76.883316); - size_t returnedId = 0; - vector returnedProducts; + vector returnedProducts; uber::ProductMaker maker; string times; string prices; - auto const errorCallback = [](uber::ErrorCode const code, uint64_t const requestId) + auto const errorCallback = [](ErrorCode const code) { TEST(false, ()); }; @@ -142,15 +143,12 @@ UNIT_TEST(Uber_ProductMaker) maker.SetTimes(reqId, times); maker.SetPrices(reqId, prices); maker.MakeProducts(reqId, - [&returnedId, &returnedProducts](vector const & products, - size_t const requestId) { - returnedId = requestId; + [&returnedProducts](vector const & products) { returnedProducts = products; }, errorCallback); TEST(!returnedProducts.empty(), ()); - TEST_EQUAL(returnedId, reqId, ()); for (auto const & product : returnedProducts) TEST(IsComplete(product), ()); @@ -164,104 +162,43 @@ UNIT_TEST(Uber_ProductMaker) maker.SetTimes(reqId, times); maker.SetPrices(reqId, prices); - maker.MakeProducts(reqId + 1, [](vector const & products, size_t const requestId) + maker.MakeProducts(reqId + 1, [](vector const & products) { TEST(false, ()); }, errorCallback); } -UNIT_TEST(Uber_Smoke) +UNIT_TEST(Uber_GetAvailableProducts) { - // Used to synchronize access into GetAvailableProducts callback method. - mutex resultsMutex; - size_t reqId = 1; - vector productsContainer; - ms::LatLon const from(38.897724, -77.036531); - ms::LatLon const to(38.862416, -76.883316); + taxi::uber::Api api; + ms::LatLon const from(55.796918, 37.537859); + ms::LatLon const to(55.758213, 37.616093); - uber::SetUberUrlForTesting("http://localhost:34568/partners"); - MY_SCOPE_GUARD(cleanup, []() { uber::SetUberUrlForTesting(""); }); + taxi::uber::SetUberUrlForTesting("http://localhost:34568/partners"); + MY_SCOPE_GUARD(cleanup, []() { taxi::uber::SetUberUrlForTesting(""); }); - auto const errorCallback = [](uber::ErrorCode const code, uint64_t const requestId) - { - TEST(false, ()); - }; + std::vector resultProducts; - auto const errorPossibleCallback = [](uber::ErrorCode const code, uint64_t const requestId) - { - TEST(code == uber::ErrorCode::NoProducts, ()); - }; - - auto const standardCallback = - [&reqId, &productsContainer, &resultsMutex](vector const & products, size_t const requestId) - { - lock_guard lock(resultsMutex); - - if (reqId == requestId) - productsContainer = products; - }; - - auto const lastCallback = - [&standardCallback](vector const & products, size_t const requestId) - { - standardCallback(products, requestId); - testing::StopEventLoop(); - }; - - string times; - string prices; - - TEST(uber::RawApi::GetEstimatedTime(from, times), ()); - TEST(uber::RawApi::GetEstimatedPrice(from, to, prices), ()); - - uber::ProductMaker maker; - - maker.Reset(reqId); - maker.SetTimes(reqId, times); - maker.SetPrices(reqId, prices); - maker.MakeProducts(reqId, standardCallback, errorCallback); - - reqId = 0; - - auto const synchronousProducts = productsContainer; - productsContainer.clear(); - - { - uber::Api uberApi; - - { - lock_guard lock(resultsMutex); - reqId = uberApi.GetAvailableProducts(ms::LatLon(55.753960, 37.624513), - ms::LatLon(55.765866, 37.661270), standardCallback, - errorPossibleCallback); - } - { - lock_guard lock(resultsMutex); - reqId = uberApi.GetAvailableProducts(ms::LatLon(59.922445, 30.367201), - ms::LatLon(59.943675, 30.361123), standardCallback, - errorPossibleCallback); - } - { - lock_guard lock(resultsMutex); - reqId = uberApi.GetAvailableProducts(ms::LatLon(52.509621, 13.450067), - ms::LatLon(52.510811, 13.409490), standardCallback, - errorPossibleCallback); - } - { - lock_guard lock(resultsMutex); - reqId = uberApi.GetAvailableProducts(from, to, lastCallback, errorCallback); - } - } + api.GetAvailableProducts(from, to, + [&resultProducts](std::vector const & products) { + resultProducts = products; + testing::StopEventLoop(); + }, + [](taxi::ErrorCode const code) { TEST(false, ()); }); testing::RunEventLoop(); - TEST_EQUAL(synchronousProducts.size(), productsContainer.size(), ()); - - auto const isEqual = - equal(synchronousProducts.begin(), synchronousProducts.end(), productsContainer.begin(), - [](uber::Product const & lhs, uber::Product const & rhs) { - return lhs.m_productId == rhs.m_productId && lhs.m_name == rhs.m_name && - lhs.m_price == rhs.m_price; - }); - TEST(isEqual, ()); + TEST(!resultProducts.empty(), ()); +} + +UNIT_TEST(Uber_GetRideRequestLinks) +{ + taxi::uber::Api api; + ms::LatLon const from(55.796918, 37.537859); + ms::LatLon const to(55.758213, 37.616093); + + auto const links = api.GetRideRequestLinks("", from, to); + + TEST(!links.m_deepLink.empty(), ()); + TEST(!links.m_universalLink.empty(), ()); } diff --git a/partners_api/partners_api_tests/yandex_tests.cpp b/partners_api/partners_api_tests/yandex_tests.cpp new file mode 100644 index 0000000000..e2b4f999bb --- /dev/null +++ b/partners_api/partners_api_tests/yandex_tests.cpp @@ -0,0 +1,55 @@ +#include "testing/testing.hpp" + +#include "partners_api/yandex_api.hpp" + +#include "geometry/latlon.hpp" + +#include "base/scope_guard.hpp" + +namespace +{ +UNIT_TEST(Yandex_GetTaxiInfo) +{ + ms::LatLon const from(55.796918, 37.537859); + ms::LatLon const to(55.758213, 37.616093); + string result; + + taxi::yandex::RawApi::GetTaxiInfo(from, to, result); + + TEST(!result.empty(), ()); +} + +UNIT_TEST(Yandex_GetAvailableProducts) +{ + taxi::yandex::Api api; + ms::LatLon const from(55.796918, 37.537859); + ms::LatLon const to(55.758213, 37.616093); + + taxi::yandex::SetYandexUrlForTesting("http://localhost:34568/partners"); + MY_SCOPE_GUARD(cleanup, []() { taxi::yandex::SetYandexUrlForTesting(""); }); + + std::vector resultProducts; + + api.GetAvailableProducts(from, to, + [&resultProducts](std::vector const & products) { + resultProducts = products; + testing::StopEventLoop(); + }, + [](taxi::ErrorCode const code) { TEST(false, ()); }); + + testing::RunEventLoop(); + + TEST(!resultProducts.empty(), ()); +} + +UNIT_TEST(Yandex_GetRideRequestLinks) +{ + taxi::yandex::Api api; + ms::LatLon const from(55.796918, 37.537859); + ms::LatLon const to(55.758213, 37.616093); + + auto const links = api.GetRideRequestLinks("", from, to); + + TEST(!links.m_deepLink.empty(), ()); +} +} // namespace diff --git a/partners_api/taxi_base.hpp b/partners_api/taxi_base.hpp new file mode 100644 index 0000000000..336694fc68 --- /dev/null +++ b/partners_api/taxi_base.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "partners_api/taxi_provider.hpp" + +#include +#include +#include + +namespace ms +{ +class LatLon; +} + +namespace taxi +{ +/// @products - vector of available products for requested route, cannot be empty. +/// @requestId - identificator which was provided to GetAvailableProducts to identify request. +using ProductsCallback = std::function const & products)>; + +/// Callback which is called when an errors occurs. +using ErrorProviderCallback = std::function; + +struct RideRequestLinks +{ + std::string m_deepLink; + std::string m_universalLink; +}; + +class ApiBase +{ +public: + virtual ~ApiBase() = default; + + /// Requests list of available products. Returns request identificator immediately. + virtual void GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, + ProductsCallback const & successFn, + ErrorProviderCallback const & errorFn) = 0; + + /// Returns link which allows you to launch some taxi app. + virtual RideRequestLinks GetRideRequestLinks(std::string const & productId, + ms::LatLon const & from, + ms::LatLon const & to) const = 0; +}; + +inline std::string DebugPrint(ErrorCode error) +{ + switch (error) + { + case ErrorCode::NoProducts: return "NoProducts"; + case ErrorCode::RemoteError: return "RemoteError"; + } +} +} // namespace taxi diff --git a/partners_api/taxi_engine.cpp b/partners_api/taxi_engine.cpp new file mode 100644 index 0000000000..50807bc2f6 --- /dev/null +++ b/partners_api/taxi_engine.cpp @@ -0,0 +1,203 @@ +#include "partners_api/taxi_engine.hpp" +#include "partners_api/uber_api.hpp" +#include "partners_api/yandex_api.hpp" + +#include "base/macros.hpp" +#include "base/stl_add.hpp" + +#include +#include +#include + +namespace +{ +template +Iter FindByProviderType(taxi::Provider::Type type, Iter first, Iter last) +{ + using IterValueType = typename std::iterator_traits::value_type; + return std::find_if(first, last, + [type](IterValueType const & item) { return item.m_type == type; }); +} +} // namespace + +namespace taxi +{ +void ResultMaker::Reset(uint64_t requestId, size_t requestsCount, + SuccessfullCallback const & successCallback, + ErrorCallback const & errorCallback) +{ + std::lock_guard lock(m_mutex); + + m_requestId = requestId; + m_requestsCount = static_cast(requestsCount); + m_successCallback = successCallback; + m_errorCallback = errorCallback; + m_providers.clear(); + m_errors.clear(); +} + +void ResultMaker::DecrementRequestCount(uint64_t requestId) +{ + std::lock_guard lock(m_mutex); + + if (m_requestId != requestId) + return; + + DecrementRequestCount(); +} + +void ResultMaker::ProcessProducts(uint64_t requestId, Provider::Type type, + std::vector const & products) +{ + std::lock_guard lock(m_mutex); + + if (m_requestId != requestId) + return; + + m_providers.emplace_back(type, products); + + DecrementRequestCount(); +} + +void ResultMaker::ProcessError(uint64_t requestId, Provider::Type type, ErrorCode code) +{ + std::lock_guard lock(m_mutex); + + if (m_requestId != requestId) + return; + + m_errors.emplace_back(type, code); + + DecrementRequestCount(); +} + +void ResultMaker::MakeResult(uint64_t requestId) const +{ + SuccessfullCallback successCallback; + ErrorCallback errorCallback; + ProvidersContainer providers; + ErrorsContainer errors; + + { + std::lock_guard lock(m_mutex); + + if (m_requestId != requestId || m_requestsCount != 0) + return; + + successCallback = m_successCallback; + errorCallback = m_errorCallback; + providers = m_providers; + errors = m_errors; + } + + if (providers.empty()) + return errorCallback(errors, requestId); + + return successCallback(providers, requestId); +} + +void ResultMaker::DecrementRequestCount() +{ + CHECK_GREATER(m_requestsCount, 0, ()); + --m_requestsCount; +} + +Engine::Engine() +{ + m_enabledCountries = {{Provider::Type::Yandex, {"Russian Federation"}}}; + m_disabledCountries = {{Provider::Type::Uber, {"Russian Federation"}}}; + + m_apis.emplace_back(Provider::Type::Yandex, my::make_unique()); + m_apis.emplace_back(Provider::Type::Uber, my::make_unique()); +} + +/// Requests list of available products. Returns request identificator immediately. +uint64_t Engine::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, + storage::TCountriesVec const & countryIds, + SuccessfullCallback const & successFn, + ErrorCallback const & failFn) +{ + auto const reqId = ++m_requestId; + auto const maker = m_maker; + + maker->Reset(reqId, m_apis.size(), successFn, failFn); + for (auto const & api : m_apis) + { + auto type = api.m_type; + + if (IsCountryDisabled(type, countryIds) || !IsCountryEnabled(type, countryIds)) + { + maker->DecrementRequestCount(reqId); + maker->MakeResult(reqId); + continue; + } + + auto const productCallback = [type, maker, reqId](std::vector const & products) + { + maker->ProcessProducts(reqId, type, products); + maker->MakeResult(reqId); + }; + + auto const errorCallback = [type, maker, reqId](ErrorCode const code) + { + maker->ProcessError(reqId, type, code); + maker->MakeResult(reqId); + }; + + api.m_api->GetAvailableProducts(from, to, productCallback, errorCallback); + } + + return reqId; +} + +/// Returns link which allows you to launch some taxi app. +RideRequestLinks Engine::GetRideRequestLinks(Provider::Type type, std::string const & productId, + ms::LatLon const & from, ms::LatLon const & to) const +{ + auto const it = FindByProviderType(type, m_apis.cbegin(), m_apis.cend()); + + CHECK(it != m_apis.cend(), ()); + + return it->m_api->GetRideRequestLinks(productId, from, to); +} + +bool Engine::IsCountryDisabled(Provider::Type type, storage::TCountriesVec const & countryIds) const +{ + auto const it = + FindByProviderType(type, m_disabledCountries.cbegin(), m_disabledCountries.cend()); + + if (it == m_disabledCountries.end()) + return false; + + auto const & disabledCountries = it->m_countries; + bool isCountryDisabled = true; + for (auto const & countryId : countryIds) + { + auto const countryIt = + std::find(disabledCountries.cbegin(), disabledCountries.cend(), countryId); + + isCountryDisabled = isCountryDisabled && countryIt != disabledCountries.cend(); + } + + return isCountryDisabled; +} + +bool Engine::IsCountryEnabled(Provider::Type type, storage::TCountriesVec const & countryIds) const +{ + auto const it = FindByProviderType(type, m_enabledCountries.cbegin(), m_enabledCountries.cend()); + + if (it == m_enabledCountries.end()) + return true; + + auto const & enabledCountries = it->m_countries; + for (auto const & countryId : countryIds) + { + auto const countryIt = std::find(enabledCountries.cbegin(), enabledCountries.cend(), countryId); + + if (countryIt != enabledCountries.cend()) + return true; + } + + return false; +} +} // namespace taxi diff --git a/partners_api/taxi_engine.hpp b/partners_api/taxi_engine.hpp new file mode 100644 index 0000000000..fc652d8010 --- /dev/null +++ b/partners_api/taxi_engine.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include "storage/index.hpp" + +#include "partners_api/taxi_base.hpp" + +#include +#include +#include + +namespace taxi +{ +using SuccessfullCallback = + std::function; + +using ErrorCallback = std::function; + +class ResultMaker +{ +public: + void Reset(uint64_t requestId, size_t requestsCount, SuccessfullCallback const & successCallback, + ErrorCallback const & errorCallback); + void DecrementRequestCount(uint64_t requestId); + + void ProcessProducts(uint64_t requestId, Provider::Type type, + std::vector const & products); + void ProcessError(uint64_t requestId, Provider::Type type, ErrorCode code); + + void MakeResult(uint64_t requestId) const; + +private: + void DecrementRequestCount(); + + mutable std::mutex m_mutex; + uint64_t m_requestId = 0; + SuccessfullCallback m_successCallback; + ErrorCallback m_errorCallback; + + int8_t m_requestsCount = 0; + ErrorsContainer m_errors; + ProvidersContainer m_providers; +}; + +class Engine final +{ +public: + Engine(); + + /// Requests list of available products. Returns request identificator immediately. + uint64_t GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, + storage::TCountriesVec const & countryIds, + SuccessfullCallback const & successFn, + ErrorCallback const & errorFn); + + /// Returns link which allows you to launch some taxi app. + RideRequestLinks GetRideRequestLinks(Provider::Type type, std::string const & productId, + ms::LatLon const & from, ms::LatLon const & to) const; + +private: + bool IsCountryDisabled(Provider::Type type, storage::TCountriesVec const & countryIds) const; + bool IsCountryEnabled(Provider::Type type, storage::TCountriesVec const & countryIds) const; + + using ApiPtr = std::unique_ptr; + + struct ApiContainerItem + { + ApiContainerItem(Provider::Type type, ApiPtr && api) : m_type(type), m_api(std::move(api)) {} + + Provider::Type m_type; + ApiPtr m_api; + }; + + std::vector m_apis; + + struct SupportedCountriesItem + { + Provider::Type m_type; + storage::TCountriesVec m_countries; + }; + + std::vector m_enabledCountries; + std::vector m_disabledCountries; + + uint64_t m_requestId = 0; + std::shared_ptr m_maker = std::make_shared(); +}; +} // namespace taxi diff --git a/partners_api/taxi_provider.hpp b/partners_api/taxi_provider.hpp new file mode 100644 index 0000000000..f70148f15e --- /dev/null +++ b/partners_api/taxi_provider.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +namespace taxi +{ +struct Product +{ + std::string m_productId; // unique id of product for provider + std::string m_name; // name of product + std::string m_time; // wait time + std::string m_price; // for some currencies this field contains symbol of currency but not always + std::string m_currency; // currency can be empty, for ex. when m_price equal to Metered +}; + +class Provider +{ +public: + enum Type + { + Uber, + Yandex + }; + + using Iter = std::vector::iterator; + using ConstIter = std::vector::const_iterator; + + Provider(Type type, std::vector const & products) : m_type(type), m_products(products) {} + + Type GetType() const { return m_type; } + std::vector const & GetProducts() const { return m_products; } + Product const & operator[](size_t i) const { return m_products[i]; } + Product & operator[](size_t i) { return m_products[i]; } + + Iter begin() { return m_products.begin(); } + Iter end() { return m_products.end(); } + ConstIter begin() const { return m_products.cbegin(); } + ConstIter end() const { return m_products.cend(); } + +private: + Type m_type; + std::vector m_products; +}; + +using ProvidersContainer = std::vector; + +enum class ErrorCode +{ + NoProducts, + RemoteError +}; + +struct ProviderError +{ + ProviderError(Provider::Type type, ErrorCode code) : m_type(type), m_code(code) {} + + Provider::Type m_type; + ErrorCode m_code; +}; + +using ErrorsContainer = std::vector; + +inline std::string DebugPrint(Provider::Type type) +{ + switch (type) + { + case Provider::Type::Uber: return "Uber"; + case Provider::Type::Yandex: return "Yandex"; + } +} +} // namespace taxi diff --git a/partners_api/uber_api.cpp b/partners_api/uber_api.cpp index 850667d5ed..73a587f384 100644 --- a/partners_api/uber_api.cpp +++ b/partners_api/uber_api.cpp @@ -47,18 +47,18 @@ bool CheckUberAnswer(json_t const * answer) return true; } -bool IsIncomplete(uber::Product const & p) +bool IsIncomplete(taxi::Product const & p) { return p.m_name.empty() || p.m_productId.empty() || p.m_time.empty() || p.m_price.empty(); } -void FillProducts(json_t const * time, json_t const * price, vector & products) +void FillProducts(json_t const * time, json_t const * price, vector & products) { // Fill data from time. auto const timeSize = json_array_size(time); for (size_t i = 0; i < timeSize; ++i) { - uber::Product product; + taxi::Product product; int64_t estimatedTime = 0; auto const item = json_array_get(time, i); FromJSONObject(item, "display_name", product.m_name); @@ -75,7 +75,7 @@ void FillProducts(json_t const * time, json_t const * price, vector & products) +void MakeFromJson(char const * times, char const * prices, vector & products) { products.clear(); try @@ -125,6 +125,8 @@ string GetUberURL() } } // namespace +namespace taxi +{ namespace uber { // static @@ -191,9 +193,9 @@ void ProductMaker::SetPrices(uint64_t const requestId, string const & prices) } void ProductMaker::MakeProducts(uint64_t const requestId, ProductsCallback const & successFn, - ErrorCallback const & errorFn) + ErrorProviderCallback const & errorFn) { - vector products; + vector products; { lock_guard lock(m_mutex); @@ -207,13 +209,14 @@ void ProductMaker::MakeProducts(uint64_t const requestId, ProductsCallback const } if (products.empty()) - errorFn(ErrorCode::NoProducts, requestId); + errorFn(ErrorCode::NoProducts); else - successFn(products, requestId); + successFn(products); } -uint64_t Api::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, - ProductsCallback const & successFn, ErrorCallback const & errorFn) +void Api::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, + ProductsCallback const & successFn, + ErrorProviderCallback const & errorFn) { auto const reqId = ++m_requestId; auto const maker = m_maker; @@ -225,7 +228,7 @@ uint64_t Api::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & t string result; if (!RawApi::GetEstimatedTime(from, result)) { - errorFn(ErrorCode::RemoteError, reqId); + errorFn(ErrorCode::RemoteError); return; } @@ -238,20 +241,17 @@ uint64_t Api::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & t string result; if (!RawApi::GetEstimatedPrice(from, to, result)) { - errorFn(ErrorCode::RemoteError, reqId); + errorFn(ErrorCode::RemoteError); return; } maker->SetPrices(reqId, result); maker->MakeProducts(reqId, successFn, errorFn); }).detach(); - - return reqId; } -// static RideRequestLinks Api::GetRideRequestLinks(string const & productId, ms::LatLon const & from, - ms::LatLon const & to) + ms::LatLon const & to) const { stringstream url; url << fixed << setprecision(6) @@ -266,13 +266,5 @@ void SetUberUrlForTesting(string const & url) { g_uberUrlForTesting = url; } - -string DebugPrint(ErrorCode error) -{ - switch (error) - { - case ErrorCode::NoProducts: return "NoProducts"; - case ErrorCode::RemoteError: return "RemoteError"; - } -} } // namespace uber +} // namespace taxi diff --git a/partners_api/uber_api.hpp b/partners_api/uber_api.hpp index 34cf270fab..436310c04b 100644 --- a/partners_api/uber_api.hpp +++ b/partners_api/uber_api.hpp @@ -1,5 +1,7 @@ #pragma once +#include "partners_api/taxi_base.hpp" + #include "std/function.hpp" #include "std/mutex.hpp" #include "std/shared_ptr.hpp" @@ -17,6 +19,8 @@ namespace downloader class HttpRequest; } +namespace taxi +{ namespace uber { // Uber api wrapper based on synchronous http requests. @@ -42,27 +46,6 @@ public: static bool GetEstimatedPrice(ms::LatLon const & from, ms::LatLon const & to, string & result); }; -struct Product -{ - string m_productId; - string m_name; - string m_time; - string m_price; // for some currencies this field contains symbol of currency but not always - string m_currency; // currency can be empty, for ex. when m_price equal to Metered -}; -/// @products - vector of available products for requested route, cannot be empty. -/// @requestId - identificator which was provided to GetAvailableProducts to identify request. -using ProductsCallback = function const & products, uint64_t const requestId)>; - -enum class ErrorCode -{ - NoProducts, - RemoteError -}; - -/// Callback which is called when an errors occurs. -using ErrorCallback = function; - /// Class which used for making products from http requests results. class ProductMaker { @@ -71,7 +54,7 @@ public: void SetTimes(uint64_t const requestId, string const & times); void SetPrices(uint64_t const requestId, string const & prices); void MakeProducts(uint64_t const requestId, ProductsCallback const & successFn, - ErrorCallback const & errorFn); + ErrorProviderCallback const & errorFn); private: uint64_t m_requestId = 0; @@ -80,22 +63,17 @@ private: mutex m_mutex; }; -struct RideRequestLinks -{ - string m_deepLink; - string m_universalLink; -}; - -class Api +class Api : public ApiBase { public: - /// Requests list of available products from Uber. Returns request identificator immediately. - uint64_t GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, - ProductsCallback const & successFn, ErrorCallback const & errorFn); + /// Requests list of available products from Uber. Returns request identifier immediately. + void GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, + ProductsCallback const & successFn, + ErrorProviderCallback const & errorFn) override; /// Returns link which allows you to launch the Uber app. - static RideRequestLinks GetRideRequestLinks(string const & productId, ms::LatLon const & from, - ms::LatLon const & to); + RideRequestLinks GetRideRequestLinks(string const & productId, ms::LatLon const & from, + ms::LatLon const & to) const override; private: shared_ptr m_maker = make_shared(); @@ -103,5 +81,5 @@ private: }; void SetUberUrlForTesting(string const & url); -string DebugPrint(ErrorCode error); } // namespace uber +} // namespace taxi diff --git a/partners_api/yandex_api.cpp b/partners_api/yandex_api.cpp new file mode 100644 index 0000000000..10040a5aec --- /dev/null +++ b/partners_api/yandex_api.cpp @@ -0,0 +1,159 @@ +#include "partners_api/yandex_api.hpp" + +#include "platform/http_client.hpp" + +#include "geometry/latlon.hpp" + +#include "base/logging.hpp" +#include "base/thread.hpp" + +#include "3party/jansson/myjansson.hpp" + +#include "private.h" + +#include +#include +#include + +namespace +{ +std::string const kTaxiInfoUrl = "https://taxi-routeinfo.taxi.yandex.net"; +std::string g_yandexUrlForTesting = ""; + +bool RunSimpleHttpRequest(string const & url, string & result) +{ + platform::HttpClient request(url); + request.SetRawHeader("Accept", "application/json"); + if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200) + { + result = request.ServerResponse(); + return true; + } + + return false; +} + +bool CheckYandexAnswer(json_t const * answer) +{ + if (answer == nullptr) + return false; + + if (!json_is_array(answer)) + return false; + + if (json_array_size(answer) <= 0) + return false; + + return true; +} + +string GetYandexURL() +{ + if (!g_yandexUrlForTesting.empty()) + return g_yandexUrlForTesting; + + return kTaxiInfoUrl; +} +} // namespace + +namespace taxi +{ +namespace yandex +{ +bool RawApi::GetTaxiInfo(ms::LatLon const & from, ms::LatLon const & to, std::string & result) +{ + std::stringstream url; + url << std::fixed << std::setprecision(6) << GetYandexURL() + << "/taxi_info?clid=" << YANDEX_CLIENT_ID << "&apikey=" << YANDEX_API_KEY + << "&rll=" << from.lon << "," << from.lat << "~" << to.lon << "," << to.lat + << "&class=econom,business,comfortplus,minivan,vip"; + + return RunSimpleHttpRequest(url.str(), result); +} + +/// Requests list of available products from Yandex. Returns request identificator immediately. +void Api::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, + ProductsCallback const & successFn, + ErrorProviderCallback const & errorFn) +{ + threads::SimpleThread([from, to, successFn, errorFn]() + { + string result; + if (!RawApi::GetTaxiInfo(from, to, result)) + { + errorFn(ErrorCode::RemoteError); + return; + } + + std::vector products; + try + { + MakeFromJson(result, products); + } + catch (my::Json::Exception const & e) + { + LOG(LERROR, (e.Msg())); + products.clear(); + } + + if (products.empty()) + errorFn(ErrorCode::NoProducts); + else + successFn(products); + + }).detach(); +} + +/// Returns link which allows you to launch the Yandex app. +RideRequestLinks Api::GetRideRequestLinks(string const & productId, ms::LatLon const & from, + ms::LatLon const & to) const +{ + std::ostringstream deepLink; + deepLink << YANDEX_BASE_URL << "%3A%2F%2Froute%3Fstart-lat%3D" << from.lat << "%26start-lon%3D" + << from.lon << "%26end-lat%3D" << to.lat << "%26end-lon%3D" << to.lon + << "%26utm_source%3Dmapsme"; + + return {deepLink.str(), ""}; +} + +void MakeFromJson(std::string const & src, std::vector & products) +{ + products.clear(); + + my::Json root(src.c_str()); + auto const productsArray = json_object_get(root.get(), "options"); + if (!CheckYandexAnswer(productsArray)) + return; + + string currency; + FromJSONObject(root.get(), "currency", currency); + + auto const productsSize = json_array_size(productsArray); + for (size_t i = 0; i < productsSize; ++i) + { + taxi::Product product; + double time = 0.0; + int64_t price = 0; + auto const item = json_array_get(productsArray, i); + + FromJSONObjectOptionalField(item, "waiting_time", time); + // Temporary unavailable. + if (time == 0.0) + continue; + + FromJSONObject(item, "class", product.m_name); + FromJSONObject(item, "price", price); + + product.m_price = strings::to_string(price); + product.m_time = strings::to_string(static_cast(time)); + product.m_currency = currency; + products.push_back(move(product)); + } +} + +void SetYandexUrlForTesting(std::string const & url) +{ + g_yandexUrlForTesting = url; +} +} // namespace yandex +} // namespace taxi diff --git a/partners_api/yandex_api.hpp b/partners_api/yandex_api.hpp new file mode 100644 index 0000000000..21ac6ea0e2 --- /dev/null +++ b/partners_api/yandex_api.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "partners_api/taxi_base.hpp" + +#include + +namespace ms +{ +class LatLon; +} + +namespace taxi +{ +namespace yandex +{ +class RawApi +{ +public: + static bool GetTaxiInfo(ms::LatLon const & from, ms::LatLon const & to, std::string & result); +}; + +class Api : public ApiBase +{ +public: + /// Requests list of available products from Yandex. Returns request identifier immediately. + void GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, + ProductsCallback const & successFn, + ErrorProviderCallback const & errorFn) override; + + /// Returns link which allows you to launch the Yandex app. + RideRequestLinks GetRideRequestLinks(std::string const & productId, ms::LatLon const & from, + ms::LatLon const & to) const override; +}; + +void MakeFromJson(std::string const & src, std::vector & products); +void SetYandexUrlForTesting(std::string const & url); +} // namespace yandex +} // namespace taxi diff --git a/tools/python/ResponseProvider.py b/tools/python/ResponseProvider.py index 2c647d1937..881a444861 100644 --- a/tools/python/ResponseProvider.py +++ b/tools/python/ResponseProvider.py @@ -143,6 +143,7 @@ class ResponseProvider: "/partners/price": self.partners_price, "/booking/min_price": self.partners_minprice, "/booking/min_price.getHotelAvailability": self.partners_minprice, + "/partners/taxi_info": self.partners_yandex_taxi_info, }[url]() except: return self.test_404() @@ -221,6 +222,9 @@ class ResponseProvider: def partners_minprice(self): return Payload(jsons.PARTNERS_MINPRICE) + def partners_yandex_taxi_info(self): + return Payload(jsons.PARTNERS_TAXI_INFO) + def kill(self): logging.debug("Kill called in ResponseProvider") self.delegate.kill() diff --git a/tools/python/jsons.py b/tools/python/jsons.py index 58928c9dcf..b495d507fb 100644 --- a/tools/python/jsons.py +++ b/tools/python/jsons.py @@ -208,3 +208,43 @@ PARTNERS_MINPRICE = """ } ] """ + +PARTNERS_TAXI_INFO = """ +{ + "currency": "RUB", + "distance": 6888.846981748964, + "options": [ + { + "class": "econom", + "min_price": 129, + "price": 344, + "waiting_time": 527.8793726078095 + }, + { + "class": "business", + "min_price": 239, + "price": 504, + "waiting_time": 76.37023611385494 + }, + { + "class": "comfortplus", + "min_price": 239, + "price": 557, + "waiting_time": 99.0058955445591 + }, + { + "class": "minivan", + "min_price": 239, + "price": 532, + "waiting_time": 322.77413167989687 + }, + { + "class": "vip", + "min_price": 359, + "price": 799, + "waiting_time": 223.34814145904883 + } + ], + "time": 1057.7440430297368 +} +""" diff --git a/traffic/traffic_info.cpp b/traffic/traffic_info.cpp index b2f73daf9a..5a795b1c82 100644 --- a/traffic/traffic_info.cpp +++ b/traffic/traffic_info.cpp @@ -517,7 +517,7 @@ TrafficInfo::ServerDataStatus TrafficInfo::ProcessFailure(platform::HttpClient c case 404: /* Not Found */ { int64_t version = 0; - strings::to_int64(request.ServerResponse().c_str(), version); + ASSERT(strings::to_int64(request.ServerResponse().c_str(), version), ()); if (version > mwmVersion && version <= m_currentDataVersion) m_availability = Availability::ExpiredData; diff --git a/xcode/partners_api/partners_api.xcodeproj/project.pbxproj b/xcode/partners_api/partners_api.xcodeproj/project.pbxproj index 9f58ce8193..bc28ae4317 100644 --- a/xcode/partners_api/partners_api.xcodeproj/project.pbxproj +++ b/xcode/partners_api/partners_api.xcodeproj/project.pbxproj @@ -25,6 +25,14 @@ 3DFEBF861EF82BEA00317D5C /* viator_api.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3DFEBF841EF82BEA00317D5C /* viator_api.hpp */; }; 3DFEBF891EF82C1300317D5C /* viator_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3DFEBF871EF82C1300317D5C /* viator_tests.cpp */; }; 3DFEBF8A1EF82C1300317D5C /* mopub_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3DFEBF881EF82C1300317D5C /* mopub_tests.cpp */; }; + 3DFEBF9A1EFBFC1500317D5C /* taxi_base.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3DFEBF941EFBFC1500317D5C /* taxi_base.hpp */; }; + 3DFEBF9B1EFBFC1500317D5C /* taxi_engine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3DFEBF951EFBFC1500317D5C /* taxi_engine.cpp */; }; + 3DFEBF9C1EFBFC1500317D5C /* taxi_engine.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3DFEBF961EFBFC1500317D5C /* taxi_engine.hpp */; }; + 3DFEBF9D1EFBFC1500317D5C /* taxi_provider.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3DFEBF971EFBFC1500317D5C /* taxi_provider.hpp */; }; + 3DFEBF9E1EFBFC1500317D5C /* yandex_api.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3DFEBF981EFBFC1500317D5C /* yandex_api.cpp */; }; + 3DFEBF9F1EFBFC1500317D5C /* yandex_api.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3DFEBF991EFBFC1500317D5C /* yandex_api.hpp */; }; + 3DFEBFA31EFBFC2300317D5C /* taxi_engine_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3DFEBFA01EFBFC2300317D5C /* taxi_engine_tests.cpp */; }; + 3DFEBFA41EFBFC2300317D5C /* yandex_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3DFEBFA11EFBFC2300317D5C /* yandex_tests.cpp */; }; F67E75251DB8F06F00D6741F /* opentable_api.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F67E75231DB8F06F00D6741F /* opentable_api.cpp */; }; F67E75261DB8F06F00D6741F /* opentable_api.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F67E75241DB8F06F00D6741F /* opentable_api.hpp */; }; F6B536401DA520E40067EEA5 /* booking_api.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6B5363C1DA520E40067EEA5 /* booking_api.cpp */; }; @@ -66,6 +74,14 @@ 3DFEBF841EF82BEA00317D5C /* viator_api.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = viator_api.hpp; sourceTree = ""; }; 3DFEBF871EF82C1300317D5C /* viator_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = viator_tests.cpp; sourceTree = ""; }; 3DFEBF881EF82C1300317D5C /* mopub_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mopub_tests.cpp; sourceTree = ""; }; + 3DFEBF941EFBFC1500317D5C /* taxi_base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = taxi_base.hpp; sourceTree = ""; }; + 3DFEBF951EFBFC1500317D5C /* taxi_engine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = taxi_engine.cpp; sourceTree = ""; }; + 3DFEBF961EFBFC1500317D5C /* taxi_engine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = taxi_engine.hpp; sourceTree = ""; }; + 3DFEBF971EFBFC1500317D5C /* taxi_provider.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = taxi_provider.hpp; sourceTree = ""; }; + 3DFEBF981EFBFC1500317D5C /* yandex_api.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = yandex_api.cpp; sourceTree = ""; }; + 3DFEBF991EFBFC1500317D5C /* yandex_api.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = yandex_api.hpp; sourceTree = ""; }; + 3DFEBFA01EFBFC2300317D5C /* taxi_engine_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = taxi_engine_tests.cpp; sourceTree = ""; }; + 3DFEBFA11EFBFC2300317D5C /* yandex_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = yandex_tests.cpp; sourceTree = ""; }; F67E75231DB8F06F00D6741F /* opentable_api.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opentable_api.cpp; sourceTree = ""; }; F67E75241DB8F06F00D6741F /* opentable_api.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = opentable_api.hpp; sourceTree = ""; }; F6B536341DA5209F0067EEA5 /* libpartners_api.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libpartners_api.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -140,6 +156,12 @@ children = ( 3DFEBF831EF82BEA00317D5C /* viator_api.cpp */, 3DFEBF841EF82BEA00317D5C /* viator_api.hpp */, + 3DFEBF941EFBFC1500317D5C /* taxi_base.hpp */, + 3DFEBF951EFBFC1500317D5C /* taxi_engine.cpp */, + 3DFEBF961EFBFC1500317D5C /* taxi_engine.hpp */, + 3DFEBF971EFBFC1500317D5C /* taxi_provider.hpp */, + 3DFEBF981EFBFC1500317D5C /* yandex_api.cpp */, + 3DFEBF991EFBFC1500317D5C /* yandex_api.hpp */, 3430643A1E9FBCF500DC7665 /* mopub_ads.cpp */, 3430643B1E9FBCF500DC7665 /* mopub_ads.hpp */, 346E888F1E9D087400D4CE9B /* ads_base.cpp */, @@ -166,7 +188,9 @@ isa = PBXGroup; children = ( 3DFEBF871EF82C1300317D5C /* viator_tests.cpp */, - 3DFEBF881EF82C1300317D5C /* mopub_tests.cpp */, + 3DFEBFA01EFBFC2300317D5C /* taxi_engine_tests.cpp */, + 3DFEBFA11EFBFC2300317D5C /* yandex_tests.cpp */, + 3DFEBFA21EFBFC2300317D5C /* mopub_tests.cpp */, 346E889D1E9D088200D4CE9B /* ads_engine_tests.cpp */, 346E889E1E9D088200D4CE9B /* rb_tests.cpp */, 3DBC1C501E4B14810016897F /* facebook_tests.cpp */, @@ -214,8 +238,12 @@ 3430643D1E9FBCF500DC7665 /* mopub_ads.hpp in Headers */, 3DFEBF861EF82BEA00317D5C /* viator_api.hpp in Headers */, 346E889C1E9D087400D4CE9B /* rb_ads.hpp in Headers */, + 3DFEBF9A1EFBFC1500317D5C /* taxi_base.hpp in Headers */, 346E889A1E9D087400D4CE9B /* banner.hpp in Headers */, + 3DFEBF9F1EFBFC1500317D5C /* yandex_api.hpp in Headers */, + 3DFEBF9D1EFBFC1500317D5C /* taxi_provider.hpp in Headers */, 346E88991E9D087400D4CE9B /* ads_engine.hpp in Headers */, + 3DFEBF9C1EFBFC1500317D5C /* taxi_engine.hpp in Headers */, F6B536431DA520E40067EEA5 /* uber_api.hpp in Headers */, 3DBC1C551E4B14920016897F /* facebook_ads.hpp in Headers */, ); @@ -312,17 +340,22 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3DFEBFA41EFBFC2300317D5C /* yandex_tests.cpp in Sources */, 3430643C1E9FBCF500DC7665 /* mopub_ads.cpp in Sources */, 346E88961E9D087400D4CE9B /* ads_base.cpp in Sources */, 3DFEBF891EF82C1300317D5C /* viator_tests.cpp in Sources */, 3DFEBF851EF82BEA00317D5C /* viator_api.cpp in Sources */, F6B536421DA520E40067EEA5 /* uber_api.cpp in Sources */, + 3DFEBF9B1EFBFC1500317D5C /* taxi_engine.cpp in Sources */, 346E889B1E9D087400D4CE9B /* rb_ads.cpp in Sources */, + 3DFEBFA51EFBFC2300317D5C /* mopub_tests.cpp in Sources */, 3DBC1C541E4B14920016897F /* facebook_ads.cpp in Sources */, 3DFEBF8A1EF82C1300317D5C /* mopub_tests.cpp in Sources */, 346E88981E9D087400D4CE9B /* ads_engine.cpp in Sources */, F67E75251DB8F06F00D6741F /* opentable_api.cpp in Sources */, + 3DFEBFA31EFBFC2300317D5C /* taxi_engine_tests.cpp in Sources */, F6B536401DA520E40067EEA5 /* booking_api.cpp in Sources */, + 3DFEBF9E1EFBFC1500317D5C /* yandex_api.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };