Skip to content

Commit

Permalink
Added ability to handle error codes from all Api requests
Browse files Browse the repository at this point in the history
  • Loading branch information
Temomik committed Dec 1, 2023
1 parent 7825962 commit 658f37f
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 11 deletions.
2 changes: 1 addition & 1 deletion include/tgbot/Api.h
Original file line number Diff line number Diff line change
Expand Up @@ -1989,7 +1989,7 @@ friend class Bot;

const HttpClient& _httpClient;

private:
protected:
boost::property_tree::ptree sendRequest(const std::string& method, const std::vector<HttpReqArg>& args = std::vector<HttpReqArg>()) const;

const std::string _token;
Expand Down
16 changes: 15 additions & 1 deletion include/tgbot/TgException.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

}
Expand Down
25 changes: 17 additions & 8 deletions src/Api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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, "<html>")) {
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<bool>("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<bool>("ok", false)) {
return result.get_child("result");
} else {
std::string message = result.get("description", "");
size_t errorCode = result.get<size_t>("error_code", 0u);

throw TgException(message, static_cast<TgException::ErrorCode>(errorCode));
}
} catch (...) {
int max_retries = _httpClient.getRequestMaxRetries();
Expand Down
4 changes: 3 additions & 1 deletion src/TgException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
}

}
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
64 changes: 64 additions & 0 deletions test/tgbot/Api.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include <string>
#include <vector>

#include <boost/test/unit_test.hpp>

#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<HttpReqArg>& 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<HttpReqArg>());
} catch (TgException& exception) {
return exception.errorCode == expectedCode;
}

return false;
}

BOOST_AUTO_TEST_SUITE(tApi)

BOOST_AUTO_TEST_CASE(sendRequest) {
BOOST_CHECK(Request(TgErrorCode::HtmlResponse, "<html>"));
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()

0 comments on commit 658f37f

Please sign in to comment.