From 658f37f93d1a0c31c447adb3022cfd6cee4a1876 Mon Sep 17 00:00:00 2001 From: temomik Date: Fri, 1 Dec 2023 20:16:03 +0000 Subject: [PATCH] Added ability to handle error codes from all Api requests --- include/tgbot/Api.h | 2 +- include/tgbot/TgException.h | 16 +++++++++- src/Api.cpp | 25 ++++++++++----- src/TgException.cpp | 4 ++- test/CMakeLists.txt | 1 + test/tgbot/Api.cpp | 64 +++++++++++++++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 test/tgbot/Api.cpp diff --git a/include/tgbot/Api.h b/include/tgbot/Api.h index 7db68e724..6bd24e42d 100644 --- a/include/tgbot/Api.h +++ b/include/tgbot/Api.h @@ -1989,7 +1989,7 @@ friend class Bot; const HttpClient& _httpClient; -private: +protected: boost::property_tree::ptree sendRequest(const std::string& method, const std::vector& args = std::vector()) const; const std::string _token; diff --git a/include/tgbot/TgException.h b/include/tgbot/TgException.h index e80711cbc..0fbda9391 100644 --- a/include/tgbot/TgException.h +++ b/include/tgbot/TgException.h @@ -16,7 +16,21 @@ namespace TgBot { class TGBOT_API TgException : public std::runtime_error { public: - explicit TgException(const std::string& description); + + /** + * @brief Enum of possible errors from Api requests + */ + enum class ErrorCode : size_t { + Undefined = 0, + BadRequest = 400, Unauthorized = 401, + Forbidden = 403, NotFound = 404, + Flood = 402, Internal = 500, + HtmlResponse = 100, InvalidJson = 101 + }; + + explicit TgException(const std::string& description, ErrorCode errorCode); + + const ErrorCode errorCode; }; } diff --git a/src/Api.cpp b/src/Api.cpp index f238a70f1..f3f812458 100644 --- a/src/Api.cpp +++ b/src/Api.cpp @@ -2513,19 +2513,28 @@ boost::property_tree::ptree Api::sendRequest(const std::string& method, const st { try { std::string serverResponse = _httpClient.makeRequest(url, args); + if (!serverResponse.compare(0, 6, "")) { - throw TgException("tgbot-cpp library have got html page instead of json response. Maybe you entered wrong bot token."); + std::string message = "tgbot-cpp library have got html page instead of json response. Maybe you entered wrong bot token."; + throw TgException(message, TgException::ErrorCode::HtmlResponse); } - boost::property_tree::ptree result = _tgTypeParser.parseJson(serverResponse); + boost::property_tree::ptree result; try { - if (result.get("ok", false)) { - return result.get_child("result"); - } else { - throw TgException(result.get("description", "")); - } + result = _tgTypeParser.parseJson(serverResponse); } catch (boost::property_tree::ptree_error& e) { - throw TgException("tgbot-cpp library can't parse json response. " + std::string(e.what())); + std::string message = "tgbot-cpp library can't parse json response. " + std::string(e.what()); + + throw TgException(message, TgException::ErrorCode::InvalidJson); + } + + if (result.get("ok", false)) { + return result.get_child("result"); + } else { + std::string message = result.get("description", ""); + size_t errorCode = result.get("error_code", 0u); + + throw TgException(message, static_cast(errorCode)); } } catch (...) { int max_retries = _httpClient.getRequestMaxRetries(); diff --git a/src/TgException.cpp b/src/TgException.cpp index e61e09cb0..7164ddce5 100644 --- a/src/TgException.cpp +++ b/src/TgException.cpp @@ -4,7 +4,9 @@ namespace TgBot { -TgBot::TgException::TgException(const std::string& description) : runtime_error(description) { +TgException::TgException(const std::string& description, ErrorCode errorCode) + : runtime_error(description), errorCode(errorCode) +{ } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0cce2366b..899035f80 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,6 @@ set(TEST_SRC_LIST main.cpp + tgbot/Api.cpp tgbot/net/Url.cpp tgbot/net/HttpParser.cpp tgbot/tools/StringTools.cpp diff --git a/test/tgbot/Api.cpp b/test/tgbot/Api.cpp new file mode 100644 index 000000000..257586590 --- /dev/null +++ b/test/tgbot/Api.cpp @@ -0,0 +1,64 @@ +#include +#include + +#include + +#include "tgbot/net/HttpClient.h" +#include "tgbot/Api.h" +#include "tgbot/TgException.h" + +using namespace std; +using namespace TgBot; + +typedef TgException::ErrorCode TgErrorCode; + +class TestableApi : public Api { +public: + using Api::Api; + using Api::sendRequest; +}; + +class HttpClientMock : public HttpClient { +public: + std::string makeRequest(const Url& url, const std::vector& args) const override + {return response;}; + + int getRequestMaxRetries() const override { return 0;}; + int getRequestBackoff() const override {return 1;}; + + string response; +}; + +bool Request(TgErrorCode expectedCode, const string& response) { + HttpClientMock httpClientMock; + httpClientMock.response = response; + + TestableApi api("token", httpClientMock, "url"); + + try { + api.sendRequest("", vector()); + } catch (TgException& exception) { + return exception.errorCode == expectedCode; + } + + return false; +} + +BOOST_AUTO_TEST_SUITE(tApi) + +BOOST_AUTO_TEST_CASE(sendRequest) { + BOOST_CHECK(Request(TgErrorCode::HtmlResponse, "")); + BOOST_CHECK(Request(TgErrorCode::Undefined, "{\"ok\": false}")); + BOOST_CHECK(Request(TgErrorCode::Undefined, "{\"ok\": false, \"error_code\":0}")); + + BOOST_CHECK(Request(TgErrorCode::BadRequest, "{\"ok\": false, \"error_code\":400}")); + BOOST_CHECK(Request(TgErrorCode::Unauthorized, "{\"ok\": false, \"error_code\":401}")); + BOOST_CHECK(Request(TgErrorCode::Forbidden, "{\"ok\": false, \"error_code\":403}")); + BOOST_CHECK(Request(TgErrorCode::NotFound, "{\"ok\": false, \"error_code\":404}")); + BOOST_CHECK(Request(TgErrorCode::Flood, "{\"ok\": false, \"error_code\":402}")); + BOOST_CHECK(Request(TgErrorCode::Internal, "{\"ok\": false, \"error_code\":500}")); + + BOOST_CHECK(Request(TgErrorCode::InvalidJson, "error_code:101")); +} + +BOOST_AUTO_TEST_SUITE_END()