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>2018-06-19 17:16:50 +0300
committerVlad Mihaylenko <vxmihaylenko@gmail.com>2018-06-21 15:25:28 +0300
commitb6c0b3c25e178bc749aeaab38c6d4cda066461df (patch)
tree10861624c0b5789da9bb65712c82e98e39be69ee /partners_api
parentb6fc1f76f4e6db9d4661c45cd77a92441a5e55bc (diff)
[booking] added method to retrieve detailed info about hotel rooms (blockAvailability). GetMinPrice method is refactored.
Diffstat (limited to 'partners_api')
-rw-r--r--partners_api/CMakeLists.txt2
-rw-r--r--partners_api/booking_api.cpp160
-rw-r--r--partners_api/booking_api.hpp61
-rw-r--r--partners_api/booking_availability_params.cpp6
-rw-r--r--partners_api/booking_availability_params.hpp2
-rw-r--r--partners_api/booking_block_params.cpp60
-rw-r--r--partners_api/booking_block_params.hpp33
-rw-r--r--partners_api/booking_params_base.hpp16
-rw-r--r--partners_api/partners_api_tests/booking_tests.cpp73
9 files changed, 322 insertions, 91 deletions
diff --git a/partners_api/CMakeLists.txt b/partners_api/CMakeLists.txt
index b8a474c7f3..d7f0e3f53a 100644
--- a/partners_api/CMakeLists.txt
+++ b/partners_api/CMakeLists.txt
@@ -13,6 +13,8 @@ set(
booking_api.hpp
booking_availability_params.cpp
booking_availability_params.hpp
+ booking_block_params.cpp
+ booking_block_params.hpp
booking_params_base.hpp
facebook_ads.cpp
facebook_ads.hpp
diff --git a/partners_api/booking_api.cpp b/partners_api/booking_api.cpp
index dd4732d364..ad92a1548d 100644
--- a/partners_api/booking_api.cpp
+++ b/partners_api/booking_api.cpp
@@ -30,7 +30,7 @@ using namespace std::chrono;
namespace
{
string const kBookingApiBaseUrlV1 = "https://distribution-xml.booking.com/json/bookings";
-string const kBookingApiBaseUrlV2 = "https://distribution-xml.booking.com/2.0/json/";
+string const kBookingApiBaseUrlV2 = "https://distribution-xml.booking.com/2.0/json";
string const kExtendedHotelInfoBaseUrl = "https://hotels.maps.me/getDescription";
string const kPhotoOriginalUrl = "http://aff.bstatic.com/images/hotel/max500/";
string const kPhotoSmallUrl = "http://aff.bstatic.com/images/hotel/max300/";
@@ -231,43 +231,123 @@ void FillHotelInfo(string const & src, HotelInfo & info)
info.m_reviews = ParseReviews(reviewsArray);
}
-void FillPriceAndCurrency(string const & src, string const & currency, string & minPrice,
- string & priceCurrency)
+void FillPriceAndCurrency(json_t * src, string const & currency, BlockInfo & result)
{
- my::Json root(src.c_str());
- if (!json_is_array(root.get()))
- MYTHROW(my::Json::Exception, ("The answer must contain a json array."));
- size_t const rootSize = json_array_size(root.get());
+ FromJSONObject(src, "currency", result.m_currency);
+ FromJSONObject(src, "price", result.m_minPrice);
- if (rootSize == 0)
+ if (currency.empty() || result.m_currency == currency)
return;
- // Read default hotel price and currency.
- auto obj = json_array_get(root.get(), 0);
- FromJSONObject(obj, "min_price", minPrice);
- FromJSONObject(obj, "currency_code", priceCurrency);
-
- if (currency.empty() || priceCurrency == currency)
+ // Try to get price in requested currency.
+ json_t * other = json_object_get(src, "other_currency");
+ if (!json_is_object(other))
return;
- // Try to get price in requested currency.
- json_t * arr = json_object_get(obj, "other_currency");
- if (arr == nullptr || !json_is_array(arr))
+ string otherCurrency;
+ FromJSONObject(other, "currency", otherCurrency);
+ if (otherCurrency == currency)
+ {
+ result.m_currency = otherCurrency;
+ FromJSONObject(other, "price", result.m_minPrice);
return;
+ }
+}
+
+BlockInfo MakeBlock(json_t * src, string const & currency)
+{
+ BlockInfo result;
+
+ FromJSONObject(src, "block_id", result.m_blockId);
+ FromJSONObject(src, "name", result.m_name);
+ FromJSONObject(src, "room_description", result.m_description);
+ FromJSONObject(src, "max_occupancy", result.m_maxOccupancy);
+ FromJSONObject(src, "breakfast_included", result.m_breakfastIncluded);
+ FromJSONObject(src, "deposit_required", result.m_depositRequired);
- size_t sz = json_array_size(arr);
- string code;
+ auto minPriceRoot = json_object_get(src, "min_price");
+ if (!json_is_object(minPriceRoot))
+ MYTHROW(my::Json::Exception, ("The min_price must contain a json object."));
+
+ FillPriceAndCurrency(minPriceRoot, currency, result);
+
+ auto photosArray = json_object_get(src, "photos");
+ size_t sz = json_array_size(photosArray);
+ string photoUrl;
for (size_t i = 0; i < sz; ++i)
{
- auto el = json_array_get(arr, i);
- FromJSONObject(el, "currency_code", code);
- if (code == currency)
+ auto photoItem = json_array_get(photosArray, i);
+ FromJSONObject(photoItem, "url_original", photoUrl);
+ result.m_photos.emplace_back(photoUrl);
+ }
+
+ Deals deals;
+ bool lastMinuteDeal = false;
+ FromJSONObjectOptionalField(src, "is_last_minute_deal", lastMinuteDeal);
+ if (lastMinuteDeal)
+ {
+ deals.m_types.emplace_back(Deals::Type::LastMinute);
+ FromJSONObject(src, "last_minute_deal_percentage", deals.m_discount);
+ }
+ bool smartDeal = false;
+ FromJSONObjectOptionalField(src, "is_smart_deal", smartDeal);
+ if (smartDeal)
+ deals.m_types.emplace_back(Deals::Type::Smart);
+
+ string refundableUntil;
+ auto refundableUntilObject = json_object_get(src, "refundable_until");
+ if (json_is_string(refundableUntilObject))
+ {
+ FromJSON(refundableUntilObject, refundableUntil);
+
+ if (!refundableUntil.empty())
{
- priceCurrency = code;
- FromJSONObject(el, "min_price", minPrice);
- break;
+ istringstream ss(refundableUntil);
+ tm t = {};
+ ss >> base::get_time(&t, "%Y-%m-%d %H:%M:%S");
+ if (ss.fail())
+ LOG(LWARNING, ("Incorrect refundable_until date =", refundableUntil));
+ else
+ result.m_refundableUntil = system_clock::from_time_t(mktime(&t));
}
}
+
+ return result;
+}
+
+void FillBlocks(string const & src, string const & currency, Blocks & blocks)
+{
+ my::Json root(src.c_str());
+ if (!json_is_object(root.get()))
+ MYTHROW(my::Json::Exception, ("The answer must contain a json object."));
+
+ auto rootArray = json_object_get(root.get(), "result");
+ if (!json_is_array(rootArray))
+ MYTHROW(my::Json::Exception, ("The \"result\" field must contain a json array."));
+
+ size_t const rootSize = json_array_size(rootArray);
+ ASSERT_LESS(rootSize, 2, ("Several hotels is not supported in this method"));
+ if (rootSize == 0)
+ return;
+
+ auto rootItem = json_array_get(rootArray, 0);
+ if (!json_is_object(rootItem))
+ MYTHROW(my::Json::Exception, ("The root item must contain a json object."));
+
+ auto blocksArray = json_object_get(rootItem, "block");
+ if (!json_is_array(blocksArray))
+ MYTHROW(my::Json::Exception, ("The \"block\" field must contain a json array."));
+
+ size_t const blocksSize = json_array_size(blocksArray);
+ for (size_t i = 0; i < blocksSize; ++i)
+ {
+ auto block = json_array_get(blocksArray, i);
+
+ if (!json_is_object(block))
+ MYTHROW(my::Json::Exception, ("The block item must contain a json object."));
+
+ blocks.Add(MakeBlock(block, currency));
+ }
}
void FillHotelIds(string const & src, vector<std::string> & ids)
@@ -345,6 +425,14 @@ bool RawApi::HotelAvailability(AvailabilityParams const & params, string & resul
return RunSimpleHttpRequest(true, url, result);
}
+// static
+bool RawApi::BlockAvailability(BlockParams const & params, string & result)
+{
+ string url = MakeApiUrlV2("blockAvailability", params.Get());
+
+ return RunSimpleHttpRequest(true, url, result);
+}
+
string Api::GetBookHotelUrl(string const & baseUrl) const
{
ASSERT(!baseUrl.empty(), ());
@@ -407,31 +495,29 @@ string Api::ApplyAvailabilityParams(string const & url, AvailabilityParams const
return ApplyAvailabilityParamsUniversal(url, params);
}
-void Api::GetMinPrice(string const & hotelId, string const & currency,
- GetMinPriceCallback const & fn) const
+void Api::GetBlockAvailability(BlockParams const & params,
+ BlockAvailabilityCallback const & fn) const
{
- GetPlatform().RunTask(Platform::Thread::Network, [hotelId, currency, fn]()
+ GetPlatform().RunTask(Platform::Thread::Network, [params, fn]()
{
- string minPrice;
- string priceCurrency;
string httpResult;
- if (!RawApi::GetHotelAvailability(hotelId, currency, httpResult))
+ if (!RawApi::BlockAvailability(params, httpResult))
{
- fn(hotelId, minPrice, priceCurrency);
+ fn(params.m_hotelId, {});
return;
}
+ Blocks blocks;
try
{
- FillPriceAndCurrency(httpResult, currency, minPrice, priceCurrency);
+ FillBlocks(httpResult, params.m_currency, blocks);
}
catch (my::Json::Exception const & e)
{
LOG(LERROR, (e.Msg()));
- minPrice.clear();
- priceCurrency.clear();
+ blocks = {};
}
- fn(hotelId, minPrice, priceCurrency);
+ fn(params.m_hotelId, blocks);
});
}
@@ -456,7 +542,7 @@ void Api::GetHotelInfo(string const & hotelId, string const & lang,
}
catch (my::Json::Exception const & e)
{
- LOG(LERROR, ("Failed to parse json:", hotelId, result, e.what()));
+ LOG(LINFO, ("Failed to parse json:", hotelId, result, e.what()));
ClearHotelInfo(info);
}
diff --git a/partners_api/booking_api.hpp b/partners_api/booking_api.hpp
index 5c4302c3b6..f35d295003 100644
--- a/partners_api/booking_api.hpp
+++ b/partners_api/booking_api.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "partners_api/booking_availability_params.hpp"
+#include "partners_api/booking_block_params.hpp"
#include "platform/safe_callback.hpp"
@@ -48,6 +49,59 @@ struct HotelInfo
uint32_t m_scoreCount = 0;
};
+struct Deals
+{
+ enum Type
+ {
+ /// Good price.
+ Smart,
+ /// Sale with discount in percent from base price.
+ LastMinute
+ };
+
+ std::vector<Type> m_types;
+ uint8_t m_discount = 0;
+};
+
+struct BlockInfo
+{
+ static double constexpr kIncorrectPrice = std::numeric_limits<double>::max();
+ std::string m_blockId;
+ std::string m_name;
+ std::string m_description;
+ uint8_t m_maxOccupancy = 0;
+ double m_minPrice = kIncorrectPrice;
+ std::string m_currency;
+ std::vector<std::string> m_photos;
+ Deals m_deals;
+ std::chrono::time_point<std::chrono::system_clock> m_refundableUntil;
+ bool m_breakfastIncluded = false;
+ bool m_depositRequired = false;
+};
+
+struct Blocks
+{
+ void Add(BlockInfo && block)
+ {
+ if (block.m_minPrice < m_totalMinPrice)
+ {
+ m_totalMinPrice = block.m_minPrice;
+ m_currency = block.m_currency;
+ }
+ if (block.m_deals.m_discount > m_maxDiscount)
+ m_maxDiscount = block.m_deals.m_discount;
+
+ m_blocks.emplace_back(block);
+ }
+
+ double m_totalMinPrice = BlockInfo::kIncorrectPrice;
+ std::string m_currency;
+
+ uint8_t m_maxDiscount = 0;
+
+ std::vector<BlockInfo> m_blocks;
+};
+
class RawApi
{
public:
@@ -56,9 +110,11 @@ public:
static bool GetExtendedInfo(std::string const & hotelId, std::string const & lang, std::string & result);
// Booking Api v2 methods:
static bool HotelAvailability(AvailabilityParams const & params, std::string & result);
+ static bool BlockAvailability(BlockParams const & params, string & result);
};
-using GetMinPriceCallback = platform::SafeCallback<void(std::string const & hotelId, std::string const & price, std::string const & currency)>;
+using BlockAvailabilityCallback =
+ platform::SafeCallback<void(std::string const & hotelId, Blocks const & blocks)>;
using GetHotelInfoCallback = platform::SafeCallback<void(HotelInfo const & hotelInfo)>;
// NOTE: this callback will be called on the network thread.
using GetHotelAvailabilityCallback = std::function<void(std::vector<std::string> hotelIds)>;
@@ -79,8 +135,7 @@ public:
/// Real-time information methods (used for retrieving rapidly changing information).
/// These methods send requests directly to Booking.
- void GetMinPrice(std::string const & hotelId, std::string const & currency,
- GetMinPriceCallback const & fn) const;
+ void GetBlockAvailability(BlockParams const & params, BlockAvailabilityCallback const & fn) const;
/// NOTE: callback will be called on the network thread.
void GetHotelAvailability(AvailabilityParams const & params,
diff --git a/partners_api/booking_availability_params.cpp b/partners_api/booking_availability_params.cpp
index 341e4d32da..506b764a13 100644
--- a/partners_api/booking_availability_params.cpp
+++ b/partners_api/booking_availability_params.cpp
@@ -1,5 +1,4 @@
#include "partners_api/booking_availability_params.hpp"
-#include "partners_api/utils.hpp"
#include "base/string_utils.hpp"
@@ -9,11 +8,6 @@ using namespace base;
namespace
{
-std::string FormatTime(booking::AvailabilityParams::Time p)
-{
- return partners_api::FormatTime(p, "%Y-%m-%d");
-}
-
bool IsAcceptedByFilter(booking::AvailabilityParams::UrlFilter const & filter,
std::string const & value)
{
diff --git a/partners_api/booking_availability_params.hpp b/partners_api/booking_availability_params.hpp
index 3f3bc26db9..7d29c742aa 100644
--- a/partners_api/booking_availability_params.hpp
+++ b/partners_api/booking_availability_params.hpp
@@ -37,8 +37,6 @@ struct AvailabilityParams : public ParamsBase
int8_t m_ageOfChild = kNoChildren;
};
- using Clock = std::chrono::system_clock;
- using Time = Clock::time_point;
using Hotels = std::vector<std::string>;
using Rooms = std::vector<Room>;
using Stars = std::vector<std::string>;
diff --git a/partners_api/booking_block_params.cpp b/partners_api/booking_block_params.cpp
new file mode 100644
index 0000000000..bcd98b4548
--- /dev/null
+++ b/partners_api/booking_block_params.cpp
@@ -0,0 +1,60 @@
+#include "partners_api/booking_block_params.hpp"
+
+#include "base/string_utils.hpp"
+
+using namespace base;
+
+namespace booking
+{
+// static
+BlockParams BlockParams::MakeDefault()
+{
+ BlockParams result;
+ // Use tomorrow and day after tomorrow by default.
+ result.m_checkin = Clock::now() + std::chrono::hours(24);
+ result.m_checkout = Clock::now() + std::chrono::hours(48);
+ // Information about sales by default.
+ result.m_extras = {"deal_smart", "deal_lastm", "photos"};
+
+ return result;
+}
+
+url::Params BlockParams::Get() const
+{
+ url::Params params = {{"hotel_ids", m_hotelId},
+ {"checkin", FormatTime(m_checkin)},
+ {"checkout", FormatTime(m_checkout)}};
+
+ if (!m_currency.empty())
+ params.emplace_back("currency", m_currency);
+
+ if (!m_extras.empty())
+ params.emplace_back("extras", strings::JoinStrings(m_extras, ','));
+
+ if (!m_language.empty())
+ params.emplace_back("language", m_language);
+
+ return params;
+}
+
+bool BlockParams::IsEmpty() const
+{
+ return m_checkin == Time() || m_checkout == Time();
+}
+
+bool BlockParams::Equals(ParamsBase const & rhs) const
+{
+ return rhs.Equals(*this);
+}
+
+bool BlockParams::Equals(BlockParams const & rhs) const
+{
+ return m_checkin == rhs.m_checkin && m_checkout == rhs.m_checkout &&
+ m_currency == rhs.m_currency && m_extras == rhs.m_extras && m_language == rhs.m_language;
+}
+
+void BlockParams::Set(ParamsBase const & src)
+{
+ src.CopyTo(*this);
+}
+} // namepace booking \ No newline at end of file
diff --git a/partners_api/booking_block_params.hpp b/partners_api/booking_block_params.hpp
new file mode 100644
index 0000000000..836cfbfc49
--- /dev/null
+++ b/partners_api/booking_block_params.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "partners_api/booking_params_base.hpp"
+
+#include "base/url_helpers.hpp"
+
+#include <string>
+#include <vector>
+
+namespace booking
+{
+struct BlockParams : public ParamsBase
+{
+ using Extras = std::vector<std::string>;
+
+ static BlockParams MakeDefault();
+
+ base::url::Params Get() const;
+
+ // ParamsBase overrides:
+ bool IsEmpty() const override;
+ bool Equals(ParamsBase const & rhs) const override;
+ bool Equals(BlockParams const & lhs) const override;
+ void Set(ParamsBase const & src) override;
+
+ std::string m_hotelId;
+ std::string m_currency;
+ Time m_checkin;
+ Time m_checkout;
+ Extras m_extras;
+ std::string m_language;
+};
+} // namespce booking
diff --git a/partners_api/booking_params_base.hpp b/partners_api/booking_params_base.hpp
index a740197b34..c2c275662b 100644
--- a/partners_api/booking_params_base.hpp
+++ b/partners_api/booking_params_base.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include "partners_api/utils.hpp"
+
#include "base/macros.hpp"
#include <exception>
@@ -7,9 +9,18 @@
namespace booking
{
struct AvailabilityParams;
+struct BlockParams;
struct ParamsBase
{
+ using Clock = std::chrono::system_clock;
+ using Time = Clock::time_point;
+
+ static std::string FormatTime(Time p)
+ {
+ return partners_api::FormatTime(p, "%Y-%m-%d");
+ }
+
virtual ~ParamsBase() = default;
virtual bool IsEmpty() const = 0;
@@ -21,6 +32,11 @@ struct ParamsBase
return false;
}
+ virtual bool Equals(BlockParams const & lhs) const
+ {
+ return false;
+ }
+
template <typename T>
void CopyTo(T & dest) const
{
diff --git a/partners_api/partners_api_tests/booking_tests.cpp b/partners_api/partners_api_tests/booking_tests.cpp
index fdf741e2b8..5491a9c21a 100644
--- a/partners_api/partners_api_tests/booking_tests.cpp
+++ b/partners_api/partners_api_tests/booking_tests.cpp
@@ -16,7 +16,7 @@ class AsyncGuiThreadBooking : public AsyncGuiThread
public:
AsyncGuiThreadBooking()
{
- SetBookingUrlForTesting("http://localhost:34568/booking/min_price");
+ SetBookingUrlForTesting("http://localhost:34568/booking");
}
~AsyncGuiThreadBooking() override
@@ -55,66 +55,53 @@ UNIT_TEST(Booking_HotelAvailability)
LOG(LINFO, (result));
}
-UNIT_CLASS_TEST(AsyncGuiThreadBooking, Booking_GetMinPrice)
+UNIT_CLASS_TEST(AsyncGuiThreadBooking, Booking_GetBlockAvailability)
{
- string const kHotelId = "0"; // Internal hotel id for testing.
+ auto params = BlockParams::MakeDefault();
+ params.m_hotelId = "0"; // Internal hotel id for testing.
Api api;
{
- string price;
+ double price = std::numeric_limits<double>::max();
string currency;
string hotelId;
- api.GetMinPrice(kHotelId, "" /* default currency */,
- [&hotelId, &price, &currency](string const & id, string const & val, string const & curr) {
- hotelId = id;
- price = val;
- currency = curr;
- testing::Notify();
- });
+ api.GetBlockAvailability(params, [&hotelId, &price, &currency](std::string const & id,
+ Blocks const & blocks)
+ {
+ hotelId = id;
+ price = blocks.m_totalMinPrice;
+ currency = blocks.m_currency;
+ testing::Notify();
+ });
testing::Wait();
- TEST_EQUAL(hotelId, kHotelId, ());
- TEST(!price.empty(), ());
+ TEST_EQUAL(hotelId, params.m_hotelId, ());
+ TEST_NOT_EQUAL(price, std::numeric_limits<double>::max(), ());
TEST(!currency.empty(), ());
- TEST_EQUAL(currency, "USD", ());
+ TEST_EQUAL(currency, "EUR", ());
}
{
- string price;
+ auto params = BlockParams::MakeDefault();
+ params.m_hotelId = "0"; // Internal hotel id for testing.
+ params.m_currency = "RUB";
+ double price = std::numeric_limits<double>::max();
string currency;
string hotelId;
- api.GetMinPrice(kHotelId, "RUB", [&hotelId, &price, &currency](string const & id, string const & val, string const & curr)
- {
- hotelId = id;
- price = val;
- currency = curr;
- testing::Notify();
- });
+ api.GetBlockAvailability(params, [&hotelId, &price, &currency](std::string const & id,
+ Blocks const & blocks)
+ {
+ hotelId = id;
+ price = blocks.m_totalMinPrice;
+ currency = blocks.m_currency;
+ testing::Notify();
+ });
testing::Wait();
- TEST_EQUAL(hotelId, kHotelId, ());
- TEST(!price.empty(), ());
+ TEST_EQUAL(hotelId, params.m_hotelId, ());
+ TEST_NOT_EQUAL(price, std::numeric_limits<double>::max(), ());
TEST(!currency.empty(), ());
TEST_EQUAL(currency, "RUB", ());
}
-
- {
- string price;
- string currency;
- string hotelId;
- api.GetMinPrice(kHotelId, "ISK", [&hotelId, &price, &currency](string const & id, string const & val, string const & curr)
- {
- hotelId = id;
- price = val;
- currency = curr;
- testing::Notify();
- });
- testing::Wait();
-
- TEST_EQUAL(hotelId, kHotelId, ());
- TEST(!price.empty(), ());
- TEST(!currency.empty(), ());
- TEST_EQUAL(currency, "ISK", ());
- }
}
UNIT_CLASS_TEST(AsyncGuiThread, GetHotelInfo)