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-03 15:22:46 +0300
committerArsentiy Milchakov <milcars@mapswithme.com>2016-10-10 18:14:45 +0300
commit5e716d70d81926305c53e88f68afba31ecde9224 (patch)
tree711b1d1cf976012352e001ffa5d5e4d31928a408 /partners_api
parentecc59be84ff4b13c768f41422964d69a90c3fa2c (diff)
dummy implementation and smoke test
Diffstat (limited to 'partners_api')
-rw-r--r--partners_api/booking_api.cpp271
-rw-r--r--partners_api/booking_api.hpp136
-rw-r--r--partners_api/partners_api.pro18
-rw-r--r--partners_api/partners_api_tests/booking_tests.cpp81
-rw-r--r--partners_api/partners_api_tests/partners_api_tests.pro16
-rw-r--r--partners_api/partners_api_tests/uber_tests.cpp29
-rw-r--r--partners_api/uber_api.cpp266
-rw-r--r--partners_api/uber_api.hpp74
8 files changed, 891 insertions, 0 deletions
diff --git a/partners_api/booking_api.cpp b/partners_api/booking_api.cpp
new file mode 100644
index 0000000000..b634411716
--- /dev/null
+++ b/partners_api/booking_api.cpp
@@ -0,0 +1,271 @@
+#include "partners_api/booking_api.hpp"
+
+#include "base/gmtime.hpp"
+#include "base/logging.hpp"
+
+#include "std/chrono.hpp"
+#include "std/iostream.hpp"
+#include "std/sstream.hpp"
+
+#include "3party/jansson/myjansson.hpp"
+
+#include "private.h"
+
+char const BookingApi::kDefaultCurrency[1];
+
+BookingApi::BookingApi() : m_affiliateId(BOOKING_AFFILIATE_ID)
+{
+ stringstream ss;
+ ss << BOOKING_KEY << ":" << BOOKING_SECRET;
+ m_apiUrl = "https://" + ss.str() + "@distribution-xml.booking.com/json/bookings.";
+}
+
+string BookingApi::GetBookingUrl(string const & baseUrl, string const & /* lang */) const
+{
+ return GetDescriptionUrl(baseUrl) + "#availability";
+}
+
+string BookingApi::GetDescriptionUrl(string const & baseUrl, string const & /* lang */) const
+{
+ return baseUrl + "?aid=" + m_affiliateId;
+}
+
+void BookingApi::GetMinPrice(string const & hotelId, string const & currency,
+ function<void(string const &, string const &)> const & fn)
+{
+ char dateArrival[12]{};
+ char dateDeparture[12]{};
+
+ system_clock::time_point p = system_clock::from_time_t(time(nullptr));
+ tm arrival = my::GmTime(system_clock::to_time_t(p));
+ tm departure = my::GmTime(system_clock::to_time_t(p + hours(24)));
+ strftime(dateArrival, sizeof(dateArrival), "%Y-%m-%d", &arrival);
+ strftime(dateDeparture, sizeof(dateDeparture), "%Y-%m-%d", &departure);
+
+ string url = MakeApiUrl("getHotelAvailability", {{"hotel_ids", hotelId},
+ {"currency_code", currency},
+ {"arrival_date", dateArrival},
+ {"departure_date", dateDeparture}});
+ auto const callback = [this, fn, currency](downloader::HttpRequest & answer)
+ {
+
+ string minPrice;
+ string priceCurrency;
+ try
+ {
+ my::Json root(answer.Data().c_str());
+ if (!json_is_array(root.get()))
+ MYTHROW(my::Json::Exception, ("The answer must contain a json array."));
+ size_t const sz = json_array_size(root.get());
+
+ if (sz > 0)
+ {
+ // Read default hotel price and currency.
+ auto obj = json_array_get(root.get(), 0);
+ my::FromJSONObject(obj, "min_price", minPrice);
+ my::FromJSONObject(obj, "currency_code", priceCurrency);
+
+ // Try to get price in requested currency.
+ if (!currency.empty() && priceCurrency != currency)
+ {
+ json_t * arr = json_object_get(obj, "other_currency");
+ if (arr && json_is_array(arr))
+ {
+ size_t sz = json_array_size(arr);
+ for (size_t i = 0; i < sz; ++i)
+ {
+ auto el = json_array_get(arr, i);
+ string code;
+ my::FromJSONObject(el, "currency_code", code);
+ if (code == currency)
+ {
+ priceCurrency = code;
+ my::FromJSONObject(el, "min_price", minPrice);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (my::Json::Exception const & e)
+ {
+ LOG(LERROR, (e.Msg()));
+ minPrice.clear();
+ priceCurrency.clear();
+ }
+ fn(minPrice, priceCurrency);
+ m_request.reset();
+ };
+
+ m_request.reset(downloader::HttpRequest::Get(url, callback));
+}
+
+// TODO(mgsergio): This is just a mockup, make it a real function.
+void BookingApi::GetHotelInfo(string const & hotelId, string const & /* lang */,
+ function<void(HotelInfo const & hotelInfo)> const & fn)
+{
+ HotelInfo info;
+
+ info.m_hotelId = "000";
+ info.m_description = "Interesting place among SoHo, Little "
+ "Italy and China town. Modern design. "
+ "Great view from roof. Near subway. "
+ "Free refreshment every afternoon. "
+ "The staff was very friendly.";
+
+ info.m_photos.push_back({
+ "http://storage9.static.itmages.ru/i/16/0915/h_1473944906_4427771_63a7c2282b.jpg",
+ "http://storage7.static.itmages.ru/i/16/0915/h_1473945189_5545647_db54564f06.jpg"});
+
+ info.m_photos.push_back({
+ "http://storage9.static.itmages.ru/i/16/0915/h_1473944906_1573275_450fcd78b0.jpg",
+ "http://storage8.static.itmages.ru/i/16/0915/h_1473945194_6402871_b68c63c705.jpg"});
+
+ info.m_photos.push_back({
+ "http://storage1.static.itmages.ru/i/16/0915/h_1473944906_6998375_f1ba6024a5.jpg",
+ "http://storage7.static.itmages.ru/i/16/0915/h_1473945188_9401486_7185c713bc.jpg"});
+
+ info.m_photos.push_back({
+ "http://storage7.static.itmages.ru/i/16/0915/h_1473944904_8294064_035b4328ee.jpg",
+ "http://storage9.static.itmages.ru/i/16/0915/h_1473945189_8999398_d9bfe0d56d.jpg"});
+
+ info.m_photos.push_back({
+ "http://storage6.static.itmages.ru/i/16/0915/h_1473944904_2231876_680171f67f.jpg",
+ "http://storage1.static.itmages.ru/i/16/0915/h_1473945190_2042562_c6cfcccd18.jpg"});
+
+ info.m_photos.push_back({
+ "http://storage7.static.itmages.ru/i/16/0915/h_1473944904_2871576_660e0aad58.jpg",
+ "http://storage1.static.itmages.ru/i/16/0915/h_1473945190_9605355_94164142b7.jpg"});
+
+ info.m_photos.push_back({
+ "http://storage8.static.itmages.ru/i/16/0915/h_1473944905_3578559_d4e95070e9.jpg",
+ "http://storage3.static.itmages.ru/i/16/0915/h_1473945190_3367031_145793d530.jpg"});
+
+ info.m_photos.push_back({
+ "http://storage8.static.itmages.ru/i/16/0915/h_1473944905_5596402_9bdce96ace.jpg",
+ "http://storage4.static.itmages.ru/i/16/0915/h_1473945191_2783367_2440027ece.jpg"});
+
+ info.m_photos.push_back({
+ "http://storage8.static.itmages.ru/i/16/0915/h_1473944905_4312757_433c687f4d.jpg",
+ "http://storage6.static.itmages.ru/i/16/0915/h_1473945191_1817571_b945aa1f3e.jpg"});
+
+ info.m_facilities = {
+ {"non_smoking_rooms", "Non smoking rooms"},
+ {"gym", "Training gym"},
+ {"pets_are_allowed", "Pets are allowed"}
+ };
+
+ info.m_reviews = {
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous1",
+ "http://storage2.static.itmages.ru/i/16/0915/h_1473945375_5332083_b44af613bd.jpg",
+ 9.2,
+ system_clock::now()
+ ),
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous2",
+ "http://storage2.static.itmages.ru/i/16/0915/h_1473945375_7504873_be0fe246e3.jpg",
+ 9.2,
+ system_clock::now()
+ ),
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous2",
+ "http://storage1.static.itmages.ru/i/16/0915/h_1473945374_9397526_996bbca0d7.jpg",
+ 9.2,
+ system_clock::now()
+ ),
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous1",
+ "http://storage2.static.itmages.ru/i/16/0915/h_1473945375_5332083_b44af613bd.jpg",
+ 9.2,
+ system_clock::now()
+ ),
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous2",
+ "http://storage2.static.itmages.ru/i/16/0915/h_1473945375_7504873_be0fe246e3.jpg",
+ 9.2,
+ system_clock::now()
+ ),
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous2",
+ "http://storage1.static.itmages.ru/i/16/0915/h_1473945374_9397526_996bbca0d7.jpg",
+ 9.2,
+ system_clock::now()
+ ),
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous1",
+ "http://storage2.static.itmages.ru/i/16/0915/h_1473945375_5332083_b44af613bd.jpg",
+ 9.2,
+ system_clock::now()
+ ),
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous2",
+ "http://storage2.static.itmages.ru/i/16/0915/h_1473945375_7504873_be0fe246e3.jpg",
+ 9.2,
+ system_clock::now()
+ ),
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous2",
+ "http://storage1.static.itmages.ru/i/16/0915/h_1473945374_9397526_996bbca0d7.jpg",
+ 9.2,
+ system_clock::now()
+ ),
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous1",
+ "http://storage2.static.itmages.ru/i/16/0915/h_1473945375_5332083_b44af613bd.jpg",
+ 9.2,
+ system_clock::now()
+ ),
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous2",
+ "http://storage2.static.itmages.ru/i/16/0915/h_1473945375_7504873_be0fe246e3.jpg",
+ 9.2,
+ system_clock::now()
+ ),
+ HotelReview::CriticReview(
+ "Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly.",
+ "Little bit noise from outside",
+ "Anonymous2",
+ "http://storage1.static.itmages.ru/i/16/0915/h_1473945374_9397526_996bbca0d7.jpg",
+ 9.2,
+ system_clock::now()
+ )
+ };
+
+ fn(info);
+}
+
+string BookingApi::MakeApiUrl(string const & func,
+ initializer_list<pair<string, string>> const & params)
+{
+ stringstream ss;
+ ss << m_apiUrl << func << "?";
+ bool firstRun = true;
+ for (auto const & param : params)
+ ss << (firstRun ? firstRun = false, "" : "&") << param.first << "=" << param.second;
+
+ return ss.str();
+}
diff --git a/partners_api/booking_api.hpp b/partners_api/booking_api.hpp
new file mode 100644
index 0000000000..db9b9c41c4
--- /dev/null
+++ b/partners_api/booking_api.hpp
@@ -0,0 +1,136 @@
+#pragma once
+
+#include "platform/http_request.hpp"
+
+#include "std/chrono.hpp"
+#include "std/function.hpp"
+#include "std/initializer_list.hpp"
+#include "std/limits.hpp"
+#include "std/string.hpp"
+#include "std/unique_ptr.hpp"
+#include "std/utility.hpp"
+
+class BookingApi
+{
+ string m_affiliateId;
+ string m_apiUrl;
+
+public:
+ struct HotelPhotoUrls
+ {
+ string m_small;
+ string m_original;
+ };
+
+ struct HotelReview
+ {
+ HotelReview() = default;
+ // C++11 doesn't allow aggragate initialization for structs with default member initializer.
+ // But C++14 does.
+ HotelReview(string const & reviewPositive,
+ string const & reviewNegative,
+ string const & reviewNeutral,
+ string const & author,
+ string const & authorPictUrl,
+ float const rating,
+ time_point<system_clock> const date)
+ : m_reviewPositive(reviewPositive)
+ , m_reviewNegative(reviewNegative)
+ , m_reviewNeutral(reviewNeutral)
+ , m_author(author)
+ , m_authorPictUrl(authorPictUrl)
+ , m_rating(rating)
+ , m_date(date)
+ {
+ }
+
+
+ static HotelReview CriticReview(string const & reviewPositive,
+ string const & reviewNegative,
+ string const & author,
+ string const & authorPictUrl,
+ float const rating,
+ time_point<system_clock> const date)
+ {
+ return {
+ reviewPositive,
+ reviewNegative,
+ "",
+ author,
+ authorPictUrl,
+ rating,
+ date
+ };
+ }
+
+ static HotelReview NeutralReview(string const & reviewNeutral,
+ string const & author,
+ string const & authorPictUrl,
+ float const rating,
+ time_point<system_clock> const date)
+ {
+ return {
+ "",
+ "",
+ reviewNeutral,
+ author,
+ authorPictUrl,
+ rating,
+ date
+ };
+ }
+
+ static auto constexpr kInvalidRating = numeric_limits<float>::max();
+
+ /// Review text. There can be either one or both positive/negative review or
+ /// a neutral one.
+ string m_reviewPositive;
+ string m_reviewNegative;
+ string m_reviewNeutral;
+ /// Review author name.
+ string m_author;
+ /// Url to a author's picture.
+ string m_authorPictUrl;
+ /// Author's hotel evaluation.
+ float m_rating = kInvalidRating;
+ /// An issue date.
+ time_point<system_clock> m_date;
+ };
+
+ struct Facility
+ {
+ string m_id;
+ string m_localizedName;
+ };
+
+ struct HotelInfo
+ {
+ string m_hotelId;
+
+ string m_description;
+ vector<HotelPhotoUrls> m_photos;
+ vector<Facility> m_facilities;
+ vector<HotelReview> m_reviews;
+ };
+
+ static constexpr const char kDefaultCurrency[1] = {0};
+
+ BookingApi();
+ string GetBookingUrl(string const & baseUrl, string const & lang = string()) const;
+ string GetDescriptionUrl(string const & baseUrl, string const & lang = string()) const;
+
+ // Real-time information methods (used for retriving rapidly changing information).
+ // These methods send requests directly to Booking.
+ void GetMinPrice(string const & hotelId, string const & currency,
+ function<void(string const &, string const &)> const & fn);
+
+
+ // Static information methods (use for information that can be cached).
+ // These methods use caching server to prevent Booking from being ddossed.
+ void GetHotelInfo(string const & hotelId, string const & lang,
+ function<void(HotelInfo const & hotelInfo)> const & fn);
+
+protected:
+ unique_ptr<downloader::HttpRequest> m_request;
+ string MakeApiUrl(string const & func, initializer_list<pair<string, string>> const & params);
+};
diff --git a/partners_api/partners_api.pro b/partners_api/partners_api.pro
new file mode 100644
index 0000000000..966eb00066
--- /dev/null
+++ b/partners_api/partners_api.pro
@@ -0,0 +1,18 @@
+TARGET = partners_api
+TEMPLATE = lib
+CONFIG += staticlib warn_on
+
+ROOT_DIR = ..
+
+INCLUDEPATH *= $$ROOT_DIR/3party/jansson/src
+
+include($$ROOT_DIR/common.pri)
+
+SOURCES += \
+ booking_api.cpp \
+ uber_api.cpp \
+
+HEADERS += \
+ booking_api.hpp \
+ uber_api.hpp \
+
diff --git a/partners_api/partners_api_tests/booking_tests.cpp b/partners_api/partners_api_tests/booking_tests.cpp
new file mode 100644
index 0000000000..1af991dcee
--- /dev/null
+++ b/partners_api/partners_api_tests/booking_tests.cpp
@@ -0,0 +1,81 @@
+#include "testing/testing.hpp"
+
+#include "partners_api/booking_api.hpp"
+
+UNIT_TEST(Booking_SmokeTest)
+{
+ BookingApi api;
+
+ string url = api.GetBookingUrl("http://someurl.com");
+ TEST(!url.empty(), ());
+}
+
+UNIT_TEST(Booking_GetMinPrice)
+{
+ BookingApi api;
+
+ {
+ string price;
+ string currency;
+ api.GetMinPrice("10340", BookingApi::kDefaultCurrency,
+ [&price, &currency](string const & val, string const & curr)
+ {
+ price = val;
+ currency = curr;
+ testing::StopEventLoop();
+ });
+ testing::RunEventLoop();
+
+ TEST(!price.empty(), ());
+ TEST(!currency.empty(), ());
+ TEST_EQUAL(currency, "EUR", ());
+ }
+
+ {
+ string price;
+ string currency;
+ api.GetMinPrice("10340", "RUB", [&price, &currency](string const & val, string const & curr)
+ {
+ price = val;
+ currency = curr;
+ testing::StopEventLoop();
+ });
+ testing::RunEventLoop();
+
+ TEST(!price.empty(), ());
+ TEST(!currency.empty(), ());
+ TEST_EQUAL(currency, "RUB", ());
+ }
+
+ {
+ string price;
+ string currency;
+ api.GetMinPrice("10340", "ISK", [&price, &currency](string const & val, string const & curr)
+ {
+ price = val;
+ currency = curr;
+ testing::StopEventLoop();
+ });
+ testing::RunEventLoop();
+
+ TEST(!price.empty(), ());
+ TEST(!currency.empty(), ());
+ TEST_EQUAL(currency, "ISK", ());
+ }
+}
+
+UNIT_TEST(GetHotelInfo) // GetHotelInfo is a mockup now.
+{
+ BookingApi api;
+ BookingApi::HotelInfo info;
+
+ api.GetHotelInfo("000", "en", [&info](BookingApi::HotelInfo const & i)
+ {
+ info = i;
+ });
+
+ TEST(!info.m_description.empty(), ());
+ TEST_EQUAL(info.m_photos.size(), 9, ());
+ TEST_EQUAL(info.m_facilities.size(), 3, ());
+ TEST_EQUAL(info.m_reviews.size(), 12, ());
+}
diff --git a/partners_api/partners_api_tests/partners_api_tests.pro b/partners_api/partners_api_tests/partners_api_tests.pro
new file mode 100644
index 0000000000..b5ea1148b1
--- /dev/null
+++ b/partners_api/partners_api_tests/partners_api_tests.pro
@@ -0,0 +1,16 @@
+TARGET = partners_api_tests
+CONFIG += console warn_on
+CONFIG -= app_bundle
+TEMPLATE = app
+
+ROOT_DIR = ../..
+DEPENDENCIES = partners_api platform coding base tomcrypt jansson stats_client
+
+include($$ROOT_DIR/common.pri)
+
+QT *= core
+
+SOURCES += \
+ $$ROOT_DIR/testing/testingmain.cpp \
+ booking_tests.cpp \
+ uber_tests.cpp \
diff --git a/partners_api/partners_api_tests/uber_tests.cpp b/partners_api/partners_api_tests/uber_tests.cpp
new file mode 100644
index 0000000000..6fc05fb7f7
--- /dev/null
+++ b/partners_api/partners_api_tests/uber_tests.cpp
@@ -0,0 +1,29 @@
+#include "testing/testing.hpp"
+
+#include "geometry/latlon.hpp"
+
+#include "partners_api/uber_api.hpp"
+
+#include "base/logging.hpp"
+
+UNIT_TEST(Uber_SmokeTest)
+{
+ ms::LatLon from(59.856464, 30.371867);
+ ms::LatLon to(59.856000, 30.371000);
+
+ {
+ uber::Api uberApi;
+ auto reqId = 0;
+ reqId = uberApi.GetAvailableProducts(from, to, [&reqId](vector<uber::Product> const & products, size_t const requestId)
+ {
+ TEST(!products.empty(), ());
+ TEST_EQUAL(requestId, reqId, ());
+
+ for(auto const & product : products)
+ {
+ 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(),());
+ }
+ });
+ }
+}
diff --git a/partners_api/uber_api.cpp b/partners_api/uber_api.cpp
new file mode 100644
index 0000000000..4f5f9cb2fa
--- /dev/null
+++ b/partners_api/uber_api.cpp
@@ -0,0 +1,266 @@
+#include "uber_api.hpp"
+
+#include "platform/http_client.hpp"
+
+#include "geometry/latlon.hpp"
+
+#include "base/assert.hpp"
+#include "base/logging.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
+{
+string RunSimpleHttpRequest(string const & url)
+{
+ HttpClient request(url);
+
+ if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200)
+ {
+ return request.ServerResponse();
+ }
+
+ return {};
+}
+
+bool CheckUberAnswer(json_t const * answer)
+{
+ // Uber products are not available at this point.
+ if (!json_is_array(answer))
+ return false;
+
+ if (json_array_size(answer) <= 0)
+ return false;
+
+ return true;
+}
+
+void FillProducts(json_t const * time, json_t const * price, vector<uber::Product> & products)
+{
+ auto const timeSize = json_array_size(time);
+ for (size_t i = 0; i < timeSize; ++i)
+ {
+ uber::Product product;
+ json_int_t estimatedTime = 0;
+ auto const item = json_array_get(time, i);
+ my::FromJSONObject(item, "display_name", product.m_name);
+ my::FromJSONObject(item, "estimate", estimatedTime);
+ product.m_time = strings::to_string(estimatedTime);
+ products.push_back(product);
+ }
+
+ auto const priceSize = json_array_size(price);
+ for (size_t i = 0; i < priceSize; ++i)
+ {
+ string name;
+ auto const item = json_array_get(price, i);
+
+ my::FromJSONObject(item, "display_name", name);
+ auto const it = find_if(products.begin(), products.end(), [&name](uber::Product const & product){
+ return product.m_name == name;
+ });
+ if (it == products.end())
+ continue;
+
+ my::FromJSONObject(item, "product_id", it->m_productId);
+ my::FromJSONObject(item, "estimate", it->m_price);
+ }
+
+ products.erase(remove_if(products.begin(), products.end(), [](uber::Product const & p){
+ return p.m_name.empty() || p.m_productId.empty() || p.m_time.empty() ||
+ p.m_price.empty();
+ }), products.end());
+}
+
+void GetAvailableProductsAsync(ms::LatLon const & from, ms::LatLon const & to, size_t const requestId,
+ 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;
+
+ try
+ {
+ my::Json timeRoot(time.get().c_str());
+ my::Json priceRoot(price.get().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))
+ {
+ FillProducts(timesArray, pricesArray, products);
+ }
+ }
+ catch (my::Json::Exception const & e)
+ {
+ LOG(LERROR, (e.Msg()));
+ products.clear();
+ }
+
+ fn(products, requestId);
+}
+} // namespace
+
+namespace uber
+{
+// static
+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);
+
+ return RunSimpleHttpRequest(url.str());
+}
+
+// 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"
+ }
+ ]
+ })";
+}
+
+// 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
+ }
+ ]
+ })";
+}
+
+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; });
+
+ return requestId;
+}
+
+// static
+string Api::GetRideRequestLink(string const & m_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);
+
+ return url.str();
+}
+} // namespace uber
diff --git a/partners_api/uber_api.hpp b/partners_api/uber_api.hpp
new file mode 100644
index 0000000000..4d9a19a0ab
--- /dev/null
+++ b/partners_api/uber_api.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "base/thread.hpp"
+
+#include "std/function.hpp"
+#include "std/mutex.hpp"
+#include "std/string.hpp"
+#include "std/unique_ptr.hpp"
+#include "std/vector.hpp"
+
+namespace ms
+{
+ class LatLon;
+} // namespace ms
+
+namespace downloader
+{
+ class HttpRequest;
+} // namespace downloader
+
+namespace uber
+{
+// Uber api wrapper based on synchronous http requests.
+class RawApi
+{
+public:
+ /// Returns information about the Uber products offered at a given location.
+ /// The response includes 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 string GetProducts(ms::LatLon const & pos);
+ /// Returns 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 string GetEstimatedTime(ms::LatLon const & pos);
+ /// Returns 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 string GetEstimatedPrice(ms::LatLon const & from, ms::LatLon const & to);
+};
+
+struct Product
+{
+ string m_productId;
+ string m_name;
+ string m_time;
+ string m_price;
+};
+/// @products - vector of available products for requested route.
+/// @requestId - identificator which was provided to GetAvailableProducts to identify request.
+using ProductsCallback = function<void(vector<Product> const & products, size_t const requestId)>;
+
+class Api
+{
+public:
+ /// 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);
+
+private:
+ using threadDeleter = function<void(threads::SimpleThread *)>;
+ unique_ptr<threads::SimpleThread, threadDeleter> m_thread;
+
+ mutex m_mutex;
+};
+} // namespace uber
+
+
+