diff options
| author | Aotrix <[email protected]> | 2025-11-16 21:07:44 +0100 |
|---|---|---|
| committer | Aotrix <[email protected]> | 2025-11-16 21:07:44 +0100 |
| commit | e006ece3289c848ca2975f06386d6a2a51bdb67f (patch) | |
| tree | 1d72ae49b6a14cd43b3dec06ff78a7ae6d628f3c | |
| parent | 832e276e94d4d5a30885e6e352ffdd19f4f16f04 (diff) | |
Refactored JSON value retrieval
| -rw-r--r-- | subprojects/battleNetApi/include/apiUrl.hpp | 3 | ||||
| -rw-r--r-- | subprojects/battleNetApi/include/hearthstoneApi.hpp | 26 | ||||
| -rw-r--r-- | subprojects/battleNetApi/src/apiUrl.cpp | 22 | ||||
| -rw-r--r-- | subprojects/battleNetApi/src/hearthstoneApi.cpp | 7 | ||||
| -rw-r--r-- | subprojects/battleNetApi/tests/tests.cpp | 20 |
5 files changed, 64 insertions, 14 deletions
diff --git a/subprojects/battleNetApi/include/apiUrl.hpp b/subprojects/battleNetApi/include/apiUrl.hpp index 12124c6..509b248 100644 --- a/subprojects/battleNetApi/include/apiUrl.hpp +++ b/subprojects/battleNetApi/include/apiUrl.hpp @@ -9,7 +9,8 @@ class ApiUrl { void setRegion(const std::string &iRegion); void setLocale(const std::string &iLocale); void setGameMode(const std::string &iGameMode); - std::string operator()(); + void setPageSize(const std::string &iPageSize); + std::string operator()() const; private: std::string mContentType; diff --git a/subprojects/battleNetApi/include/hearthstoneApi.hpp b/subprojects/battleNetApi/include/hearthstoneApi.hpp index bbcc8c7..c11acc7 100644 --- a/subprojects/battleNetApi/include/hearthstoneApi.hpp +++ b/subprojects/battleNetApi/include/hearthstoneApi.hpp @@ -1,17 +1,41 @@ #pragma once #include "token.hpp" +#include <iostream> +#include <regex> #include <string> class HearthstoneApi { public: HearthstoneApi(Token &&iToken); - std::uint64_t numberOfCards(); + std::uint64_t numberOfCards(const std::string &iApiResponse) const; private: std::string authorizationHeader(); std::string makeApiRequest(const std::string &iUrl) const; static size_t curlCallbackStringOutput(void *iContent, size_t iSize, size_t iNmemb, std::string *iS); + /** + * @brief + * @param[in] iJsonContent The std::string which contains Json (Battle.net API response for instance) + * @param[in] iKeyValueRegex The regex used to retrieve value from (key, value) pair + * An example: "\"cardCount\":(\\d+)" + */ + template <typename T> T valueInJson(const std::string &iJsonContent, const std::string &iKeyValueRegex) const { + const std::regex iRegex{iKeyValueRegex}; + std::smatch aMatches; + if (std::regex_search(iJsonContent, aMatches, iRegex)) { + const std::string kValueString{aMatches[1].str()}; + T aValue; + const auto kConversionStatus = + std::from_chars(kValueString.data(), kValueString.data() + kValueString.size(), aValue); + if (kConversionStatus.ec == std::errc{}) { + return aValue; + } + } + std::cerr << std::format("Unable to find value with regex: '{}'\n", iKeyValueRegex); + return 0; + } + Token mToken; std::string mAuthorizationHeader; //<! Bearer authorization string for OAuth 2.0 header diff --git a/subprojects/battleNetApi/src/apiUrl.cpp b/subprojects/battleNetApi/src/apiUrl.cpp index e08c8c7..3de13cd 100644 --- a/subprojects/battleNetApi/src/apiUrl.cpp +++ b/subprojects/battleNetApi/src/apiUrl.cpp @@ -4,15 +4,29 @@ ApiUrl::ApiUrl(const std::string &iContentType, const std::string &iRegion) : mContentType{iContentType}, mRegion{iRegion} {} void ApiUrl::setRegion(const std::string &iRegion) { - mRegion = iRegion; //<! eu + if (not iRegion.empty()) { + mRegion = iRegion; //!< The region of the data to receive, e.g. "eu" + } } void ApiUrl::setLocale(const std::string &iLocale) { - aParameters["locale"] = iLocale; //<! fr_FR, ko_KR + if (not iLocale.empty()) { + aParameters["locale"] = iLocale; //!< The locale to reflect in localized data, e.g. "fr_FR" or "ko_KR" + //!< If you do not supply a value, all translations are returned. + } } void ApiUrl::setGameMode(const std::string &iGameMode) { - aParameters["gameMode"] = iGameMode; //<! constructed + if (not iGameMode.empty()) { + aParameters["gameMode"] = + iGameMode; //!< A recognized game mode, e.g. "constructed" + //!< @see https://community.developer.battle.net/documentation/hearthstone/guides/game-modes + } +} +void ApiUrl::setPageSize(const std::string &iPageSize) { + if (not iPageSize.empty()) { + aParameters["pageSize"] = iPageSize; //!< The number of results to choose per page, e.g. "40" + } } -std::string ApiUrl::operator()() { +std::string ApiUrl::operator()() const { std::string aFormattedParameters; for (const auto &[key, value] : aParameters) { aFormattedParameters += std::format("&{}={}", key, value); diff --git a/subprojects/battleNetApi/src/hearthstoneApi.cpp b/subprojects/battleNetApi/src/hearthstoneApi.cpp index b6eaf71..c34e6ad 100644 --- a/subprojects/battleNetApi/src/hearthstoneApi.cpp +++ b/subprojects/battleNetApi/src/hearthstoneApi.cpp @@ -4,11 +4,10 @@ HearthstoneApi::HearthstoneApi(Token &&iToken) : mToken(iToken), mAuthorizationHeader(authorizationHeader()) {} -std::uint64_t HearthstoneApi::numberOfCards() { - // TODO: implement logic - return 123456; +std::uint64_t HearthstoneApi::numberOfCards(const std::string &iApiResponse) const { + const std::string aCardCountRegex{"\"cardCount\":(\\d+)"}; + return valueInJson<std::uint64_t>(iApiResponse, aCardCountRegex); } - std::string HearthstoneApi::authorizationHeader() { return std::format("Authorization: Bearer {}", mToken.toString()); } size_t HearthstoneApi::curlCallbackStringOutput(void *iContent, size_t iSize, size_t iNmemb, std::string *iS) { diff --git a/subprojects/battleNetApi/tests/tests.cpp b/subprojects/battleNetApi/tests/tests.cpp index 368a767..6513301 100644 --- a/subprojects/battleNetApi/tests/tests.cpp +++ b/subprojects/battleNetApi/tests/tests.cpp @@ -22,7 +22,10 @@ class TokenMock { class HearthstoneApiMock { public: HearthstoneApiMock() : mHearthstoneApi(std::move(mToken.getToken())) {}; - std::string makeApiRequest(const std::string &iCardsUrl) { return mHearthstoneApi.makeApiRequest(iCardsUrl); } + std::string makeApiRequest(const std::string &iUrl) { return mHearthstoneApi.makeApiRequest(iUrl); } + std::uint64_t numberOfCards(const std::string &iApiResponse) { + return mHearthstoneApi.numberOfCards(iApiResponse); + }; private: TokenMock mToken; @@ -38,9 +41,16 @@ TEST_CASE("Retrieve API Token", "[getApiToken]") { REQUIRE(kTokenSize <= 40); } -TEST_CASE("Retrieve number of pages", "[getPagesCount]") { +TEST_CASE("Retrieve number of cards", "[getCardsCount]") { HearthstoneApiMock aHearthstoneApi; - // std::cout << aHearthstoneApi.numberOfCards() << '\n'; + const std::string kContentType{"cards"}; + const std::string kRegion{"eu"}; + ApiUrl aApiUrl(kContentType, kRegion); + aApiUrl.setLocale("fr_FR"); + aApiUrl.setGameMode("constructed"); + aApiUrl.setPageSize("1"); + std::uint64_t kNumberOfCards{aHearthstoneApi.numberOfCards(aHearthstoneApi.makeApiRequest(aApiUrl()))}; + std::cout << std::format("Number of cards: {}", kNumberOfCards) << '\n'; } TEST_CASE("Retrieve cards", "[retrieveCards]") { @@ -50,5 +60,7 @@ TEST_CASE("Retrieve cards", "[retrieveCards]") { ApiUrl aApiUrl(kContentType, kRegion); aApiUrl.setLocale("fr_FR"); aApiUrl.setGameMode("constructed"); - std::cout << aHearthstoneApi.makeApiRequest(aApiUrl()) << '\n'; + aApiUrl.setPageSize("10"); + const std::string kApiResponse{aHearthstoneApi.makeApiRequest(aApiUrl())}; + REQUIRE(kApiResponse.size() > 300); } |
