forked from organicmaps/organicmaps
review fixes - way to provide testing urls for taxi providers is changed
This commit is contained in:
parent
7ae9434a7a
commit
9613778802
9 changed files with 95 additions and 95 deletions
|
@ -10,13 +10,14 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
std::vector<taxi::Product> GetUberSynchronous(ms::LatLon const & from, ms::LatLon const & to)
|
||||
std::vector<taxi::Product> GetUberSynchronous(ms::LatLon const & from, ms::LatLon const & to,
|
||||
std::string const & url)
|
||||
{
|
||||
std::string times;
|
||||
std::string prices;
|
||||
|
||||
TEST(taxi::uber::RawApi::GetEstimatedTime(from, times), ());
|
||||
TEST(taxi::uber::RawApi::GetEstimatedPrice(from, to, prices), ());
|
||||
TEST(taxi::uber::RawApi::GetEstimatedTime(from, times, url), ());
|
||||
TEST(taxi::uber::RawApi::GetEstimatedPrice(from, to, prices, url), ());
|
||||
|
||||
size_t reqId = 0;
|
||||
taxi::uber::ProductMaker maker;
|
||||
|
@ -32,24 +33,26 @@ std::vector<taxi::Product> GetUberSynchronous(ms::LatLon const & from, ms::LatLo
|
|||
return uberProducts;
|
||||
}
|
||||
|
||||
std::vector<taxi::Product> GetYandexSynchronous(ms::LatLon const & from, ms::LatLon const & to)
|
||||
std::vector<taxi::Product> GetYandexSynchronous(ms::LatLon const & from, ms::LatLon const & to,
|
||||
std::string const & url)
|
||||
{
|
||||
std::string yandexAnswer;
|
||||
std::vector<taxi::Product> yandexProducts;
|
||||
|
||||
TEST(taxi::yandex::RawApi::GetTaxiInfo(from, to, yandexAnswer), ());
|
||||
TEST(taxi::yandex::RawApi::GetTaxiInfo(from, to, yandexAnswer, url), ());
|
||||
|
||||
taxi::yandex::MakeFromJson(yandexAnswer, yandexProducts);
|
||||
|
||||
return yandexProducts;
|
||||
}
|
||||
|
||||
taxi::ProvidersContainer GetProvidersSynchronous(ms::LatLon const & from, ms::LatLon const & to)
|
||||
taxi::ProvidersContainer GetProvidersSynchronous(ms::LatLon const & from, ms::LatLon const & to,
|
||||
std::string const & url)
|
||||
{
|
||||
taxi::ProvidersContainer providers;
|
||||
|
||||
providers.emplace_back(taxi::Provider::Type::Uber, GetUberSynchronous(from, to));
|
||||
providers.emplace_back(taxi::Provider::Type::Yandex, GetYandexSynchronous(from, to));
|
||||
providers.emplace_back(taxi::Provider::Type::Uber, GetUberSynchronous(from, to, url));
|
||||
providers.emplace_back(taxi::Provider::Type::Yandex, GetYandexSynchronous(from, to, url));
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
@ -189,12 +192,7 @@ UNIT_TEST(TaxiEngine_Smoke)
|
|||
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(""); });
|
||||
std::string const kTesturl = "http://localhost:34568/partners";
|
||||
|
||||
auto const errorCallback = [](taxi::ErrorsContainer const & errors, uint64_t const requestId) {
|
||||
TEST(false, ());
|
||||
|
@ -221,11 +219,11 @@ UNIT_TEST(TaxiEngine_Smoke)
|
|||
testing::StopEventLoop();
|
||||
};
|
||||
|
||||
taxi::ProvidersContainer const synchronousProviders = GetProvidersSynchronous(from, to);
|
||||
taxi::ProvidersContainer const synchronousProviders = GetProvidersSynchronous(from, to, kTesturl);
|
||||
|
||||
{
|
||||
taxi::Engine engine;
|
||||
|
||||
taxi::Engine engine({{taxi::Provider::Type::Uber, kTesturl},
|
||||
{taxi::Provider::Type::Yandex, kTesturl}});
|
||||
{
|
||||
lock_guard<mutex> lock(resultsMutex);
|
||||
reqId = engine.GetAvailableProducts(
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
#include "geometry/latlon.hpp"
|
||||
|
||||
#include "base/scope_guard.hpp"
|
||||
|
||||
#include "std/algorithm.hpp"
|
||||
#include "std/atomic.hpp"
|
||||
#include "std/mutex.hpp"
|
||||
|
@ -167,13 +165,10 @@ UNIT_TEST(Uber_ProductMaker)
|
|||
|
||||
UNIT_TEST(Uber_GetAvailableProducts)
|
||||
{
|
||||
taxi::uber::Api api;
|
||||
taxi::uber::Api api("http://localhost:34568/partners");
|
||||
ms::LatLon const from(55.796918, 37.537859);
|
||||
ms::LatLon const to(55.758213, 37.616093);
|
||||
|
||||
taxi::uber::SetUberUrlForTesting("http://localhost:34568/partners");
|
||||
MY_SCOPE_GUARD(cleanup, []() { taxi::uber::SetUberUrlForTesting(""); });
|
||||
|
||||
std::vector<taxi::Product> resultProducts;
|
||||
|
||||
api.GetAvailableProducts(from, to,
|
||||
|
@ -181,7 +176,10 @@ UNIT_TEST(Uber_GetAvailableProducts)
|
|||
resultProducts = products;
|
||||
testing::StopEventLoop();
|
||||
},
|
||||
[](taxi::ErrorCode const code) { TEST(false, ()); });
|
||||
[](taxi::ErrorCode const code) {
|
||||
LOG(LWARNING, (code));
|
||||
testing::StopEventLoop();
|
||||
});
|
||||
|
||||
testing::RunEventLoop();
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
#include "geometry/latlon.hpp"
|
||||
|
||||
#include "base/scope_guard.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
UNIT_TEST(Yandex_GetTaxiInfo)
|
||||
|
@ -21,13 +19,10 @@ UNIT_TEST(Yandex_GetTaxiInfo)
|
|||
|
||||
UNIT_TEST(Yandex_GetAvailableProducts)
|
||||
{
|
||||
taxi::yandex::Api api;
|
||||
taxi::yandex::Api api("http://localhost:34568/partners");
|
||||
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,
|
||||
|
@ -35,7 +30,10 @@ UNIT_TEST(Yandex_GetAvailableProducts)
|
|||
resultProducts = products;
|
||||
testing::StopEventLoop();
|
||||
},
|
||||
[](taxi::ErrorCode const code) { TEST(false, ()); });
|
||||
[](taxi::ErrorCode const code) {
|
||||
LOG(LWARNING, (code));
|
||||
testing::StopEventLoop();
|
||||
});
|
||||
|
||||
testing::RunEventLoop();
|
||||
|
||||
|
|
|
@ -107,13 +107,13 @@ void ResultMaker::DecrementRequestCount()
|
|||
}
|
||||
|
||||
// Engine -----------------------------------------------------------------------------------------
|
||||
Engine::Engine()
|
||||
Engine::Engine(std::vector<ProviderUrl> urls /* = {} */)
|
||||
{
|
||||
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>());
|
||||
AddApi<yandex::Api>(urls, Provider::Type::Yandex);
|
||||
AddApi<uber::Api>(urls, Provider::Type::Uber);
|
||||
}
|
||||
|
||||
/// Requests list of available products. Returns request identificator immediately.
|
||||
|
@ -210,4 +210,18 @@ bool Engine::IsAnyCountryEnabled(Provider::Type type,
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename ApiType>
|
||||
void Engine::AddApi(std::vector<ProviderUrl> const & urls, Provider::Type type)
|
||||
{
|
||||
auto const it = std::find_if(urls.cbegin(), urls.cend(), [type](ProviderUrl const & item)
|
||||
{
|
||||
return item.m_type == type;
|
||||
});
|
||||
|
||||
if (it != urls.cend())
|
||||
m_apis.emplace_back(type, my::make_unique<ApiType>(it->m_url));
|
||||
else
|
||||
m_apis.emplace_back(type, my::make_unique<ApiType>());
|
||||
}
|
||||
} // namespace taxi
|
||||
|
|
|
@ -47,10 +47,16 @@ private:
|
|||
ProvidersContainer m_providers;
|
||||
};
|
||||
|
||||
struct ProviderUrl
|
||||
{
|
||||
Provider::Type m_type;
|
||||
std::string m_url;
|
||||
};
|
||||
|
||||
class Engine final
|
||||
{
|
||||
public:
|
||||
Engine();
|
||||
explicit Engine(std::vector<ProviderUrl> urls = {});
|
||||
|
||||
/// Requests list of available products. Returns request identificator immediately.
|
||||
uint64_t GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to,
|
||||
|
@ -66,6 +72,9 @@ private:
|
|||
bool IsAllCountriesDisabled(Provider::Type type, storage::TCountriesVec const & countryIds) const;
|
||||
bool IsAnyCountryEnabled(Provider::Type type, storage::TCountriesVec const & countryIds) const;
|
||||
|
||||
template <typename ApiType>
|
||||
void AddApi(std::vector<ProviderUrl> const & urls, Provider::Type type);
|
||||
|
||||
using ApiPtr = std::unique_ptr<ApiBase>;
|
||||
|
||||
struct ApiContainerItem
|
||||
|
|
|
@ -18,9 +18,6 @@ using namespace platform;
|
|||
|
||||
namespace
|
||||
{
|
||||
string const kUberEstimatesUrl = "https://api.uber.com/v1/estimates";
|
||||
string g_uberUrlForTesting = "";
|
||||
|
||||
bool RunSimpleHttpRequest(string const & url, string & result)
|
||||
{
|
||||
HttpClient request(url);
|
||||
|
@ -115,14 +112,6 @@ void MakeFromJson(char const * times, char const * prices, vector<taxi::Product>
|
|||
products.clear();
|
||||
}
|
||||
}
|
||||
|
||||
string GetUberURL()
|
||||
{
|
||||
if (!g_uberUrlForTesting.empty())
|
||||
return g_uberUrlForTesting;
|
||||
|
||||
return kUberEstimatesUrl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace taxi
|
||||
|
@ -130,33 +119,33 @@ namespace taxi
|
|||
namespace uber
|
||||
{
|
||||
// static
|
||||
bool RawApi::GetProducts(ms::LatLon const & pos, string & result)
|
||||
bool RawApi::GetProducts(ms::LatLon const & pos, string & result,
|
||||
std::string const & baseUrl /* = kProductsUrl */)
|
||||
{
|
||||
stringstream url;
|
||||
url << fixed << setprecision(6)
|
||||
<< "https://api.uber.com/v1/products?server_token=" << UBER_SERVER_TOKEN
|
||||
ostringstream url;
|
||||
url << fixed << setprecision(6) << baseUrl << "?server_token=" << UBER_SERVER_TOKEN
|
||||
<< "&latitude=" << pos.lat << "&longitude=" << pos.lon;
|
||||
|
||||
return RunSimpleHttpRequest(url.str(), result);
|
||||
}
|
||||
|
||||
// static
|
||||
bool RawApi::GetEstimatedTime(ms::LatLon const & pos, string & result)
|
||||
bool RawApi::GetEstimatedTime(ms::LatLon const & pos, string & result,
|
||||
std::string const & baseUrl /* = kEstimatesUrl */)
|
||||
{
|
||||
stringstream url;
|
||||
url << fixed << setprecision(6)
|
||||
<< GetUberURL() << "/time?server_token=" << UBER_SERVER_TOKEN
|
||||
ostringstream url;
|
||||
url << fixed << setprecision(6) << baseUrl << "/time?server_token=" << UBER_SERVER_TOKEN
|
||||
<< "&start_latitude=" << pos.lat << "&start_longitude=" << pos.lon;
|
||||
|
||||
return RunSimpleHttpRequest(url.str(), result);
|
||||
}
|
||||
|
||||
// static
|
||||
bool RawApi::GetEstimatedPrice(ms::LatLon const & from, ms::LatLon const & to, string & result)
|
||||
bool RawApi::GetEstimatedPrice(ms::LatLon const & from, ms::LatLon const & to, string & result,
|
||||
std::string const & baseUrl /* = kEstimatesUrl */)
|
||||
{
|
||||
stringstream url;
|
||||
url << fixed << setprecision(6)
|
||||
<< GetUberURL() << "/price?server_token=" << UBER_SERVER_TOKEN
|
||||
ostringstream url;
|
||||
url << fixed << setprecision(6) << baseUrl << "/price?server_token=" << UBER_SERVER_TOKEN
|
||||
<< "&start_latitude=" << from.lat << "&start_longitude=" << from.lon
|
||||
<< "&end_latitude=" << to.lat << "&end_longitude=" << to.lon;
|
||||
|
||||
|
@ -226,13 +215,14 @@ void Api::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to,
|
|||
|
||||
auto const reqId = ++m_requestId;
|
||||
auto const maker = m_maker;
|
||||
auto const baseUrl = m_baseUrl;
|
||||
|
||||
maker->Reset(reqId);
|
||||
|
||||
threads::SimpleThread([maker, from, reqId, successFn, errorFn]()
|
||||
threads::SimpleThread([maker, from, reqId, baseUrl, successFn, errorFn]()
|
||||
{
|
||||
string result;
|
||||
if (!RawApi::GetEstimatedTime(from, result))
|
||||
if (!RawApi::GetEstimatedTime(from, result, baseUrl))
|
||||
{
|
||||
errorFn(ErrorCode::RemoteError);
|
||||
return;
|
||||
|
@ -242,10 +232,10 @@ void Api::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to,
|
|||
maker->MakeProducts(reqId, successFn, errorFn);
|
||||
}).detach();
|
||||
|
||||
threads::SimpleThread([maker, from, to, reqId, successFn, errorFn]()
|
||||
threads::SimpleThread([maker, from, to, reqId, baseUrl, successFn, errorFn]()
|
||||
{
|
||||
string result;
|
||||
if (!RawApi::GetEstimatedPrice(from, to, result))
|
||||
if (!RawApi::GetEstimatedPrice(from, to, result, baseUrl))
|
||||
{
|
||||
errorFn(ErrorCode::RemoteError);
|
||||
return;
|
||||
|
@ -267,10 +257,5 @@ RideRequestLinks Api::GetRideRequestLinks(string const & productId, ms::LatLon c
|
|||
|
||||
return {"uber://" + url.str(), "https://m.uber.com/ul" + url.str()};
|
||||
}
|
||||
|
||||
void SetUberUrlForTesting(string const & url)
|
||||
{
|
||||
g_uberUrlForTesting = url;
|
||||
}
|
||||
} // namespace uber
|
||||
} // namespace taxi
|
||||
|
|
|
@ -23,6 +23,8 @@ namespace taxi
|
|||
{
|
||||
namespace uber
|
||||
{
|
||||
string const kEstimatesUrl = "https://api.uber.com/v1/estimates";
|
||||
string const kProductsUrl = "https://api.uber.com/v1/products";
|
||||
/// Uber api wrapper based on synchronous http requests.
|
||||
class RawApi
|
||||
{
|
||||
|
@ -31,19 +33,22 @@ public:
|
|||
/// otherwise returns false. The response contains the display name and other details about each
|
||||
/// product, and lists the products in the proper display order. This endpoint does not reflect
|
||||
/// real-time availability of the products.
|
||||
static bool GetProducts(ms::LatLon const & pos, string & result);
|
||||
static bool GetProducts(ms::LatLon const & pos, string & result,
|
||||
std::string const & url = kProductsUrl);
|
||||
/// Returns true when http request was executed successfully and response copied into @result,
|
||||
/// otherwise returns false. The response contains ETAs for all products currently available
|
||||
/// at a given location, with the ETA for each product expressed as integers in seconds. If a
|
||||
/// product returned from GetProducts is not returned from this endpoint for a given
|
||||
/// latitude/longitude pair then there are currently none of that product available to request.
|
||||
/// Call this endpoint every minute to provide the most accurate, up-to-date ETAs.
|
||||
static bool GetEstimatedTime(ms::LatLon const & pos, string & result);
|
||||
static bool GetEstimatedTime(ms::LatLon const & pos, string & result,
|
||||
std::string const & url = kEstimatesUrl);
|
||||
/// Returns true when http request was executed successfully and response copied into @result,
|
||||
/// otherwise returns false. The response contains an estimated price range for each product
|
||||
/// offered at a given location. The price estimate is provided as a formatted string with the
|
||||
/// full price range and the localized currency symbol.
|
||||
static bool GetEstimatedPrice(ms::LatLon const & from, ms::LatLon const & to, string & result);
|
||||
static bool GetEstimatedPrice(ms::LatLon const & from, ms::LatLon const & to, string & result,
|
||||
std::string const & url = kEstimatesUrl);
|
||||
};
|
||||
|
||||
/// Class which used for making products from http requests results.
|
||||
|
@ -66,6 +71,7 @@ private:
|
|||
class Api : public ApiBase
|
||||
{
|
||||
public:
|
||||
explicit Api(std::string const & baseUrl = kEstimatesUrl) : m_baseUrl(baseUrl) {}
|
||||
// ApiBase overrides:
|
||||
/// Requests list of available products from Uber.
|
||||
void GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to,
|
||||
|
@ -79,6 +85,7 @@ public:
|
|||
private:
|
||||
shared_ptr<ProductMaker> m_maker = make_shared<ProductMaker>();
|
||||
uint64_t m_requestId = 0;
|
||||
string const m_baseUrl;
|
||||
};
|
||||
|
||||
void SetUberUrlForTesting(string const & url);
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
std::string const kTaxiInfoUrl = "https://taxi-routeinfo.taxi.yandex.net";
|
||||
std::string g_yandexUrlForTesting = "";
|
||||
|
||||
bool RunSimpleHttpRequest(std::string const & url, std::string & result)
|
||||
{
|
||||
platform::HttpClient request(url);
|
||||
|
@ -46,27 +43,19 @@ bool CheckYandexResponse(json_t const * answer)
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::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)
|
||||
bool RawApi::GetTaxiInfo(ms::LatLon const & from, ms::LatLon const & to, std::string & result,
|
||||
std::string const & baseUrl /* = kTaxiInfoUrl */)
|
||||
{
|
||||
std::ostringstream 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";
|
||||
url << std::fixed << std::setprecision(6) << baseUrl << "/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);
|
||||
}
|
||||
|
@ -79,10 +68,12 @@ void Api::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to,
|
|||
ASSERT(successFn, ());
|
||||
ASSERT(errorFn, ());
|
||||
|
||||
threads::SimpleThread([from, to, successFn, errorFn]()
|
||||
auto const baseUrl = m_baseUrl;
|
||||
|
||||
threads::SimpleThread([from, to, baseUrl, successFn, errorFn]()
|
||||
{
|
||||
std::string result;
|
||||
if (!RawApi::GetTaxiInfo(from, to, result))
|
||||
if (!RawApi::GetTaxiInfo(from, to, result, baseUrl))
|
||||
{
|
||||
errorFn(ErrorCode::RemoteError);
|
||||
return;
|
||||
|
@ -153,10 +144,5 @@ void MakeFromJson(std::string const & src, std::vector<taxi::Product> & products
|
|||
products.push_back(move(product));
|
||||
}
|
||||
}
|
||||
|
||||
void SetYandexUrlForTesting(std::string const & url)
|
||||
{
|
||||
g_yandexUrlForTesting = url;
|
||||
}
|
||||
} // namespace yandex
|
||||
} // namespace taxi
|
||||
|
|
|
@ -13,17 +13,20 @@ namespace taxi
|
|||
{
|
||||
namespace yandex
|
||||
{
|
||||
std::string const kTaxiInfoUrl = "https://taxi-routeinfo.taxi.yandex.net";
|
||||
/// Yandex api wrapper based on synchronous http requests.
|
||||
class RawApi
|
||||
{
|
||||
public:
|
||||
static bool GetTaxiInfo(ms::LatLon const & from, ms::LatLon const & to, std::string & result);
|
||||
static bool GetTaxiInfo(ms::LatLon const & from, ms::LatLon const & to, std::string & result,
|
||||
std::string const & url = kTaxiInfoUrl);
|
||||
};
|
||||
|
||||
/// Class which used for making products from http requests results.
|
||||
class Api : public ApiBase
|
||||
{
|
||||
public:
|
||||
explicit Api(std::string const & baseUrl = kTaxiInfoUrl) : m_baseUrl(baseUrl) {}
|
||||
// ApiBase overrides:
|
||||
/// Requests list of available products from Yandex.
|
||||
void GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to,
|
||||
|
@ -33,9 +36,11 @@ public:
|
|||
/// 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;
|
||||
|
||||
private:
|
||||
std::string const m_baseUrl;
|
||||
};
|
||||
|
||||
void MakeFromJson(std::string const & src, std::vector<taxi::Product> & products);
|
||||
void SetYandexUrlForTesting(std::string const & url);
|
||||
} // namespace yandex
|
||||
} // namespace taxi
|
||||
|
|
Loading…
Add table
Reference in a new issue