[partners_api] yandex + taxi engine

This commit is contained in:
Arsentiy Milchakov 2017-06-22 18:20:54 +03:00 committed by Yuri Gorshenin
parent 1cb6b0777b
commit c99adbfb29
27 changed files with 1163 additions and 212 deletions

View file

@ -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,

View file

@ -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,

View file

@ -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<uber::Product> 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<uber::Product> 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<uber::Product> 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));

View file

@ -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());
}
/**

View file

@ -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
{

View file

@ -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

View file

@ -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;

View file

@ -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() <UICollectionViewDataSource, UICollectionViewDelegate>
{
@ -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<Product> 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())];

View file

@ -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<dp::Color> colorList = { dp::Color(255, 0, 0, 255), dp::Color(0, 255, 0, 255), dp::Color(0, 0, 255, 255),

View file

@ -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<booking::Api> m_bookingApi = make_unique<booking::Api>();
unique_ptr<uber::Api> m_uberApi = make_unique<uber::Api>();
unique_ptr<viator::Api> m_viatorApi = make_unique<viator::Api>();
unique_ptr<taxi::Engine> m_taxiEngine = make_unique<taxi::Engine>();
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<CityFinder> m_cityFinder;

View file

@ -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

View file

@ -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 \

View file

@ -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<taxi::Product> 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<taxi::Product> uberProducts;
maker.Reset(reqId);
maker.SetTimes(reqId, times);
maker.SetPrices(reqId, prices);
maker.MakeProducts(
reqId, [&uberProducts](vector<taxi::Product> const & products) { uberProducts = products; },
[](taxi::ErrorCode const code) { TEST(false, ()); });
return uberProducts;
}
std::vector<taxi::Product> GetYandexSynchronous(ms::LatLon const & from, ms::LatLon const & to)
{
std::string yandexAnswer;
std::vector<taxi::Product> 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<taxi::Product> products1 =
{
{"1", "", "", "", ""},
{"2", "", "", "", ""},
{"3", "", "", "", ""},
};
std::vector<taxi::Product> 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<std::mutex> 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<mutex> lock(resultsMutex);
reqId = engine.GetAvailableProducts(
ms::LatLon(55.753960, 37.624513), ms::LatLon(55.765866, 37.661270),
{"Brazil", "Russian Federation"}, standardCallback, errorPossibleCallback);
}
{
lock_guard<mutex> lock(resultsMutex);
reqId = engine.GetAvailableProducts(
ms::LatLon(59.922445, 30.367201), ms::LatLon(59.943675, 30.361123),
{"Brazil", "Russian Federation"}, standardCallback, errorPossibleCallback);
}
{
lock_guard<mutex> lock(resultsMutex);
reqId = engine.GetAvailableProducts(
ms::LatLon(52.509621, 13.450067), ms::LatLon(52.510811, 13.409490),
{"Brazil", "Russian Federation"}, standardCallback, errorPossibleCallback);
}
{
lock_guard<mutex> lock(resultsMutex);
reqId = engine.GetAvailableProducts(from, to, {"Brazil", "Russian Federation"}, lastCallback,
errorCallback);
}
}
testing::RunEventLoop();
CompareProviders(providersContainer, synchronousProviders);
}
} // namespace

View file

@ -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<uber::Product> returnedProducts;
vector<Product> 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<uber::Product> const & products,
size_t const requestId) {
returnedId = requestId;
[&returnedProducts](vector<Product> 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<uber::Product> const & products, size_t const requestId)
maker.MakeProducts(reqId + 1, [](vector<Product> 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<uber::Product> 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<taxi::Product> resultProducts;
auto const errorPossibleCallback = [](uber::ErrorCode const code, uint64_t const requestId)
{
TEST(code == uber::ErrorCode::NoProducts, ());
};
auto const standardCallback =
[&reqId, &productsContainer, &resultsMutex](vector<uber::Product> const & products, size_t const requestId)
{
lock_guard<mutex> lock(resultsMutex);
if (reqId == requestId)
productsContainer = products;
};
auto const lastCallback =
[&standardCallback](vector<uber::Product> 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<mutex> lock(resultsMutex);
reqId = uberApi.GetAvailableProducts(ms::LatLon(55.753960, 37.624513),
ms::LatLon(55.765866, 37.661270), standardCallback,
errorPossibleCallback);
}
{
lock_guard<mutex> lock(resultsMutex);
reqId = uberApi.GetAvailableProducts(ms::LatLon(59.922445, 30.367201),
ms::LatLon(59.943675, 30.361123), standardCallback,
errorPossibleCallback);
}
{
lock_guard<mutex> lock(resultsMutex);
reqId = uberApi.GetAvailableProducts(ms::LatLon(52.509621, 13.450067),
ms::LatLon(52.510811, 13.409490), standardCallback,
errorPossibleCallback);
}
{
lock_guard<mutex> lock(resultsMutex);
reqId = uberApi.GetAvailableProducts(from, to, lastCallback, errorCallback);
}
}
api.GetAvailableProducts(from, to,
[&resultProducts](std::vector<taxi::Product> 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(), ());
}

View file

@ -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<taxi::Product> resultProducts;
api.GetAvailableProducts(from, to,
[&resultProducts](std::vector<taxi::Product> 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

View file

@ -0,0 +1,53 @@
#pragma once
#include "partners_api/taxi_provider.hpp"
#include <functional>
#include <string>
#include <vector>
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<void(std::vector<Product> const & products)>;
/// Callback which is called when an errors occurs.
using ErrorProviderCallback = std::function<void(ErrorCode const code)>;
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

View file

@ -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 <algorithm>
#include <initializer_list>
#include <iterator>
namespace
{
template <typename Iter>
Iter FindByProviderType(taxi::Provider::Type type, Iter first, Iter last)
{
using IterValueType = typename std::iterator_traits<Iter>::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<std::mutex> lock(m_mutex);
m_requestId = requestId;
m_requestsCount = static_cast<uint8_t>(requestsCount);
m_successCallback = successCallback;
m_errorCallback = errorCallback;
m_providers.clear();
m_errors.clear();
}
void ResultMaker::DecrementRequestCount(uint64_t requestId)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_requestId != requestId)
return;
DecrementRequestCount();
}
void ResultMaker::ProcessProducts(uint64_t requestId, Provider::Type type,
std::vector<Product> const & products)
{
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<yandex::Api>());
m_apis.emplace_back(Provider::Type::Uber, my::make_unique<uber::Api>());
}
/// 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<Product> 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

View file

@ -0,0 +1,87 @@
#pragma once
#include "storage/index.hpp"
#include "partners_api/taxi_base.hpp"
#include <memory>
#include <mutex>
#include <vector>
namespace taxi
{
using SuccessfullCallback =
std::function<void(ProvidersContainer const & products, uint64_t const requestId)>;
using ErrorCallback = std::function<void(ErrorsContainer const & errors, uint64_t const requestId)>;
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<Product> 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<ApiBase>;
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<ApiContainerItem> m_apis;
struct SupportedCountriesItem
{
Provider::Type m_type;
storage::TCountriesVec m_countries;
};
std::vector<SupportedCountriesItem> m_enabledCountries;
std::vector<SupportedCountriesItem> m_disabledCountries;
uint64_t m_requestId = 0;
std::shared_ptr<ResultMaker> m_maker = std::make_shared<ResultMaker>();
};
} // namespace taxi

View file

@ -0,0 +1,72 @@
#pragma once
#include <string>
#include <vector>
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<Product>::iterator;
using ConstIter = std::vector<Product>::const_iterator;
Provider(Type type, std::vector<Product> const & products) : m_type(type), m_products(products) {}
Type GetType() const { return m_type; }
std::vector<Product> 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<Product> m_products;
};
using ProvidersContainer = std::vector<Provider>;
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<ProviderError>;
inline std::string DebugPrint(Provider::Type type)
{
switch (type)
{
case Provider::Type::Uber: return "Uber";
case Provider::Type::Yandex: return "Yandex";
}
}
} // namespace taxi

View file

@ -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<uber::Product> & products)
void FillProducts(json_t const * time, json_t const * price, vector<taxi::Product> & 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<uber::Produc
auto const item = json_array_get(price, i);
FromJSONObject(item, "display_name", name);
auto const it = find_if(products.begin(), products.end(), [&name](uber::Product const & product)
auto const it = find_if(products.begin(), products.end(), [&name](taxi::Product const & product)
{
return product.m_name == name;
});
@ -95,7 +95,7 @@ void FillProducts(json_t const * time, json_t const * price, vector<uber::Produc
products.erase(remove_if(products.begin(), products.end(), IsIncomplete), products.end());
}
void MakeFromJson(char const * times, char const * prices, vector<uber::Product> & products)
void MakeFromJson(char const * times, char const * prices, vector<taxi::Product> & 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<uber::Product> products;
vector<Product> products;
{
lock_guard<mutex> 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

View file

@ -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<void(vector<Product> const & products, uint64_t const requestId)>;
enum class ErrorCode
{
NoProducts,
RemoteError
};
/// Callback which is called when an errors occurs.
using ErrorCallback = function<void(ErrorCode const code, uint64_t const requestId)>;
/// 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<ProductMaker> m_maker = make_shared<ProductMaker>();
@ -103,5 +81,5 @@ private:
};
void SetUberUrlForTesting(string const & url);
string DebugPrint(ErrorCode error);
} // namespace uber
} // namespace taxi

159
partners_api/yandex_api.cpp Normal file
View file

@ -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 <iomanip>
#include <limits>
#include <sstream>
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<Product> 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<taxi::Product> & 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<int64_t>(time));
product.m_currency = currency;
products.push_back(move(product));
}
}
void SetYandexUrlForTesting(std::string const & url)
{
g_yandexUrlForTesting = url;
}
} // namespace yandex
} // namespace taxi

View file

@ -0,0 +1,38 @@
#pragma once
#include "partners_api/taxi_base.hpp"
#include <string>
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<taxi::Product> & products);
void SetYandexUrlForTesting(std::string const & url);
} // namespace yandex
} // namespace taxi

View file

@ -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()

View file

@ -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
}
"""

View file

@ -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;

View file

@ -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 = "<group>"; };
3DFEBF871EF82C1300317D5C /* viator_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = viator_tests.cpp; sourceTree = "<group>"; };
3DFEBF881EF82C1300317D5C /* mopub_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mopub_tests.cpp; sourceTree = "<group>"; };
3DFEBF941EFBFC1500317D5C /* taxi_base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = taxi_base.hpp; sourceTree = "<group>"; };
3DFEBF951EFBFC1500317D5C /* taxi_engine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = taxi_engine.cpp; sourceTree = "<group>"; };
3DFEBF961EFBFC1500317D5C /* taxi_engine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = taxi_engine.hpp; sourceTree = "<group>"; };
3DFEBF971EFBFC1500317D5C /* taxi_provider.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = taxi_provider.hpp; sourceTree = "<group>"; };
3DFEBF981EFBFC1500317D5C /* yandex_api.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = yandex_api.cpp; sourceTree = "<group>"; };
3DFEBF991EFBFC1500317D5C /* yandex_api.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = yandex_api.hpp; sourceTree = "<group>"; };
3DFEBFA01EFBFC2300317D5C /* taxi_engine_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = taxi_engine_tests.cpp; sourceTree = "<group>"; };
3DFEBFA11EFBFC2300317D5C /* yandex_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = yandex_tests.cpp; sourceTree = "<group>"; };
F67E75231DB8F06F00D6741F /* opentable_api.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opentable_api.cpp; sourceTree = "<group>"; };
F67E75241DB8F06F00D6741F /* opentable_api.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = opentable_api.hpp; sourceTree = "<group>"; };
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;
};