Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArsentiy Milchakov <milcars@mapswithme.com>2016-10-06 20:54:45 +0300
committerArsentiy Milchakov <milcars@mapswithme.com>2016-10-10 18:14:45 +0300
commit5a90cb83a64a4faf34a9eeed9b3295ed6abd8b89 (patch)
tree18bc43837f2f40f8d68178827f37e96aad85803a /partners_api
parentf284a8a1392936cb0adbc795896a55c14989f94f (diff)
Uber api with network requests and tests
Diffstat (limited to 'partners_api')
-rw-r--r--partners_api/partners_api_tests/partners_api_tests.pro10
-rw-r--r--partners_api/partners_api_tests/uber_tests.cpp122
-rw-r--r--partners_api/uber_api.cpp208
-rw-r--r--partners_api/uber_api.hpp14
4 files changed, 210 insertions, 144 deletions
diff --git a/partners_api/partners_api_tests/partners_api_tests.pro b/partners_api/partners_api_tests/partners_api_tests.pro
index b5ea1148b1..45c70aa138 100644
--- a/partners_api/partners_api_tests/partners_api_tests.pro
+++ b/partners_api/partners_api_tests/partners_api_tests.pro
@@ -4,12 +4,22 @@ CONFIG -= app_bundle
TEMPLATE = app
ROOT_DIR = ../..
+
+INCLUDEPATH *= $$ROOT_DIR/3party/jansson/src
+
DEPENDENCIES = partners_api platform coding base tomcrypt jansson stats_client
include($$ROOT_DIR/common.pri)
+DEFINES *= OMIM_UNIT_TEST_WITH_QT_EVENT_LOOP
+
QT *= core
+macx-* {
+ QT *= widgets # needed for QApplication with event loop, to test async events
+ LIBS *= "-framework IOKit" "-framework SystemConfiguration"
+}
+
SOURCES += \
$$ROOT_DIR/testing/testingmain.cpp \
booking_tests.cpp \
diff --git a/partners_api/partners_api_tests/uber_tests.cpp b/partners_api/partners_api_tests/uber_tests.cpp
index 6fc05fb7f7..ea2e9cbdea 100644
--- a/partners_api/partners_api_tests/uber_tests.cpp
+++ b/partners_api/partners_api_tests/uber_tests.cpp
@@ -6,24 +6,122 @@
#include "base/logging.hpp"
-UNIT_TEST(Uber_SmokeTest)
+#include "3party/jansson/myjansson.hpp"
+
+UNIT_TEST(Uber_GetProducts)
+{
+ ms::LatLon pos(38.897724, -77.036531);
+
+ TEST(!uber::RawApi::GetProducts(pos).empty(), ());
+}
+
+UNIT_TEST(Uber_GetTimes)
{
- ms::LatLon from(59.856464, 30.371867);
- ms::LatLon to(59.856000, 30.371000);
+ ms::LatLon pos(38.897724, -77.036531);
+
+ my::Json timeRoot(uber::RawApi::GetEstimatedTime(pos).c_str());
+ auto const timesArray = json_object_get(timeRoot.get(), "times");
+
+ TEST(json_is_array(timesArray), ());
+ TEST(json_array_size(timesArray) > 0, ());
+ auto const timeSize = json_array_size(timesArray);
+ for (size_t i = 0; i < timeSize; ++i)
{
- uber::Api uberApi;
- auto reqId = 0;
- reqId = uberApi.GetAvailableProducts(from, to, [&reqId](vector<uber::Product> const & products, size_t const requestId)
+ string name;
+ json_int_t estimatedTime = 0;
+ auto const item = json_array_get(timesArray, i);
+
+ try
+ {
+ my::FromJSONObject(item, "display_name", name);
+ my::FromJSONObject(item, "estimate", estimatedTime);
+ }
+ catch (my::Json::Exception const & e)
{
- TEST(!products.empty(), ());
- TEST_EQUAL(requestId, reqId, ());
+ LOG(LERROR, (e.Msg()));
+ }
- for(auto const & product : products)
+ string estimated = strings::to_string(estimatedTime);
+
+ TEST(!name.empty(), ());
+ TEST(!estimated.empty(), ());
+ }
+}
+
+UNIT_TEST(Uber_GetPrices)
+{
+ ms::LatLon from(38.897724, -77.036531);
+ ms::LatLon to(38.862416, -76.883316);
+
+ my::Json priceRoot(uber::RawApi::GetEstimatedPrice(from, to).c_str());
+ auto const pricesArray = json_object_get(priceRoot.get(), "prices");
+
+ TEST(json_is_array(pricesArray), ());
+ TEST(json_array_size(pricesArray) > 0, ());
+
+ auto const pricesSize = json_array_size(pricesArray);
+ for (size_t i = 0; i < pricesSize; ++i)
+ {
+ string productId;
+ string price;
+ string currency;
+
+ auto const item = json_array_get(pricesArray, i);
+
+ try
+ {
+ my::FromJSONObject(item, "product_id", productId);
+ my::FromJSONObject(item, "estimate", price);
+
+ auto const val = json_object_get(item, "currency_code");
+ if (val != nullptr)
{
- LOG(LINFO, (product.m_productId, product.m_name, product.m_time, product.m_price));
- TEST(!product.m_productId.empty() && !product.m_name.empty() && !product.m_time.empty() && !product.m_price.empty(),());
+ if (!json_is_null(val))
+ currency = json_string_value(val);
+ else
+ currency = "null";
}
- });
+ }
+ catch (my::Json::Exception const & e)
+ {
+ LOG(LERROR, (e.Msg()));
+ }
+
+ TEST(!productId.empty(), ());
+ TEST(!price.empty(), ());
+ TEST(!currency.empty(), ());
+ }
+}
+
+UNIT_TEST(Uber_SmokeTest)
+{
+ ms::LatLon from(38.897724, -77.036531);
+ ms::LatLon to(38.862416, -76.883316);
+
+ uber::Api uberApi;
+ size_t reqId = 0;
+ size_t returnedId = 0;
+ vector<uber::Product> returnedProducts;
+ reqId = uberApi.GetAvailableProducts(
+ from, to, [&returnedId, &returnedProducts](vector<uber::Product> const & products, size_t const requestId)
+ {
+ returnedId = requestId;
+ returnedProducts = products;
+
+ testing::StopEventLoop();
+ });
+
+ testing::RunEventLoop();
+
+ TEST(!returnedProducts.empty(), ());
+ TEST_EQUAL(returnedId, reqId, ());
+
+ for (auto const & product : returnedProducts)
+ {
+ TEST(!product.m_productId.empty() &&
+ !product.m_name.empty() &&
+ !product.m_time.empty() &&
+ !product.m_price.empty(),());
}
}
diff --git a/partners_api/uber_api.cpp b/partners_api/uber_api.cpp
index 4f5f9cb2fa..edb8908e3c 100644
--- a/partners_api/uber_api.cpp
+++ b/partners_api/uber_api.cpp
@@ -7,31 +7,42 @@
#include "base/assert.hpp"
#include "base/logging.hpp"
+#include "std/chrono.hpp"
#include "std/future.hpp"
#include "3party/jansson/myjansson.hpp"
#include "private.h"
-#define UBER_SERVER_TOKEN ""
-#define UBER_CLIENT_ID ""
-
using namespace platform;
namespace
{
+uint32_t const kHttpMinWait = 10;
+
string RunSimpleHttpRequest(string const & url)
{
HttpClient request(url);
-
if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200)
{
return request.ServerResponse();
}
-
return {};
}
+/// Feature should refers to a shared state.
+template <typename T>
+bool WaitForFeature(future<T> const & f, uint32_t waitMillisec, atomic<bool> const & runFlag)
+{
+ future_status status = future_status::deferred;
+ while (runFlag && status != future_status::ready)
+ {
+ status = f.wait_for(milliseconds(waitMillisec));
+ }
+
+ return runFlag;
+}
+
bool CheckUberAnswer(json_t const * answer)
{
// Uber products are not available at this point.
@@ -46,6 +57,7 @@ bool CheckUberAnswer(json_t const * answer)
void FillProducts(json_t const * time, json_t const * price, vector<uber::Product> & products)
{
+ // Fill data from time.
auto const timeSize = json_array_size(time);
for (size_t i = 0; i < timeSize; ++i)
{
@@ -58,6 +70,7 @@ void FillProducts(json_t const * time, json_t const * price, vector<uber::Produc
products.push_back(product);
}
+ // Fill data from price.
auto const priceSize = json_array_size(price);
for (size_t i = 0; i < priceSize; ++i)
{
@@ -73,6 +86,11 @@ void FillProducts(json_t const * time, json_t const * price, vector<uber::Produc
my::FromJSONObject(item, "product_id", it->m_productId);
my::FromJSONObject(item, "estimate", it->m_price);
+
+ // The field currency_code can contain null in case when price equal to Metered.
+ auto const currency = json_object_get(item, "currency_code");
+ if (currency != nullptr && !json_is_null(currency))
+ it->m_currency = json_string_value(currency);
}
products.erase(remove_if(products.begin(), products.end(), [](uber::Product const & p){
@@ -81,18 +99,33 @@ void FillProducts(json_t const * time, json_t const * price, vector<uber::Produc
}), products.end());
}
-void GetAvailableProductsAsync(ms::LatLon const & from, ms::LatLon const & to, size_t const requestId,
- uber::ProductsCallback const & fn)
+void GetAvailableProductsAsync(ms::LatLon const from, ms::LatLon const to,
+ size_t const requestId, atomic<bool> const & runFlag,
+ uber::ProductsCallback const fn)
{
auto time = async(launch::async, uber::RawApi::GetEstimatedTime, ref(from));
auto price = async(launch::async, uber::RawApi::GetEstimatedPrice, ref(from), ref(to));
vector<uber::Product> products;
+ if (!WaitForFeature(time, kHttpMinWait, runFlag) || !WaitForFeature(price, kHttpMinWait, runFlag))
+ {
+ return;
+ }
+
try
{
- my::Json timeRoot(time.get().c_str());
- my::Json priceRoot(price.get().c_str());
+ string timeStr = time.get();
+ string priceStr = price.get();
+
+ if (timeStr.empty() || priceStr.empty())
+ {
+ LOG(LWARNING, ("Time or price is empty, time:", timeStr, "; price:", priceStr));
+ return;
+ }
+
+ my::Json timeRoot(timeStr.c_str());
+ my::Json priceRoot(priceStr.c_str());
auto const timesArray = json_object_get(timeRoot.get(), "times");
auto const pricesArray = json_object_get(priceRoot.get(), "prices");
if (CheckUberAnswer(timesArray) && CheckUberAnswer(pricesArray))
@@ -116,9 +149,8 @@ namespace uber
string RawApi::GetProducts(ms::LatLon const & pos)
{
stringstream url;
- url << "https://api.uber.com/v1/products?server_token=" << UBER_SERVER_TOKEN <<
- "&latitude=" << static_cast<float>(pos.lat) <<
- "&longitude=" << static_cast<float>(pos.lon);
+ url << "https://api.uber.com/v1/products?server_token=" << UBER_SERVER_TOKEN
+ << "&latitude=" << pos.lat << "&longitude=" << pos.lon;
return RunSimpleHttpRequest(url.str());
}
@@ -126,141 +158,63 @@ string RawApi::GetProducts(ms::LatLon const & pos)
// static
string RawApi::GetEstimatedTime(ms::LatLon const & pos)
{
-// stringstream url;
-// url << "https://api.uber.com/v1/products?server_token=" << UBER_SERVER_TOKEN <<
-// "&start_latitude=" << static_cast<float>(pos.lat) <<
-// "&start_longitude=" << static_cast<float>(pos.lon);
-
-// return RunSimpleHttpRequest(url.str());
- return R"({
- "times":[
- {
- "localized_display_name":"uberPOOL",
- "estimate":180,
- "display_name":"uberPOOL",
- "product_id":"26546650-e557-4a7b-86e7-6a3942445247"
- },
- {
- "localized_display_name":"uberX",
- "estimate":180,
- "display_name":"uberX",
- "product_id":"a1111c8c-c720-46c3-8534-2fcdd730040d"
- },
- {
- "localized_display_name":"uberXL",
- "estimate":420,
- "display_name":"uberXL",
- "product_id":"821415d8-3bd5-4e27-9604-194e4359a449"
- },
- {
- "localized_display_name":"UberBLACK",
- "estimate":180,
- "display_name":"UberBLACK",
- "product_id":"d4abaae7-f4d6-4152-91cc-77523e8165a4"
- }
- ]
- })";
+ stringstream url;
+ url << "https://api.uber.com/v1/estimates/time?server_token=" << UBER_SERVER_TOKEN
+ << "&start_latitude=" << pos.lat << "&start_longitude=" << pos.lon;
+
+ return RunSimpleHttpRequest(url.str());
}
// static
string RawApi::GetEstimatedPrice(ms::LatLon const & from, ms::LatLon const & to)
{
-// stringstream url;
-// url << "https://api.uber.com/v1/products?server_token=" << UBER_SERVER_TOKEN <<
-// "&start_latitude=" << static_cast<float>(from.lat) <<
-// "&start_longitude=" << static_cast<float>(from.lon) <<
-// "&end_latitude=" << static_cast<float>(to.lat) <<
-// "&end_longitude=" << static_cast<float>(to.lon);
-
-// return RunSimpleHttpRequest(url.str());
- return R"({
- "prices":[
- {
- "product_id": "26546650-e557-4a7b-86e7-6a3942445247",
- "currency_code": "USD",
- "display_name": "POOL",
- "estimate": "$7",
- "low_estimate": 7,
- "high_estimate": 7,
- "surge_multiplier": 1,
- "duration": 640,
- "distance": 5.34
- },
- {
- "product_id": "08f17084-23fd-4103-aa3e-9b660223934b",
- "currency_code": "USD",
- "display_name": "UberBLACK",
- "estimate": "$23-29",
- "low_estimate": 23,
- "high_estimate": 29,
- "surge_multiplier": 1,
- "duration": 640,
- "distance": 5.34
- },
- {
- "product_id": "9af0174c-8939-4ef6-8e91-1a43a0e7c6f6",
- "currency_code": "USD",
- "display_name": "UberSUV",
- "estimate": "$36-44",
- "low_estimate": 36,
- "high_estimate": 44,
- "surge_multiplier": 1.25,
- "duration": 640,
- "distance": 5.34
- },
- {
- "product_id": "aca52cea-9701-4903-9f34-9a2395253acb",
- "currency_code": null,
- "display_name": "uberTAXI",
- "estimate": "Metered",
- "low_estimate": null,
- "high_estimate": null,
- "surge_multiplier": 1,
- "duration": 640,
- "distance": 5.34
- },
- {
- "product_id": "a27a867a-35f4-4253-8d04-61ae80a40df5",
- "currency_code": "USD",
- "display_name": "uberX",
- "estimate": "$15",
- "low_estimate": 15,
- "high_estimate": 15,
- "surge_multiplier": 1,
- "duration": 640,
- "distance": 5.34
- }
- ]
- })";
+ stringstream url;
+ url << "https://api.uber.com/v1/estimates/price?server_token=" << UBER_SERVER_TOKEN
+ << "&start_latitude=" << from.lat << "&start_longitude=" << from.lon
+ << "&end_latitude=" << to.lat << "&end_longitude=" << to.lon;
+
+ return RunSimpleHttpRequest(url.str());
+}
+
+Api::~Api()
+{
+ ResetThread();
}
size_t Api::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to,
ProductsCallback const & fn)
{
- lock_guard<mutex> lock(m_mutex);
-
- m_thread.reset();
static size_t requestId = 0;
- ++requestId;
- m_thread = unique_ptr<threads::SimpleThread, threadDeleter>(
- new threads::SimpleThread(GetAvailableProductsAsync, ref(from), ref(to), requestId, ref(fn)),
- [](threads::SimpleThread * ptr) { ptr->join(); delete ptr; });
+ ResetThread();
+ m_runFlag = true;
+ m_thread = make_unique<threads::SimpleThread>(GetAvailableProductsAsync, from, to,
+ ++requestId, ref(m_runFlag), fn);
return requestId;
}
// static
-string Api::GetRideRequestLink(string const & m_productId, ms::LatLon const & from,
+string Api::GetRideRequestLink(string const & productId, ms::LatLon const & from,
ms::LatLon const & to)
{
stringstream url;
- url << "uber://?client_id=" << UBER_CLIENT_ID <<
- "&action=setPickup&product_id=" << m_productId <<
- "&pickup[latitude]=" << static_cast<float>(from.lat) <<
- "&pickup[longitude]=" << static_cast<float>(from.lon) <<
- "&dropoff[latitude]=" << static_cast<float>(to.lat)<<
- "&dropoff[longitude]=" << static_cast<float>(to.lon);
+ url << "uber://?client_id=" << UBER_CLIENT_ID << "&action=setPickup&product_id=" << productId
+ << "&pickup[latitude]=" << static_cast<float>(from.lat)
+ << "&pickup[longitude]=" << static_cast<float>(from.lon)
+ << "&dropoff[latitude]=" << static_cast<float>(to.lat)
+ << "&dropoff[longitude]=" << static_cast<float>(to.lon);
return url.str();
}
+
+void Api::ResetThread()
+{
+ m_runFlag = false;
+
+ if (m_thread)
+ {
+ m_thread->join();
+ m_thread.reset();
+ }
+}
} // namespace uber
diff --git a/partners_api/uber_api.hpp b/partners_api/uber_api.hpp
index 4d9a19a0ab..d16144caa1 100644
--- a/partners_api/uber_api.hpp
+++ b/partners_api/uber_api.hpp
@@ -2,6 +2,7 @@
#include "base/thread.hpp"
+#include "std/atomic.hpp"
#include "std/function.hpp"
#include "std/mutex.hpp"
#include "std/string.hpp"
@@ -46,7 +47,8 @@ struct Product
string m_productId;
string m_name;
string m_time;
- string m_price;
+ 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.
/// @requestId - identificator which was provided to GetAvailableProducts to identify request.
@@ -55,18 +57,20 @@ using ProductsCallback = function<void(vector<Product> const & products, size_t
class Api
{
public:
+ ~Api();
/// Requests list of available products from Uber. Returns request identificator immediately.
size_t GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to,
ProductsCallback const & fn);
/// Returns link which allows you to launch the Uber app.
- static string GetRideRequestLink(string const & m_productId, ms::LatLon const & from, ms::LatLon const & to);
+ static string GetRideRequestLink(string const & productId, ms::LatLon const & from,
+ ms::LatLon const & to);
private:
- using threadDeleter = function<void(threads::SimpleThread *)>;
- unique_ptr<threads::SimpleThread, threadDeleter> m_thread;
+ void ResetThread();
+ unique_ptr<threads::SimpleThread> m_thread;
- mutex m_mutex;
+ atomic<bool> m_runFlag;
};
} // namespace uber