Compare commits
5 Commits
703d952ad3
...
a441bbdc3a
Author | SHA1 | Date | |
---|---|---|---|
a441bbdc3a | |||
f42ca7f92e | |||
360879521a | |||
5514c15f12 | |||
4adbf858a9 |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
build
|
||||||
|
.cache
|
||||||
|
compile_commands.json
|
28
README.md
28
README.md
@ -5,6 +5,34 @@ software [Redmine](https://redmine.org).
|
|||||||
|
|
||||||
It also provides a small CLI application to interact with a remine server through its API.
|
It also provides a small CLI application to interact with a remine server through its API.
|
||||||
|
|
||||||
|
## Supported API
|
||||||
|
|
||||||
|
| Resource | Method | Supported |
|
||||||
|
|---|---|---|
|
||||||
|
|Issue | * | no |
|
||||||
|
|Projects | * | no |
|
||||||
|
|Project Membership | * | no |
|
||||||
|
|Users | * | no|
|
||||||
|
|Time Entries | * | no |
|
||||||
|
|News | * | no |
|
||||||
|
|Issue Relation | * | no |
|
||||||
|
|Versions | * | no |
|
||||||
|
|Wiki Pages | * | no |
|
||||||
|
|Queries | * | no |
|
||||||
|
|Attachments | * | no |
|
||||||
|
|Issue Statuses | * | no |
|
||||||
|
|Trackers| * | no |
|
||||||
|
|Enumerations | * | no |
|
||||||
|
|Issue Categories | * | no |
|
||||||
|
|Roles | * | no |
|
||||||
|
|Groups | * | no |
|
||||||
|
|Custom Fields | * | no |
|
||||||
|
|Search | * | no |
|
||||||
|
|Files | * | no |
|
||||||
|
|My Account | * | yes |
|
||||||
|
|Journals | * | no |
|
||||||
|
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
- Dominik Meyer <dmeyer@federationhq.de>
|
- Dominik Meyer <dmeyer@federationhq.de>
|
||||||
|
|
||||||
|
23
docs/Doxyfile.in
Normal file
23
docs/Doxyfile.in
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
DOXYFILE_ENCODING = UTF-8
|
||||||
|
PROJECT_NAME = @PROJECT_NAME@
|
||||||
|
PROJECT_NUMBER = "@GIT_BRANCH@ @GIT_COMMIT_HASH@"
|
||||||
|
PROJECT_BRIEF = "C++ library implementing the redmine api"
|
||||||
|
OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/doxygen/
|
||||||
|
OUTPUT_LANGUAGE = English
|
||||||
|
BRIEF_MEMBER_DESC = YES
|
||||||
|
REPEAT_BRIEF = YES
|
||||||
|
ALWAYS_DETAILED_SEC = NO
|
||||||
|
INLINE_INHERITED_MEMB = NO
|
||||||
|
FULL_PATH_NAMES = NO
|
||||||
|
MULTILINE_CPP_IS_BRIEF = NO
|
||||||
|
TAB_SIZE = 4
|
||||||
|
MARKDOWN_SUPPORT = YES
|
||||||
|
GENERATE_TODOLIST = YES
|
||||||
|
GENERATE_TESTLIST = YES
|
||||||
|
GENERATE_BUGLIST = YES
|
||||||
|
GENERATE_DEPRECATEDLIST= YES
|
||||||
|
INPUT = @CMAKE_CURRENT_SOURCE_DIR@/README.md @CMAKE_CURRENT_SOURCE_DIR@/src/ @CMAKE_CURRENT_SOURCE_DIR@/tests/ @CMAKE_CURRENT_SOURCE_DIR@/docs @CMAKE_CURRENT_SOURCE_DIR@/include
|
||||||
|
INPUT_ENCODING = UTF-8
|
||||||
|
FILE_PATTERNS = *.hpp *.cpp *.md
|
||||||
|
USE_MDFILE_AS_MAINPAGE = README.md
|
||||||
|
RECURSIVE = YES
|
107
include/Redmine/API.hpp
Normal file
107
include/Redmine/API.hpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "nlohmann/json_fwd.hpp"
|
||||||
|
#include <httplib.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <Redmine/User.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
|
||||||
|
/*@}*/
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
150
src/Redmine/API.cpp
Normal file
150
src/Redmine/API.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "nlohmann/json_fwd.hpp"
|
||||||
|
#include <Redmine/API.hpp>
|
||||||
|
#include <Redmine/Exception/Api.hpp>
|
||||||
|
#include <exception>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
#define LOGURU_WITH_STREAMS 1
|
||||||
|
#include <loguru.hpp>
|
||||||
|
#include <httplib.h>
|
||||||
|
|
||||||
|
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 " <<redmineApiURL_;
|
||||||
|
httplib::Client client{redmineApiURL_};
|
||||||
|
|
||||||
|
httplib::Headers headers =
|
||||||
|
{
|
||||||
|
{ "X-Redmine-API-Key", authToken_ },
|
||||||
|
{ "Content-Type", "application/json"}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto res = client.Get(path + ".json" , headers);
|
||||||
|
|
||||||
|
if (res->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 " <<redmineApiURL_;
|
||||||
|
httplib::Client client{redmineApiURL_};
|
||||||
|
client.set_basic_auth(authToken_, "");
|
||||||
|
|
||||||
|
auto res = client.Put(path + ".json", data.dump(),"application/json");
|
||||||
|
|
||||||
|
if (res->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<std::string> errors=error["errors"].get<std::vector<std::string>>();
|
||||||
|
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;
|
||||||
|
}
|
28
src/config.hpp.in
Normal file
28
src/config.hpp.in
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef __LIBCMS_CONFIG_HPP__
|
||||||
|
#define __LIBCMS_CONFIG_HPP__
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#define PROJECT_NAME @PROJECT_NAME@
|
||||||
|
|
||||||
|
#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||||
|
#define VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||||
|
#define VERSION_PATCH @PROJECT_VERSION_PATCH@
|
||||||
|
#define PROJECT_VERSION "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@"
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user