From 99c405f19dd2a7cff5cc4c37790a424fb88346a4 Mon Sep 17 00:00:00 2001 From: Arsentiy Milchakov Date: Thu, 5 Mar 2020 15:55:15 +0300 Subject: [PATCH] [core] freenow taxi is added --- data/taxi_places/freenow.json | 191 +++++++++++++++ partners_api/CMakeLists.txt | 2 + partners_api/freenow_api.cpp | 219 ++++++++++++++++++ partners_api/freenow_api.hpp | 70 ++++++ .../partners_api_tests/CMakeLists.txt | 7 +- .../partners_api_tests/freenow_tests.cpp | 150 ++++++++++++ .../partners_api_tests/taxi_engine_tests.cpp | 33 ++- partners_api/taxi_engine.cpp | 3 + partners_api/taxi_places_loader.cpp | 1 + partners_api/taxi_provider.hpp | 2 + tools/python/ResponseProvider.py | 8 + tools/python/jsons.py | 69 ++++++ tools/python/testserver.py | 9 +- tools/python/tornado_handler.py | 6 +- 14 files changed, 764 insertions(+), 6 deletions(-) create mode 100644 data/taxi_places/freenow.json create mode 100644 partners_api/freenow_api.cpp create mode 100644 partners_api/freenow_api.hpp create mode 100644 partners_api/partners_api_tests/freenow_tests.cpp diff --git a/data/taxi_places/freenow.json b/data/taxi_places/freenow.json new file mode 100644 index 0000000000..77850425f0 --- /dev/null +++ b/data/taxi_places/freenow.json @@ -0,0 +1,191 @@ +{ + "disabled": { + "countries": [], + "mwms": [] + }, + "enabled": { + "countries": [ + { + "cities": [ + "Arezzo", + "Bologna", + "Cagliari", + "Milan", + "Naples", + "Palermo", + "Rome", + "Turin" + ], + "id": "Italy" + }, + { + "cities": [ + "Faro", + "Lisbon", + "Porto" + ], + "id": "Portugal" + }, + { + "cities": [ + "Gdansk", + "Katowice", + "Krakow", + "\u0141\u00f3d\u017a", + "Pozna\u0144", + "Warsaw", + "Wroc\u0142aw" + ], + "id": "Poland" + }, + { + "cities": [ + "Stockholm" + ], + "id": "Sweden" + }, + { + "cities": [ + "Graz", + "Innsbruck", + "Salzburg", + "Vienna" + ], + "id": "Austria" + }, + { + "cities": [ + "Brighton", + "Edinburgh", + "Glasgow", + "London", + "Manchester", + "City of Nottingham", + "Oxford", + "Reading" + ], + "id": "Great Britain" + }, + { + "cities": [ + "Aachen", + "Aschaffenburg", + "Augsburg", + "Berlin", + "B\u00f6blingen", + "Bochum", + "Bonn", + "Bottrop", + "Brunswick", + "Bremen", + "Dachau", + "Darmstadt", + "Dortmund", + "Dresden", + "Dusseldorf", + "Duisburg", + "Ebersberg", + "Emmendingen (Kernstadt)", + "Erding", + "Erfurt", + "Erlangen", + "Essen", + "Esslingen am Neckar", + "Flensburg", + "Frankfurt", + "Freiburg im Breisgau", + "F\u00fcrstenfeldbruck", + "F\u00fcrth", + "Gelsenkirchen", + "Gladbeck", + "G\u00f6ppingen", + "G\u00fctersloh", + "Hagen", + "Halle (Saale)", + "Hamburg", + "Hamm", + "Hanau", + "Hanover", + "Heidelberg", + "Heilbronn", + "Herne", + "Herzogenaurach", + "Hildesheim", + "Ingolstadt", + "Kaiserslautern", + "Karlsruhe", + "Kassel", + "Kiel", + "Koblenz", + "K\u00f6ln", + "Krefeld", + "Landsberg am Lech", + "Leipzig", + "Leverkusen", + "L\u00fcbeck", + "Ludwigsburg", + "Ludwigshafen am Rhein", + "Magdeburg", + "Mainz", + "Mannheim", + "Mettmann", + "M\u00f6nchengladbach", + "Munich", + "M\u00fclheim an der Ruhr", + "M\u00fcnster", + "Neuss", + "Nuremberg", + "Oberhausen", + "Offenbach am Main", + "Osnabr\u00fcck", + "Paderborn", + "Potsdam", + "Rastatt", + "Baden-Baden", + "Regensburg", + "Reutlingen", + "Rosenheim", + "Rostock", + "Saarbr\u00fccken", + "Solingen", + "Starnberg", + "Steinfurt", + "Stuttgart", + "T\u00fcbingen", + "Neu-Ulm", + "Wetzlar", + "Wiesbaden", + "Wolfsburg", + "Wuppertal" + ], + "id": "Germany" + }, + { + "cities": [ + "Cork", + "Dublin", + "Galway", + "Limerick", + "Navan" + ], + "id": "Ireland" + }, + { + "cities": [ + "Zurich" + ], + "id": "Switzerland" + }, + { + "cities": [ + "Barcelona", + "Madrid", + "Seville", + "Valencia" + ], + "id": "Spain" + } + ], + "mwms": [] + } +} \ No newline at end of file diff --git a/partners_api/CMakeLists.txt b/partners_api/CMakeLists.txt index b39f2c9f24..182968e1fe 100644 --- a/partners_api/CMakeLists.txt +++ b/partners_api/CMakeLists.txt @@ -20,6 +20,8 @@ set( downloader_promo.hpp facebook_ads.cpp facebook_ads.hpp + freenow_api.cpp + freenow_api.hpp google_ads.cpp google_ads.hpp locals_api.cpp diff --git a/partners_api/freenow_api.cpp b/partners_api/freenow_api.cpp new file mode 100644 index 0000000000..8979f51391 --- /dev/null +++ b/partners_api/freenow_api.cpp @@ -0,0 +1,219 @@ +#include "partners_api/freenow_api.hpp" + +#include "platform/http_client.hpp" +#include "platform/platform.hpp" +#include "platform/preferred_languages.hpp" + +#include "coding/url.hpp" + +#include "geometry/latlon.hpp" + +#include "base/logging.hpp" + +#include "std/target_os.hpp" + +#include +#include +#include + +#include "3party/jansson/myjansson.hpp" + +#include "private.h" + +namespace +{ +auto const kTimeoutSec = 15; + +bool CheckResponse(json_t const * answer) +{ + if (answer == nullptr) + return false; + + if (!json_is_array(answer)) + return false; + + if (json_array_size(answer) <= 0) + return false; + + return true; +} +} // namespace + +namespace taxi +{ +namespace freenow +{ +std::string const kTaxiEndpoint = "https://api.live.free-now.com/publicapigatewayservice/v1"; + +bool RawApi::GetAccessToken(std::string & result, std::string const & baseUrl /* = kTaxiEndpoint */) +{ + platform::HttpClient request(url::Join(baseUrl, "oauth/token")); + request.SetTimeout(kTimeoutSec); + request.SetBodyData("grant_type=client_credentials", "application/x-www-form-urlencoded"); + request.SetUserAndPassword(FREENOW_CLIENT_ID, FREENOW_CLIENT_SECRET); + + return request.RunHttpRequest(result); +} + +bool RawApi::GetServiceTypes(ms::LatLon const & from, ms::LatLon const & to, + std::string const & token, std::string & result, + const std::string & baseUrl /* = kTaxiEndpoint */) +{ + std::ostringstream url; + url << std::fixed << std::setprecision(6) << baseUrl << "/service-types?pickupLatitude=" + << from.m_lat << "&pickupLongitude=" << from.m_lon << "&destinationLatitude=" << to.m_lat + << "&destinationLongitude=" << to.m_lon; + + platform::HttpClient request(url.str()); + request.SetTimeout(kTimeoutSec); + request.SetRawHeader("Authorization", "Bearer " + token); + request.SetRawHeader("Accept", "application/json"); + request.SetRawHeader("Accept-Language", languages::GetCurrentOrig()); + + return request.RunHttpRequest(result); +} + +SafeToken::SafeToken(Token const & token) + : m_token(token) +{ +} + +void SafeToken::Set(Token const & token) +{ + std::lock_guard lock(m_mutex); + + m_token = token; +} + +SafeToken::Token SafeToken::Get() const +{ + std::lock_guard lock(m_mutex); + + return m_token; +} + +/// Requests list of available products from Freenow. +void Api::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, + ProductsCallback const & successFn, + ErrorProviderCallback const & errorFn) +{ + ASSERT(successFn, ()); + ASSERT(errorFn, ()); + + // TODO(a): Add ErrorCode::FarDistance and provide this error code. + if (!IsDistanceSupported(from, to)) + { + errorFn(ErrorCode::NoProducts); + return; + } + + static_assert(std::is_const::value, ""); + + GetPlatform().RunTask(Platform::Thread::Network, [this, from, to, successFn, errorFn]() + { + auto token = m_accessToken.Get(); + if (token.m_expiredTime <= std::chrono::steady_clock::now() - std::chrono::seconds(kTimeoutSec)) + { + std::string tokenSource; + if (!RawApi::GetAccessToken(tokenSource, m_baseUrl)) + { + errorFn(ErrorCode::RemoteError); + return; + } + + token = MakeTokenFromJson(tokenSource); + m_accessToken.Set(token); + } + + std::string httpResult; + if (!RawApi::GetServiceTypes(from, to, token.m_token, httpResult, m_baseUrl)) + { + errorFn(ErrorCode::RemoteError); + return; + } + + std::vector products; + try + { + products = MakeProductsFromJson(httpResult); + } + catch (base::Json::Exception const & e) + { + LOG(LERROR, (e.Msg())); + products.clear(); + } + + if (products.empty()) + errorFn(ErrorCode::NoProducts); + else + successFn(products); + + }); +} + +/// Returns link which allows you to launch the Freenow app. +RideRequestLinks Api::GetRideRequestLinks(std::string const & productId, ms::LatLon const & from, + ms::LatLon const & to) const +{ + std::ostringstream deepLink; + deepLink << std::fixed << std::setprecision(6) + << "mytaxi://de.mytaxi.passenger/order?pickup_coordinate=" << from.m_lat << "," + << from.m_lon << "&destination_coordinate=" << to.m_lat << "," << to.m_lon; + + std::string const universalLink = "https://mytaxi.onelink.me/HySP?pid=maps.me&c=in-app-referral-" + "link_030320_my_pa_in_0_gl_gl_-_mx_mo_co_mx_af_-_ge_-_-_-_-_-" + "&is_retargeting=true&af_dp=mytaxi%3A%2F%2Fde.mytaxi.passenger"; + return {deepLink.str(), universalLink}; + +} + +std::vector MakeProductsFromJson(std::string const & src) +{ + std::vector products; + + base::Json root(src.c_str()); + auto const serviceTypesArray = json_object_get(root.get(), "serviceTypes"); + if (!CheckResponse(serviceTypesArray)) + return {}; + + auto const count = json_array_size(serviceTypesArray); + for (size_t i = 0; i < count; ++i) + { + taxi::Product product; + uint64_t time = 0; + uint64_t price = 0; + auto const item = json_array_get(serviceTypesArray, i); + + FromJSONObjectOptionalField(item, "id", product.m_productId); + FromJSONObjectOptionalField(item, "displayName", product.m_name); + + auto const eta = json_object_get(item, "eta"); + FromJSONObjectOptionalField(eta, "value", time); + product.m_time = strings::to_string(time); + + auto const fare = json_object_get(item, "fare"); + FromJSONObjectOptionalField(fare, "displayValue", product.m_price); + FromJSONObjectOptionalField(fare, "currencyCode", product.m_currency); + + if (product.m_name.empty() || product.m_time.empty() || product.m_price.empty()) + continue; + + products.push_back(std::move(product)); + } + + return products; +} + +SafeToken::Token MakeTokenFromJson(std::string const & src) +{ + SafeToken::Token result; + base::Json root(src.c_str()); + FromJSONObject(root.get(), "access_token", result.m_token); + uint64_t expiresInSeconds = 0; + FromJSONObject(root.get(), "expires_in", expiresInSeconds); + result.m_expiredTime = std::chrono::steady_clock::now() + std::chrono::seconds(expiresInSeconds); + + return result; +} +} // namespace freenow +} // namespace taxi diff --git a/partners_api/freenow_api.hpp b/partners_api/freenow_api.hpp new file mode 100644 index 0000000000..1518ac777d --- /dev/null +++ b/partners_api/freenow_api.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include "partners_api/taxi_base.hpp" + +#include +#include + +namespace ms +{ +class LatLon; +} + +namespace taxi +{ +namespace freenow +{ +extern std::string const kTaxiEndpoint; +/// "Free now" api wrapper based on synchronous http requests. +class RawApi +{ +public: + static bool GetAccessToken(std::string & result, std::string const & url = kTaxiEndpoint); + static bool GetServiceTypes(ms::LatLon const & from, ms::LatLon const & to, + std::string const & token, std::string & result, + std::string const & url = kTaxiEndpoint); +}; + +class SafeToken +{ +public: + struct Token + { + std::string m_token; + std::chrono::steady_clock::time_point m_expiredTime; + }; + + SafeToken() = default; + void Set(Token const & token); + Token Get() const; + +private: + SafeToken(Token const & token); + + mutable std::mutex m_mutex; + Token m_token; +}; + +/// Class which used for making products from http requests results. +class Api : public ApiBase +{ +public: + explicit Api(std::string const & baseUrl = kTaxiEndpoint) : ApiBase(baseUrl) {} + // ApiBase overrides: + /// Requests list of available products from "Free now". + void GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, + ProductsCallback const & successFn, + ErrorProviderCallback const & errorFn) override; + + /// Returns link which allows you to launch the "Free now" app. + RideRequestLinks GetRideRequestLinks(std::string const & productId, ms::LatLon const & from, + ms::LatLon const & to) const override; + +private: + SafeToken m_accessToken; +}; + +SafeToken::Token MakeTokenFromJson(std::string const & src); +std::vector MakeProductsFromJson(std::string const & src); +} // namespace freenow +} // namespace taxi diff --git a/partners_api/partners_api_tests/CMakeLists.txt b/partners_api/partners_api_tests/CMakeLists.txt index c448ce9162..1b14b8f3c5 100644 --- a/partners_api/partners_api_tests/CMakeLists.txt +++ b/partners_api/partners_api_tests/CMakeLists.txt @@ -7,8 +7,10 @@ set( ads_engine_tests.cpp booking_tests.cpp facebook_tests.cpp + freenow_tests.cpp google_tests.cpp - maxim_tests.cpp +# Maxim taxi project is disabled. +# maxim_tests.cpp # Megafon project is disabled until the contract is renewed or extended. # megafon_countries_tests.cpp mopub_tests.cpp @@ -17,7 +19,8 @@ set( rutaxi_tests.cpp taxi_engine_tests.cpp taxi_places_tests.cpp - uber_tests.cpp +# Uber taxi project is disabled. +# uber_tests.cpp yandex_tests.cpp ) diff --git a/partners_api/partners_api_tests/freenow_tests.cpp b/partners_api/partners_api_tests/freenow_tests.cpp new file mode 100644 index 0000000000..94ddac4fca --- /dev/null +++ b/partners_api/partners_api_tests/freenow_tests.cpp @@ -0,0 +1,150 @@ + + +#include "testing/testing.hpp" + +#include "partners_api/freenow_api.hpp" + +#include "geometry/latlon.hpp" + +#include "platform/platform.hpp" + +#include + +namespace +{ +using Runner = Platform::ThreadRunner; + +std::string const kTokenResponse = R"( +{ + "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXV", + "token_type": "bearer", + "expires_in": 600, + "scope": "service-types" +})"; + +std::string const kServiceTypesResponse = R"( +{ + "serviceTypes": [ + { + "id": "TAXI", + "type": "TAXI", + "displayName": "Taxi", + "eta": { + "value": 0, + "displayValue": "0 Minutes" + }, + "fare": { + "type": "FIXED", + "value": 5000, + "currencyCode": "GBP", + "displayValue": "5000GBP" + }, + "availablePaymentMethodTypes": [ + "BUSINESS_ACCOUNT", + "CREDIT_CARD", + "PAYPAL", + "CASH" + ], + "seats": { + "max": 4, + "values": [], + "displayValue": "4" + }, + "availableBookingOptions": [ + { + "name": "COMMENT", + "displayName": "COMMENT", + "type": "TEXT" + }, + { + "name": "MERCEDES", + "displayName": "MERCEDES", + "type": "BOOLEAN" + }, + { + "name": "FAVORITE_DRIVER", + "displayName": "FAVORITE_DRIVER", + "type": "BOOLEAN" + }, + { + "name": "FIVE_STARS", + "displayName": "FIVE_STARS", + "type": "BOOLEAN" + }, + { + "name": "SMALL_ANIMAL", + "displayName": "SMALL_ANIMAL", + "type": "BOOLEAN" + } + ] + } + ] +})"; + +UNIT_TEST(Freenow_GetAccessToken) +{ + ms::LatLon const from(55.796918, 37.537859); + ms::LatLon const to(55.758213, 37.616093); + std::string result; + + taxi::freenow::RawApi::GetAccessToken(result); + TEST(!result.empty(), ()); + + auto const token = taxi::freenow::MakeTokenFromJson(result); + TEST(!token.m_token.empty(), ()); +} + +UNIT_TEST(Freenow_MakeTokenFromJson) +{ + auto const token = taxi::freenow::MakeTokenFromJson(kTokenResponse); + TEST(!token.m_token.empty(), ()); + TEST_NOT_EQUAL(token.m_expiredTime.time_since_epoch().count(), 0, ()); +} + +UNIT_TEST(Freenow_MakeProductsFromJson) +{ + auto const products = taxi::freenow::MakeProductsFromJson(kServiceTypesResponse); + TEST_EQUAL(products.size(), 1, ()); + TEST_EQUAL(products.back().m_name, "Taxi", ()); + TEST_EQUAL(products.back().m_time, "0", ()); + TEST_EQUAL(products.back().m_price, "5000GBP", ()); + TEST_EQUAL(products.back().m_currency, "GBP", ()); +} + +UNIT_CLASS_TEST(Runner, Freenow_GetAvailableProducts) +{ + taxi::freenow::Api api("http://localhost:34568/partners"); + ms::LatLon const from(55.796918, 37.537859); + ms::LatLon const to(55.758213, 37.616093); + + std::vector resultProducts; + + api.GetAvailableProducts(from, to, + [&resultProducts](std::vector const & products) { + resultProducts = products; + testing::Notify(); + }, + [](taxi::ErrorCode const code) { + TEST(false, (code)); + }); + + testing::Wait(); + + TEST(!resultProducts.empty(), ()); + + taxi::ErrorCode errorCode = taxi::ErrorCode::RemoteError; + ms::LatLon const farPos(56.838197, 35.908507); + api.GetAvailableProducts(from, farPos, + [](std::vector const & products) { + TEST(false, ()); + }, + [&errorCode](taxi::ErrorCode const code) { + errorCode = code; + testing::Notify(); + }); + + testing::Wait(); + + TEST_EQUAL(errorCode, taxi::ErrorCode::NoProducts, ()); +} +} // namespace diff --git a/partners_api/partners_api_tests/taxi_engine_tests.cpp b/partners_api/partners_api_tests/taxi_engine_tests.cpp index ea01ecc02f..4299afa4bb 100644 --- a/partners_api/partners_api_tests/taxi_engine_tests.cpp +++ b/partners_api/partners_api_tests/taxi_engine_tests.cpp @@ -1,5 +1,6 @@ #include "testing/testing.hpp" +#include "partners_api/freenow_api.hpp" #include "partners_api/maxim_api.hpp" #include "partners_api/rutaxi_api.hpp" #include "partners_api/taxi_engine.hpp" @@ -97,6 +98,18 @@ public: storage::CountryId GetMwmId(m2::PointD const & point) override { return {}; } }; +class IrelandDublinDelegate : public taxi::Delegate +{ +public: + storage::CountriesVec GetCountryIds(m2::PointD const & point) override + { + return {"Ireland"}; + } + + std::string GetCityName(m2::PointD const & point) override { return "Dublin"; } + storage::CountryId GetMwmId(m2::PointD const & point) override { return {}; } +}; + std::vector GetUberSynchronous(ms::LatLon const & from, ms::LatLon const & to, std::string const & url) { @@ -169,6 +182,15 @@ std::vector GetRutaxiSynchronous(ms::LatLon const & from, ms::Lat return rutaxiProducts; } +std::vector GetFreenowSynchronous(ms::LatLon const & from, ms::LatLon const & to, + std::string const & url) +{ + std::string freenowAnswer; + TEST(taxi::freenow::RawApi::GetServiceTypes(from, to, "-" /* token */, freenowAnswer, url), ()); + + return taxi::freenow::MakeProductsFromJson(freenowAnswer); +} + taxi::ProvidersContainer GetProvidersSynchronous(taxi::Engine const & engine, ms::LatLon const & from, ms::LatLon const & to, taxi::Delegate & delegate, std::string const & url) @@ -192,6 +214,9 @@ taxi::ProvidersContainer GetProvidersSynchronous(taxi::Engine const & engine, providers.emplace_back(taxi::Provider::Type::Rutaxi, GetRutaxiSynchronous(from, to, delegate, url)); break; + case taxi::Provider::Type::Freenow: + providers.emplace_back(taxi::Provider::Type::Freenow, GetFreenowSynchronous(from, to, url)); + break; case taxi::Provider::Type::Count: LOG(LERROR, ()); break; @@ -445,7 +470,8 @@ UNIT_CLASS_TEST(AsyncGuiThread, TaxiEngine_Smoke) taxi::Engine engine({{taxi::Provider::Type::Uber, kTesturl}, {taxi::Provider::Type::Yandex, kTesturl}, {taxi::Provider::Type::Maxim, kTesturl}, - {taxi::Provider::Type::Rutaxi, kTesturl}}); + {taxi::Provider::Type::Rutaxi, kTesturl}, + {taxi::Provider::Type::Freenow, kTesturl}}); engine.SetDelegate(std::make_unique()); BelarusMinskDelegate delegate; @@ -523,5 +549,10 @@ UNIT_TEST(TaxiEngine_GetProvidersAtPos) engine.SetDelegate(std::make_unique()); providers = engine.GetProvidersAtPos(latlon); TEST(providers.empty(), (providers)); + + engine.SetDelegate(std::make_unique()); + providers = engine.GetProvidersAtPos(latlon); + TEST_EQUAL(providers.size(), 1, ()); + TEST_EQUAL(providers[0], taxi::Provider::Type::Freenow, ()); } } // namespace diff --git a/partners_api/taxi_engine.cpp b/partners_api/taxi_engine.cpp index 0c177ad7ad..3b3220474c 100644 --- a/partners_api/taxi_engine.cpp +++ b/partners_api/taxi_engine.cpp @@ -1,4 +1,6 @@ #include "partners_api/taxi_engine.hpp" + +#include "partners_api/freenow_api.hpp" #include "partners_api/maxim_api.hpp" #include "partners_api/rutaxi_api.hpp" #include "partners_api/taxi_places_loader.hpp" @@ -118,6 +120,7 @@ Engine::Engine(std::vector urls /* = {} */) { AddApi(urls, Provider::Type::Yandex); AddApi(urls, Provider::Type::Rutaxi); + AddApi(urls, Provider::Type::Freenow); } void Engine::SetDelegate(std::unique_ptr delegate) diff --git a/partners_api/taxi_places_loader.cpp b/partners_api/taxi_places_loader.cpp index 19ac52643f..9279ad3d5b 100644 --- a/partners_api/taxi_places_loader.cpp +++ b/partners_api/taxi_places_loader.cpp @@ -54,6 +54,7 @@ std::string Loader::GetFileNameByProvider(Provider::Type const type) case Provider::Type::Rutaxi: return "taxi_places/rutaxi.json"; case Provider::Type::Uber: return "taxi_places/uber.json"; case Provider::Type::Yandex: return "taxi_places/yandex.json"; + case Provider::Type::Freenow: return "taxi_places/freenow.json"; case Provider::Type::Count: LOG(LERROR, ("Incorrect taxi provider")); return ""; } UNREACHABLE(); diff --git a/partners_api/taxi_provider.hpp b/partners_api/taxi_provider.hpp index 2a9c63fec5..f319f881dc 100644 --- a/partners_api/taxi_provider.hpp +++ b/partners_api/taxi_provider.hpp @@ -25,6 +25,7 @@ public: Yandex, Maxim, Rutaxi, + Freenow, Count }; @@ -82,6 +83,7 @@ inline std::string DebugPrint(Provider::Type type) case Provider::Type::Yandex: return "Yandex"; case Provider::Type::Maxim: return "Maxim"; case Provider::Type::Rutaxi: return "Rutaxi"; + case Provider::Type::Freenow: return "Freenow"; case Provider::Type::Count: ASSERT(false, ()); return ""; } UNREACHABLE(); diff --git a/tools/python/ResponseProvider.py b/tools/python/ResponseProvider.py index 37bae0091d..1c768cebaa 100644 --- a/tools/python/ResponseProvider.py +++ b/tools/python/ResponseProvider.py @@ -150,6 +150,8 @@ class ResponseProvider: "/gallery/v2/search/": self.promo_gallery_city, "/single/empty/gallery/v2/search/": self.promo_gallery_city_single_empty, "/single/gallery/v2/search/": self.promo_gallery_city_single, + "/partners/oauth/token": self.freenow_auth_token, + "/partners/service-types": self.freenow_service_types, }[url]() except: return self.test_404() @@ -251,6 +253,12 @@ class ResponseProvider: def promo_gallery_city_single(self): return Payload(jsons.PROMO_GALLERY_CITY_SINGLE) + def freenow_auth_token(self): + return Payload(jsons.FREENOW_AUTH_TOKEN) + + def freenow_service_types(self): + return Payload(jsons.FREENOW_SERVICE_TYPES) + def kill(self): logging.debug("Kill called in ResponseProvider") self.delegate.kill() diff --git a/tools/python/jsons.py b/tools/python/jsons.py index 46092a722f..04caaf635a 100644 --- a/tools/python/jsons.py +++ b/tools/python/jsons.py @@ -607,3 +607,72 @@ PROMO_GALLERY_CITY_SINGLE = """ } } """ + +FREENOW_AUTH_TOKEN = """ +{ + "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXV", + "token_type": "bearer", + "expires_in": 600, + "scope": "service-types" +} +""" + +FREENOW_SERVICE_TYPES = """ +{ + "serviceTypes": [ + { + "id": "TAXI", + "type": "TAXI", + "displayName": "Taxi", + "eta": { + "value": 0, + "displayValue": "0 Minutes" + }, + "fare": { + "type": "FIXED", + "value": 5000, + "currencyCode": "GBP", + "displayValue": "5000GBP" + }, + "availablePaymentMethodTypes": [ + "BUSINESS_ACCOUNT", + "CREDIT_CARD", + "PAYPAL", + "CASH" + ], + "seats": { + "max": 4, + "values": [], + "displayValue": "4" + }, + "availableBookingOptions": [ + { + "name": "COMMENT", + "displayName": "COMMENT", + "type": "TEXT" + }, + { + "name": "MERCEDES", + "displayName": "MERCEDES", + "type": "BOOLEAN" + }, + { + "name": "FAVORITE_DRIVER", + "displayName": "FAVORITE_DRIVER", + "type": "BOOLEAN" + }, + { + "name": "FIVE_STARS", + "displayName": "FIVE_STARS", + "type": "BOOLEAN" + }, + { + "name": "SMALL_ANIMAL", + "displayName": "SMALL_ANIMAL", + "type": "BOOLEAN" + } + ] + } + ] +} +""" diff --git a/tools/python/testserver.py b/tools/python/testserver.py index 1efedc1c1b..4915ed8f4d 100644 --- a/tools/python/testserver.py +++ b/tools/python/testserver.py @@ -181,8 +181,13 @@ class PostHandler(BaseHTTPRequestHandler, ResponseProviderMixin): def do_POST(self): self.init_vars() self.server.reset_selfdestruct_timer() - length = int(self.headers.getheader('content-length')) - self.dispatch_response(Payload(self.rfile.read(length))) + headers = self.prepare_headers() + payload = self.response_provider.response_for_url_and_headers(self.path, headers) + if payload.response_code() >= 300: + length = int(self.headers.getheader('content-length')) + self.dispatch_response(Payload(self.rfile.read(length))) + else: + self.dispatch_response(payload) def do_GET(self): diff --git a/tools/python/tornado_handler.py b/tools/python/tornado_handler.py index 017bf4fc6a..aa8c00e216 100644 --- a/tools/python/tornado_handler.py +++ b/tools/python/tornado_handler.py @@ -59,7 +59,11 @@ class MainHandler(tornado.web.RequestHandler, ResponseProviderMixin): def post(self, param): - self.dispatch_response(Payload(self.request.body)) + payload = self.response_provider.response_for_url_and_headers(self.request.uri, self.headers) + if payload.response_code() >= 300: + self.dispatch_response(Payload(self.request.body)) + else: + self.dispatch_response(payload) @staticmethod