diff options
author | Arsentiy Milchakov <milcars@mapswithme.com> | 2016-10-03 15:22:46 +0300 |
---|---|---|
committer | Arsentiy Milchakov <milcars@mapswithme.com> | 2016-10-10 18:14:45 +0300 |
commit | 5e716d70d81926305c53e88f68afba31ecde9224 (patch) | |
tree | 711b1d1cf976012352e001ffa5d5e4d31928a408 /partners_api | |
parent | ecc59be84ff4b13c768f41422964d69a90c3fa2c (diff) |
dummy implementation and smoke test
Diffstat (limited to 'partners_api')
-rw-r--r-- | partners_api/booking_api.cpp | 271 | ||||
-rw-r--r-- | partners_api/booking_api.hpp | 136 | ||||
-rw-r--r-- | partners_api/partners_api.pro | 18 | ||||
-rw-r--r-- | partners_api/partners_api_tests/booking_tests.cpp | 81 | ||||
-rw-r--r-- | partners_api/partners_api_tests/partners_api_tests.pro | 16 | ||||
-rw-r--r-- | partners_api/partners_api_tests/uber_tests.cpp | 29 | ||||
-rw-r--r-- | partners_api/uber_api.cpp | 266 | ||||
-rw-r--r-- | partners_api/uber_api.hpp | 74 |
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, ¤cy](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, ¤cy](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, ¤cy](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 + + + |