From baa552445116f11157a81a4176e3996a3cf6e740 Mon Sep 17 00:00:00 2001 From: Stephane Janel Date: Wed, 27 Mar 2024 21:27:54 +0100 Subject: [PATCH] Do not throw exception when json parsing fails for unstable APIs --- src/api/common/src/fiatconverter.cpp | 7 +++++- src/api/common/src/withdrawalfees-crawler.cpp | 5 ++-- src/objects/include/monetaryamount.hpp | 1 + src/objects/test/monetaryamount_test.cpp | 1 + src/tech/include/cachedresult.hpp | 23 ++++++++----------- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/api/common/src/fiatconverter.cpp b/src/api/common/src/fiatconverter.cpp index 779aff42..75f4e29e 100644 --- a/src/api/common/src/fiatconverter.cpp +++ b/src/api/common/src/fiatconverter.cpp @@ -122,7 +122,12 @@ std::optional FiatConverter::queryCurrencyRateSource1(Market mk) { std::optional FiatConverter::queryCurrencyRateSource2(Market mk) { const auto dataStr = _curlHandle2.query("", CurlOptions(HttpRequestType::kGet)); - const json jsonData = json::parse(dataStr); + static constexpr bool kAllowExceptions = false; + const json jsonData = json::parse(dataStr, nullptr, kAllowExceptions); + if (jsonData.is_discarded()) { + log::error("Invalid response received from fiat currency converter service's second source"); + return {}; + } const auto baseIt = jsonData.find("base"); const auto ratesIt = jsonData.find("rates"); if (baseIt == jsonData.end() || ratesIt == jsonData.end()) { diff --git a/src/api/common/src/withdrawalfees-crawler.cpp b/src/api/common/src/withdrawalfees-crawler.cpp index 20b41654..4504040b 100644 --- a/src/api/common/src/withdrawalfees-crawler.cpp +++ b/src/api/common/src/withdrawalfees-crawler.cpp @@ -131,9 +131,10 @@ WithdrawalFeesCrawler::WithdrawalInfoMaps WithdrawalFeesCrawler::WithdrawalFeesF WithdrawalInfoMaps ret; if (!withdrawalFeesCsv.empty()) { - const json jsonData = json::parse(withdrawalFeesCsv); + static constexpr bool kAllowExceptions = false; + const json jsonData = json::parse(withdrawalFeesCsv, nullptr, kAllowExceptions); const auto exchangesIt = jsonData.find("exchange"); - if (exchangesIt == jsonData.end()) { + if (jsonData.is_discarded() || exchangesIt == jsonData.end()) { log::error("no exchange data found in source 1 - either site information unavailable or code to be updated"); return ret; } diff --git a/src/objects/include/monetaryamount.hpp b/src/objects/include/monetaryamount.hpp index be6c036d..c1161dca 100644 --- a/src/objects/include/monetaryamount.hpp +++ b/src/objects/include/monetaryamount.hpp @@ -92,6 +92,7 @@ class MonetaryAmount { /// Constructs a new MonetaryAmount from a string representing the amount only and a currency code. /// Precision is calculated automatically. + /// If 'amountStr' is empty, the amount will be set to 0. MonetaryAmount(std::string_view amountStr, CurrencyCode currencyCode); /// Constructs a new MonetaryAmount from another MonetaryAmount and a new CurrencyCode. diff --git a/src/objects/test/monetaryamount_test.cpp b/src/objects/test/monetaryamount_test.cpp index bb801935..015010c9 100644 --- a/src/objects/test/monetaryamount_test.cpp +++ b/src/objects/test/monetaryamount_test.cpp @@ -261,6 +261,7 @@ TEST(MonetaryAmountTest, StringConstructor) { EXPECT_EQ(MonetaryAmount("05AUD"), MonetaryAmount(5, "AUD")); EXPECT_EQ(MonetaryAmount("746REPV2"), MonetaryAmount("746", "REPV2")); + EXPECT_EQ(MonetaryAmount(""), MonetaryAmount()); EXPECT_EQ(MonetaryAmount("", "EUR"), MonetaryAmount(0, "EUR")); EXPECT_THROW(MonetaryAmount("usdt"), invalid_argument); diff --git a/src/tech/include/cachedresult.hpp b/src/tech/include/cachedresult.hpp index 214f0a40..ccabbac5 100644 --- a/src/tech/include/cachedresult.hpp +++ b/src/tech/include/cachedresult.hpp @@ -54,22 +54,14 @@ class CachedResultT : public CachedResultBase { } } - CachedResultT(const CachedResultT &) = delete; - CachedResultT &operator=(const CachedResultT &) = delete; - - CachedResultT(CachedResultT &&) noexcept = default; - CachedResultT &operator=(CachedResultT &&) noexcept = default; - - ~CachedResultT() = default; - /// Sets given value associated to the key built with given parameters, /// if given timestamp is more recent than the one associated to the value already present at this key (if any) template - void set(ResultTypeT &&val, TimePoint t, Args &&...funcArgs) { - auto [it, inserted] = - _cachedResultsMap.try_emplace(TKey(std::forward(funcArgs)...), std::forward(val), t); - if (!inserted && it->second.second < t) { - it->second = TValue(std::forward(val), t); + void set(ResultTypeT &&val, TimePoint timePoint, Args &&...funcArgs) { + auto [it, inserted] = _cachedResultsMap.try_emplace(TKey(std::forward(funcArgs)...), + std::forward(val), timePoint); + if (!inserted && it->second.second < timePoint) { + it->second = TValue(std::forward(val), timePoint); } } @@ -78,12 +70,16 @@ class CachedResultT : public CachedResultBase { template const ResultType &get(Args &&...funcArgs) { TKey key(std::forward(funcArgs)...); + auto nowTime = ClockT::now(); + auto flattenTuple = [this](auto &&...values) { return _func(std::forward(values)...); }; + if (this->_state == State::kForceUniqueRefresh) { _cachedResultsMap.clear(); this->_state = State::kForceCache; } + auto it = _cachedResultsMap.find(key); if (it == _cachedResultsMap.end()) { TValue val(std::apply(flattenTuple, key), nowTime); @@ -91,6 +87,7 @@ class CachedResultT : public CachedResultBase { } else if (this->_state != State::kForceCache && this->_refreshPeriod < nowTime - it->second.second) { it->second = TValue(std::apply(flattenTuple, std::move(key)), nowTime); } + return it->second.first; }