diff --git a/include/Redmine/API.hpp b/include/Redmine/API.hpp new file mode 100644 index 0000000..97918a0 --- /dev/null +++ b/include/Redmine/API.hpp @@ -0,0 +1,107 @@ +#pragma once +/* +* Copyright (C) 2024 Dominik Meyer +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#include "nlohmann/json_fwd.hpp" +#include +#include +#include +#include +#include + +/** + * @brief The main namespace of this library for Redmine related datatypes, classes, and functions + * + */ +namespace Redmine +{ + + /** + * @brief Client side implementation of the redmine API + * + * This implementation of the redmine API always uses the json rest api. + * + */ + class API + { + private: + + /// the URL to the redmine server api + std::string redmineApiURL_; + + /// the token to authenticate against the server in redmine called API Key + std::string authToken_; + + + nlohmann::json get(const std::string &path) const; + void put(const std::string &path, const nlohmann::json &data) const; + + public: + + /** + * @name Constructors + */ + /*@{*/ + /** + * @brief Only Constructor of the Readmine::API t + * + * Only this constructor exists to ensure url and token are available + * + * @param serverURL - full server URL (e.g. https://redmine.de) + * @param authToken - authToken taken from the redmine instance to access the API + */ + API(const std::string &serverURL, const std::string &authToken); + /*@}*/ + + /** + * @name General Methods independent of API + */ + /*@{*/ + /** + * @brief verify that the API is accessible with the provided URL and authToken + * + * @return true - everything is fine and API is accessible + * @return false - something went wrong + */ + bool ready() const; + /*@}*/ + + /** + * @name MyAccount-API + */ + /*@{*/ + + /** + * @brief Return the user information of the owner of the used auth token + * + * @return Redmine::User + */ + Redmine::User getMyAccount() const; + + /** + * @brief Set the user information of the owner of the used auth token + * + * @param user - the used to set with all required information set + */ + void setMyAccount(const Redmine::User &user) const; + + + /*@}*/ + }; + + +} \ No newline at end of file diff --git a/src/Redmine/API.cpp b/src/Redmine/API.cpp new file mode 100644 index 0000000..953e5ba --- /dev/null +++ b/src/Redmine/API.cpp @@ -0,0 +1,150 @@ +/* +* Copyright (C) 2024 Dominik Meyer +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ +#include "nlohmann/json_fwd.hpp" +#include +#include +#include +#include +#include +#include +#define LOGURU_WITH_STREAMS 1 +#include +#include + +Redmine::API::API(const std::string &serverURL, const std::string &authToken) +{ + + if (serverURL.empty() || serverURL.size() < 7) + { + DLOG_S(ERROR) << "server URL not valid"; + + throw std::invalid_argument("server url invalid"); + } + + if (!serverURL.starts_with("https://")) + { + DLOG_S(ERROR) << "server URL not starting with https://"; + + throw std::invalid_argument("server URL does not start with https://"); + } + + redmineApiURL_=serverURL; + + if (authToken.empty() || authToken.length() < 10) + { + DLOG_S(ERROR) << "auth token not valid"; + + throw std::invalid_argument("auth token is not valid"); + } + + + authToken_=authToken; +} + + nlohmann::json Redmine::API::get(const std::string &path) const + { + DLOG_S(INFO) << "getting API endpoint " << path+".json" << " from " <status == httplib::StatusCode::Unauthorized_401) + { + DLOG_S(ERROR) << "authentication with API server failed (401)"; + throw std::runtime_error("authentication failed"); + } + else if (res->status == httplib::StatusCode::NotFound_404) + { + DLOG_S(ERROR) << "unknown API endpoint (404)"; + throw std::runtime_error("unknown API endpoint"); + } + else if (res->status != httplib::StatusCode::OK_200) + { + LOG_S(ERROR) << "unsupported error " << httplib::status_message(res->status); + throw std::runtime_error("unsupported error"); + } + + DLOG_S(INFO) << "get successful"; + return nlohmann::json::parse(res->body); + } + +void Redmine::API::put(const std::string &path, const nlohmann::json &data) const + { + DLOG_S(INFO) << "putting API endpoint " << path+".json" << " from " <status == httplib::StatusCode::Unauthorized_401) + { + DLOG_S(ERROR) << "authentication with API server failed (401)"; + throw std::runtime_error("authentication failed"); + } + else if (res->status == httplib::StatusCode::NotFound_404) + { + DLOG_S(ERROR) << "unknown API endpoint (404)"; + throw std::runtime_error("unknown API endpoint"); + } + else if (res->status == httplib::StatusCode::UnprocessableContent_422) + { + nlohmann::json error=nlohmann::json::parse(res->body); + std::vector errors=error["errors"].get>(); + throw Redmine::Exception::Api("Unprocessible Content",errors); + } + else if (res->status != httplib::StatusCode::OK_200 && res->status != httplib::StatusCode::NoContent_204) + { + LOG_S(ERROR) << "unsupported error " << httplib::status_message(res->status); + LOG_S(ERROR) << res->body; + throw std::runtime_error("unsupported error"); + } + + DLOG_S(INFO) << "put successful"; + } + + +Redmine::User Redmine::API::getMyAccount() const +{ + nlohmann::json userInfo = get("/my/account"); + + return Redmine::User{userInfo}; +} + +void Redmine::API::setMyAccount(const Redmine::User &user) const +{ + + put("/my/account", user.get()); + +} + + +bool Redmine::API::ready() const +{ + try { + getMyAccount(); + } catch (std::exception &e) + { + return false; + } + return true; +} \ No newline at end of file