summaryrefslogtreecommitdiff
path: root/subprojects
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects')
-rw-r--r--subprojects/battleNetApi/CMakeLists.txt25
-rw-r--r--subprojects/battleNetApi/include/apiUrl.hpp19
-rw-r--r--subprojects/battleNetApi/include/hearthstoneApi.hpp43
-rw-r--r--subprojects/battleNetApi/include/token.hpp25
-rw-r--r--subprojects/battleNetApi/src/apiUrl.cpp40
-rw-r--r--subprojects/battleNetApi/src/hearthstoneApi.cpp40
-rw-r--r--subprojects/battleNetApi/src/token.cpp105
-rw-r--r--subprojects/battleNetApi/tests/tests.cpp65
8 files changed, 0 insertions, 362 deletions
diff --git a/subprojects/battleNetApi/CMakeLists.txt b/subprojects/battleNetApi/CMakeLists.txt
deleted file mode 100644
index b4f6a48..0000000
--- a/subprojects/battleNetApi/CMakeLists.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-cmake_minimum_required(VERSION 3.15)
-
-get_filename_component(FNAME ${CMAKE_SOURCE_DIR} NAME)
-set(ProjectName ${FNAME})
-
-project(${ProjectName} LANGUAGES CXX)
-
-set(CMAKE_COMPILE_COMMANDS ON)
-set(CMAKE_CXX_STANDARD 20)
-set(CXX_STANDARD_REQUIRED ON)
-set(CMAKE_BUILD_TYPE Debug)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 -Wall")
-set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
-add_definitions(-DSOURCE_DIR_PATH="${CMAKE_SOURCE_DIR}")
-
-set(INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
-set(SRC
- ${PROJECT_SOURCE_DIR}/src/hearthstoneApi.cpp
- ${PROJECT_SOURCE_DIR}/src/token.cpp ${PROJECT_SOURCE_DIR}/src/apiUrl.cpp
- ${PROJECT_SOURCE_DIR}/tests/tests.cpp)
-
-find_package(Catch2 3 REQUIRED)
-add_executable(tests ${SRC})
-target_include_directories(tests PRIVATE ${INCLUDE_DIR})
-target_link_libraries(tests PRIVATE Catch2::Catch2WithMain curl)
diff --git a/subprojects/battleNetApi/include/apiUrl.hpp b/subprojects/battleNetApi/include/apiUrl.hpp
deleted file mode 100644
index 509b248..0000000
--- a/subprojects/battleNetApi/include/apiUrl.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-#include <string>
-#include <unordered_map>
-
-class ApiUrl {
- public:
- ApiUrl(const std::string &iContentType, const std::string &iRegion);
-
- void setRegion(const std::string &iRegion);
- void setLocale(const std::string &iLocale);
- void setGameMode(const std::string &iGameMode);
- void setPageSize(const std::string &iPageSize);
- std::string operator()() const;
-
- private:
- std::string mContentType;
- std::string mRegion;
- std::unordered_map<std::string, std::string> aParameters;
-};
diff --git a/subprojects/battleNetApi/include/hearthstoneApi.hpp b/subprojects/battleNetApi/include/hearthstoneApi.hpp
deleted file mode 100644
index c11acc7..0000000
--- a/subprojects/battleNetApi/include/hearthstoneApi.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#pragma once
-#include "token.hpp"
-#include <iostream>
-#include <regex>
-#include <string>
-
-class HearthstoneApi {
- public:
- HearthstoneApi(Token &&iToken);
- 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
-
- friend class HearthstoneApiMock;
-};
diff --git a/subprojects/battleNetApi/include/token.hpp b/subprojects/battleNetApi/include/token.hpp
deleted file mode 100644
index e2e3088..0000000
--- a/subprojects/battleNetApi/include/token.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include <chrono>
-#include <string>
-
-class Token {
- public:
- Token(const std::string &iTokenFilePath, const std::string &iUsername, const std::string &iPassword);
- std::string toString() const;
-
- private:
- const std::string mFilePath; //!< Path to the token file
- const std::string mUsername; //!< Username for Battle.net API
- const std::string mPassword; //!< Password for Battle.net API
- const std::string mToken; //!< Token value
-
- std::string getValidToken() const;
- void refreshToken() const;
- void writeNewTokenToFile() const;
- void writeTokenToFile(const std::string &iToken) const;
- std::string getValidTokenWithApi() const;
- std::chrono::seconds getCurrentTime() const;
- std::string extractTokenWithRegex(const std::string &iText, const std::string &iRegex) const;
- static size_t curlCallbackStringOutput(void *iPtr, size_t iSize, size_t iNmemb, std::string *iUserData);
-
- friend class TokenTests;
-};
diff --git a/subprojects/battleNetApi/src/apiUrl.cpp b/subprojects/battleNetApi/src/apiUrl.cpp
deleted file mode 100644
index 3de13cd..0000000
--- a/subprojects/battleNetApi/src/apiUrl.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "apiUrl.hpp"
-#include <format>
-
-ApiUrl::ApiUrl(const std::string &iContentType, const std::string &iRegion)
- : mContentType{iContentType}, mRegion{iRegion} {}
-void ApiUrl::setRegion(const std::string &iRegion) {
- if (not iRegion.empty()) {
- mRegion = iRegion; //!< The region of the data to receive, e.g. "eu"
- }
-}
-void ApiUrl::setLocale(const std::string &iLocale) {
- 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) {
- 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()() const {
- std::string aFormattedParameters;
- for (const auto &[key, value] : aParameters) {
- aFormattedParameters += std::format("&{}={}", key, value);
- }
- if (not aFormattedParameters.empty()) {
- aFormattedParameters[0] = '?';
- }
- aFormattedParameters =
- std::format("https://{}.api.blizzard.com/hearthstone/{}/{}", mRegion, mContentType, aFormattedParameters);
- return aFormattedParameters;
-}
diff --git a/subprojects/battleNetApi/src/hearthstoneApi.cpp b/subprojects/battleNetApi/src/hearthstoneApi.cpp
deleted file mode 100644
index c34e6ad..0000000
--- a/subprojects/battleNetApi/src/hearthstoneApi.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "hearthstoneApi.hpp"
-#include <curl/curl.h>
-#include <format>
-
-HearthstoneApi::HearthstoneApi(Token &&iToken) : mToken(iToken), mAuthorizationHeader(authorizationHeader()) {}
-
-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) {
- const size_t kNewLength = iSize * iNmemb;
- try {
- iS->append(static_cast<char *>(iContent), kNewLength);
- } catch (std::bad_alloc &e) {
- // handle memory problem
- return 0;
- }
- return kNewLength;
-}
-
-std::string HearthstoneApi::makeApiRequest(const std::string &iUrl) const {
- CURL *curl = curl_easy_init();
- std::string request_output;
- if (curl) {
- curl_easy_setopt(curl, CURLOPT_URL, iUrl.c_str());
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlCallbackStringOutput);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &request_output);
- struct curl_slist *chunk = nullptr;
- chunk = curl_slist_append(chunk, mAuthorizationHeader.c_str());
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
- curl_easy_perform(curl);
- curl_easy_cleanup(curl);
- }
- return request_output;
-}
diff --git a/subprojects/battleNetApi/src/token.cpp b/subprojects/battleNetApi/src/token.cpp
deleted file mode 100644
index 89f0d2a..0000000
--- a/subprojects/battleNetApi/src/token.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-#include "token.hpp"
-#include <charconv>
-#include <chrono>
-#include <curl/curl.h>
-#include <format>
-#include <fstream>
-#include <iostream>
-#include <regex>
-#include <system_error>
-
-Token::Token(const std::string &iTokenFilePath, const std::string &iUsername, const std::string &iPassword)
- : mFilePath{iTokenFilePath}, mUsername{iUsername}, mPassword{iPassword}, mToken{getValidToken()} {}
-
-std::string Token::toString() const { return mToken; }
-
-std::string Token::getValidToken() const {
- refreshToken();
- std::ifstream aTokenFile{mFilePath};
- std::string aLine;
- std::getline(aTokenFile, aLine);
- const std::string kToken{aLine.substr(0, aLine.find(" "))};
- return kToken;
-}
-
-void Token::refreshToken() const {
- std::ifstream aTokenFile{mFilePath};
- std::string aLine;
- std::getline(aTokenFile, aLine);
- if (aLine.empty()) {
- writeNewTokenToFile();
- } else {
- const std::string kTimeLimitText{aLine.substr(aLine.find(" ") + 1)};
- long int aTimeLimit;
- const auto kConversionStatus{
- std::from_chars(kTimeLimitText.data(), kTimeLimitText.data() + kTimeLimitText.size(), aTimeLimit)};
- if (kConversionStatus.ec == std::errc{}) {
- const bool kTokenExpired{
- std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now().time_since_epoch())
- .count() >= aTimeLimit};
- if (kTokenExpired) {
- writeNewTokenToFile();
- }
- }
- }
-}
-
-void Token::writeNewTokenToFile() const {
- const std::string kToken{getValidTokenWithApi()};
- if (not kToken.empty()) {
- writeTokenToFile(kToken);
- }
-}
-
-void Token::writeTokenToFile(const std::string &iToken) const {
- std::ofstream aTokenFile{mFilePath};
- std::chrono::seconds aExpiresAt{getCurrentTime() + std::chrono::hours(24)};
- aTokenFile << iToken << " " << aExpiresAt.count() << '\n';
-}
-
-std::string Token::getValidTokenWithApi() const {
- CURL *aCurl{curl_easy_init()};
- std::string aTokenInfo;
- if (aCurl) {
- curl_easy_setopt(aCurl, CURLOPT_URL, "https://oauth.battle.net/token");
- curl_easy_setopt(aCurl, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(aCurl, CURLOPT_SSL_VERIFYHOST, 0L);
- curl_easy_setopt(aCurl, CURLOPT_WRITEFUNCTION, curlCallbackStringOutput);
- curl_easy_setopt(aCurl, CURLOPT_WRITEDATA, &aTokenInfo);
- curl_easy_setopt(aCurl, CURLOPT_USERPWD, std::format("{}:{}", mUsername, mPassword).data());
- curl_easy_setopt(aCurl, CURLOPT_POSTFIELDS, "grant_type=client_credentials");
- CURLcode result_code{curl_easy_perform(aCurl)};
- curl_easy_cleanup(aCurl);
- if (result_code != CURLE_OK) {
- std::cerr << "ERROR: token request failed" << std::endl;
- std::exit(EXIT_FAILURE);
- }
- }
-
- return extractTokenWithRegex(aTokenInfo, "\"access_token\":\"(\\w+)\"");
-}
-
-std::chrono::seconds Token::getCurrentTime() const {
- return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now().time_since_epoch());
-}
-
-std::string Token::extractTokenWithRegex(const std::string &iText, const std::string &iRegex) const {
- const std::regex kRegex{iRegex};
- std::smatch aMatches;
- if (std::regex_search(iText, aMatches, kRegex)) {
- return aMatches[1].str();
- }
- std::cerr << std::format("Unable to retrieve token with regex '{}'", iRegex);
- return "";
-}
-
-size_t Token::curlCallbackStringOutput(void *iPtr, size_t iSize, size_t iNmemb, std::string *iUserData) {
- size_t newLength{iSize * iNmemb};
- try {
- iUserData->append(static_cast<char *>(iPtr), newLength);
- } catch (std::bad_alloc &e) {
- // handle memory problem
- return 0;
- }
- return newLength;
-}
diff --git a/subprojects/battleNetApi/tests/tests.cpp b/subprojects/battleNetApi/tests/tests.cpp
deleted file mode 100644
index 74845c5..0000000
--- a/subprojects/battleNetApi/tests/tests.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#include "apiUrl.hpp"
-#include "hearthstoneApi.hpp"
-#include <catch2/catch_test_macros.hpp>
-#include <iostream>
-#include <utility>
-
-class TokenMock {
- public:
- TokenMock()
- : mTokenFilePath{"resources/token_info"}, mUsername{"2b50f1123b424d5e9c21971a110fdd04"},
- mPassword{"MrMkhVMNdOeqPL4Q4XVQAsRPR14J7Yq6"}, mToken{mTokenFilePath, mUsername, mPassword} {}
- std::string toString() const { return mToken.toString(); }
- Token &getToken() { return mToken; }
-
- private:
- const std::string mTokenFilePath;
- const std::string mUsername;
- const std::string mPassword;
- Token mToken;
-};
-
-class HearthstoneApiMock {
- public:
- HearthstoneApiMock() : mHearthstoneApi(std::move(mToken.getToken())) {};
- 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;
- const HearthstoneApi mHearthstoneApi;
-};
-
-TEST_CASE("Retrieve API Token", "[getApiToken]") {
- TokenMock aToken;
- const std::string kTokenContent{aToken.toString()};
- const std::string::size_type kTokenSize{kTokenContent.size()};
- REQUIRE(kTokenSize >= 30);
- REQUIRE(kTokenSize <= 40);
-}
-
-TEST_CASE("Retrieve number of cards", "[getCardsCount]") {
- HearthstoneApiMock aHearthstoneApi;
- 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()))};
- REQUIRE(kNumberOfCards == 5991);
-}
-
-TEST_CASE("Retrieve cards", "[retrieveCards]") {
- HearthstoneApiMock aHearthstoneApi;
- const std::string kContentType{"cards"};
- const std::string kRegion{"eu"};
- ApiUrl aApiUrl(kContentType, kRegion);
- aApiUrl.setLocale("fr_FR");
- aApiUrl.setGameMode("constructed");
- aApiUrl.setPageSize("10");
- const std::string kApiResponse{aHearthstoneApi.makeApiRequest(aApiUrl())};
- REQUIRE(kApiResponse.size() > 300);
-}