feat: some last changes
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Dominik Meyer 2024-10-09 15:20:32 +02:00
parent 3e508c344f
commit 7f269901c5
Signed by: byterazor
GPG Key ID: EABDA0FD5981BC97
15 changed files with 575 additions and 16 deletions

View File

@ -85,6 +85,12 @@ SET(REDMINE_API_SOURCES
include/Redmine/API.hpp include/Redmine/API.hpp
src/Redmine/API.cpp src/Redmine/API.cpp
include/Redmine/Filter.hpp
src/Redmine/Filter.cpp
include/Redmine/Issue.hpp
src/Redmine/Issue.cpp
) )
add_library(redmine-api-cpp-objlib OBJECT ${REDMINE_API_SOURCES}) add_library(redmine-api-cpp-objlib OBJECT ${REDMINE_API_SOURCES})
@ -129,6 +135,8 @@ add_executable(redmine-cli
src/Redmine-CLI/Command/Project.cpp src/Redmine-CLI/Command/Project.cpp
src/Redmine-CLI/Command/IssueStatus.hpp src/Redmine-CLI/Command/IssueStatus.hpp
src/Redmine-CLI/Command/IssueStatus.cpp src/Redmine-CLI/Command/IssueStatus.cpp
src/Redmine-CLI/Command/Issue.hpp
src/Redmine-CLI/Command/Issue.cpp
) )
target_link_libraries(redmine-cli redmine-api-cpp-static loguru CLI11 tableprinter::tableprinter) target_link_libraries(redmine-cli redmine-api-cpp-static loguru CLI11 tableprinter::tableprinter)
target_include_directories(redmine-cli target_include_directories(redmine-cli

View File

@ -16,6 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "Redmine/Filter.hpp"
#include "Redmine/Issue.hpp"
#include "Redmine/IssueStatus.hpp" #include "Redmine/IssueStatus.hpp"
#include "nlohmann/json_fwd.hpp" #include "nlohmann/json_fwd.hpp"
#include <cstdint> #include <cstdint>
@ -127,9 +129,19 @@ namespace Redmine
* *
* @return std::vector<Redmine::Project> * @return std::vector<Redmine::Project>
*/ */
std::vector<Redmine::Project> getProjects() const; std::vector<Redmine::Project> getProjects(const std::uint32_t limit) const;
void uploadFileToProject(const std::uint64_t projectId, const std::string &filePath, const std::string &fileName, const std::string &description, const std::uint32_t version) const; void uploadFileToProject(const std::uint64_t projectId, const std::string &filePath, const std::string &fileName, const std::string &description, const std::uint32_t version) const;
/**
* @brief Get issues with the given filters from the redmine server
*
* @param filter - the filter to apply on the issues
* @return std::vector<Redmine::Issue>
*/
std::vector<Redmine::Issue> getIssues(const Redmine::Filter& filter = {}) const;
/*@}*/ /*@}*/
}; };

View File

@ -0,0 +1,64 @@
#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/>.
*/
/**
* @brief The main namespace of this library for Redmine related datatypes, classes, and functions
*
*/
#include <cstdint>
#include <string>
#include <vector>
namespace Redmine
{
/**
* @brief Class for filering data in a Redmine API query
*
*/
class Filter
{
private:
/// filter the issued by project id
bool filterByProjectID_;
/// the project ID to filter
std::uint64_t projectID_;
/// possible project stati for a filter
enum class ProjectStatus {open, closed, both};
/// the project status to filter by
ProjectStatus projectStatus_;
/// whether to filter by assigned user or not
bool filterByAssignedUser_;
/// the user ID to filter by
std::uint64_t assignedUserID_;
public:
Filter();
void filterByProjectID(const std::uint64_t id);
void setProjectStatus(const ProjectStatus& status);
void filterByAssignedUserId(const std::uint64_t id);
std::string toQueryString() const;
};
}; // namespace Redmine

View File

@ -63,10 +63,20 @@ namespace Redmine
*/ */
nlohmann::json get() const; nlohmann::json get() const;
/**
* @brief return the id of the issue
*
* @return std::uint64_t
*/
std::uint64_t getId() const; std::uint64_t getId() const;
std::string getProject() const;
std::uint64_t getProjectId() const;
std::string getStatus() const;
std::uint64_t getStatusID() const;
std::string getSubject() const;
std::string getDescription() const;
std::string to_string() const;
}; // class Issue }; // class Issue

View File

@ -0,0 +1,79 @@
#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 <cstdint>
#include <nlohmann/json.hpp>
#include <Redmine/Object.hpp>
#include <string>
/**
* @brief The main namespace of this library for Redmine related datatypes, classes, and functions
*
*/
namespace Redmine
{
/**
* @brief This class represents an Issue Tracker within the redmine server.
*
*/
class Tracker : public Redmine::Object
{
private:
/// the data store for the issue
nlohmann::json data_;
/**
* @brief verify issue data
*
* @param data - the json object containing the issue data
*/
void _verify(const nlohmann::json &data);
public:
explicit Tracker(const nlohmann::json &issue);
/**
* @brief set the issue object from a json object
*
* @param data
*/
void set(const nlohmann::json &data);
/**
* @brief return a json object from the issue object
*
* @return nlohmann::json
*/
nlohmann::json get() const;
/**
* @brief Get the Id of the tracker
*
* @return std::uint64_t
*/
std::uint64_t getId() const;
}; // class Issue
}; // namespace Redmine

View File

@ -0,0 +1,116 @@
/*
* 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 <Redmine-CLI/Command/Issue.hpp>
#include <Redmine-CLI/Redmine.hpp>
#include "Redmine/API.hpp"
#include "Redmine/Filter.hpp"
#include "nlohmann/json_fwd.hpp"
#include <tableprinter/tableprinter.hpp>
Command::Issue::Issue(RedmineCLI::Redmine *r)
:
redmine_(r),
output_(TEXT),
projectId_(-1),
userId_(-1)
{
CLI::App* issue = redmine_->getCLI().add_subcommand("issue",
"Api Calls on issues accessible by token owner")->require_subcommand();
list_ = issue->add_subcommand("list", "List issues accessible by the token owner");
list_->needs(redmine_->getTokenOption())->needs(redmine_->getUrlOption());
list_->callback([&](){getList_();});
list_->add_option("-o,--output", output_, "The Output Format to use. Supported: TEXT, YAML")->transform(CLI::CheckedTransformer(outputMap, CLI::ignore_case));
list_->add_option("-p,--project",projectId_,"Filter issues by project id");
list_->add_option("--userid", userId_,"Filter issues by user id");
}
void Command::Issue::printIssueListText_(const std::vector<Redmine::Issue> &issues) const
{
tableprinter::printer p
{
{
{ tableprinter::name { "ID" } , tableprinter::width { 8 } } ,
{ tableprinter::name { "Subject" } , tableprinter::width { 80 } } ,
{ tableprinter::name { "Project" } , tableprinter::width { 25 } } ,
{tableprinter::name { "Status" }, tableprinter::width { 15 }}
} ,
{ std::cout }
};
p.sanity_check();
p.print_headers();
for (auto it = issues.begin(); it!= issues.end(); ++it)
{
p.print(it->getId(), it->getSubject(), it->getProject(), it->getStatus());
}
}
void Command::Issue::printIssueListJson_(const std::vector<Redmine::Issue> &issues) const
{
nlohmann::json data = nlohmann::json::array();
for (auto it = issues.begin(); it!= issues.end(); ++it)
{
nlohmann::json issue;
issue["id"] = it->getId();
issue["subject"] = it->getSubject();
issue["project"] = it->getProject();
issue["project_id"] = it->getProjectId();
issue["status"] = it->getStatus();
issue["status_id"] = it->getStatusID();
data.push_back(issue);
}
std::cout << data.dump(2);
}
void Command::Issue::getList_() const
{
Redmine::API client{redmine_->getUrl(), redmine_->getToken()};
Redmine::Filter filter{};
if (projectId_ > -1)
{
filter.filterByProjectID(projectId_);
}
if (userId_ > -1)
{
filter.filterByAssignedUserId(userId_);
}
auto issues = client.getIssues(filter);
if (output_ == TEXT)
{
printIssueListText_(issues);
}
else if (output_ == JSON)
{
printIssueListJson_(issues);
}
return;
}

View File

@ -0,0 +1,99 @@
#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 "Redmine/Filter.hpp"
#include "Redmine/Project.hpp"
#include "nlohmann/json_fwd.hpp"
#include <CLI/App.hpp>
#include <Redmine/API.hpp>
#include <atomic>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#define LOGURU_WITH_STREAMS 1
#include <loguru.hpp>
#include <CLI/CLI.hpp>
// forward declaration
namespace RedmineCLI
{
class Redmine;
};
namespace Command
{
/**
* @brief class for processing the CLI11 parsed command lines
*
*/
class Issue
{
private:
/// the main redmine cli application context
RedmineCLI::Redmine *redmine_;
/// enum for identifying supported output types
enum outputType : std::uint16_t
{
/// output is just text
TEXT,
/// output is json
JSON
};
/// requested output type
outputType output_;
std::map<std::string, outputType> outputMap{{"text", TEXT}, {"json", JSON}};
/// vector to hold requested attribute fields
std::vector<std::string> fields_;
/// the project id to work on
std::int64_t projectId_;
/// the userid to filter on
std::uint64_t userId_;
/// the status to filter on
std::string status_;
/// pointer to the list subcommand
CLI::App* list_;
void printIssueListJson_(const std::vector<Redmine::Issue> &issues) const;
void printIssueListText_(const std::vector<Redmine::Issue> &issues) const;
/// the actual implementation of the list command
void getList_() const;
public:
/**
* @brief Construct a new CLIProcessor object
*
*/
explicit Issue(RedmineCLI::Redmine *r);
}; // class Issue
}; // namespace Command

View File

@ -19,12 +19,14 @@
#include <Redmine-CLI/Redmine.hpp> #include <Redmine-CLI/Redmine.hpp>
#include "Redmine/API.hpp" #include "Redmine/API.hpp"
#include "nlohmann/json_fwd.hpp" #include "nlohmann/json_fwd.hpp"
#include <string>
#include <tableprinter/tableprinter.hpp> #include <tableprinter/tableprinter.hpp>
Command::Project::Project(RedmineCLI::Redmine *r) Command::Project::Project(RedmineCLI::Redmine *r)
: :
redmine_(r), redmine_(r),
output_(TEXT) output_(TEXT),
limit_(100)
{ {
CLI::App* project = redmine_->getCLI().add_subcommand("project", CLI::App* project = redmine_->getCLI().add_subcommand("project",
@ -36,6 +38,7 @@ output_(TEXT)
list_->callback([&](){getList_();}); list_->callback([&](){getList_();});
list_->add_option("-f,--fields", fields_, "The fields of the project object to print. Supported: name, id"); list_->add_option("-f,--fields", fields_, "The fields of the project object to print. Supported: name, id");
list_->add_option("-o,--output", output_, "The Output Format to use. Supported: TEXT, YAML")->transform(CLI::CheckedTransformer(outputMap, CLI::ignore_case)); list_->add_option("-o,--output", output_, "The Output Format to use. Supported: TEXT, YAML")->transform(CLI::CheckedTransformer(outputMap, CLI::ignore_case));
list_->add_option("-l,--limit",limit_,"The maximum number of projects to return. (Default: " + std::to_string(limit_) +" )");
upload_ = project->add_subcommand("upload", "Upload a File to the Project"); upload_ = project->add_subcommand("upload", "Upload a File to the Project");
@ -81,8 +84,8 @@ void Command::Project::printProjectListText_(const std::vector<Redmine::Project>
{ {
{ {
{ tableprinter::name { "ID" } , tableprinter::width { 8 } } , { tableprinter::name { "ID" } , tableprinter::width { 8 } } ,
{ tableprinter::name { "Identifier" } , tableprinter::width { 20 } } , { tableprinter::name { "Identifier" } , tableprinter::width { 30 } } ,
{ tableprinter::name { "Name" } , tableprinter::width { 30 } } , { tableprinter::name { "Name" } , tableprinter::width { 40 } } ,
} , } ,
{ std::cout } { std::cout }
}; };
@ -145,9 +148,9 @@ void Command::Project::getList_() const
{ {
Redmine::API client{redmine_->getUrl(), redmine_->getToken()}; Redmine::API client{redmine_->getUrl(), redmine_->getToken()};
auto projects = client.getProjects(); auto projects = client.getProjects(limit_);
printProjectList_(projects); printProjectList_(projects);
return; return;
} }

View File

@ -82,12 +82,19 @@ namespace Command
/// the version of the uploading files /// the version of the uploading files
std::uint32_t fileVersion_; std::uint32_t fileVersion_;
/// the maximum number of projects to list
std::uint32_t limit_;
/// pointer to the list subcommand /// pointer to the list subcommand
CLI::App* list_; CLI::App* list_;
/// pointer to the upload subcommand /// pointer to the upload subcommand
CLI::App* upload_; CLI::App* upload_;
/// point to the list issues subcommand
CLI::App* issuesList_;
/** /**
* @brief method to print the project list * @brief method to print the project list
* *

View File

@ -40,5 +40,5 @@ version_(nullptr)
myAccount_ = std::make_unique<Command::MyAccount>(this); myAccount_ = std::make_unique<Command::MyAccount>(this);
project_ = std::make_unique<Command::Project>(this); project_ = std::make_unique<Command::Project>(this);
issueStatus_ = std::make_unique<Command::IssueStatus>(this); issueStatus_ = std::make_unique<Command::IssueStatus>(this);
issue_ = std::make_unique<Command::Issue>(this);
} }

View File

@ -15,6 +15,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "Redmine-CLI/Command/Issue.hpp"
#include "Redmine-CLI/Command/IssueStatus.hpp" #include "Redmine-CLI/Command/IssueStatus.hpp"
#include "Redmine-CLI/Command/MyAccount.hpp" #include "Redmine-CLI/Command/MyAccount.hpp"
#include "Redmine-CLI/Command/Project.hpp" #include "Redmine-CLI/Command/Project.hpp"
@ -63,6 +64,10 @@ namespace RedmineCLI
/// pointer to the project subcommand /// pointer to the project subcommand
std::unique_ptr<Command::Project> project_; std::unique_ptr<Command::Project> project_;
/// pointer to the issue subcommand
std::unique_ptr<Command::Issue> issue_;
public: public:
/** /**

View File

@ -14,13 +14,16 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "Redmine/Issue.hpp"
#include "Redmine/IssueStatus.hpp" #include "Redmine/IssueStatus.hpp"
#include "Redmine/Project.hpp" #include "Redmine/Project.hpp"
#include "nlohmann/json_fwd.hpp" #include "nlohmann/json_fwd.hpp"
#include <Redmine/API.hpp> #include <Redmine/API.hpp>
#include <Redmine/Exception/Api.hpp> #include <Redmine/Exception/Api.hpp>
#include <cstdint>
#include <exception> #include <exception>
#include <filesystem> #include <filesystem>
#include <iterator>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <vector> #include <vector>
@ -185,9 +188,9 @@ void Redmine::API::setMyAccount(const Redmine::User &user) const
} }
std::vector<Redmine::Project> Redmine::API::getProjects() const std::vector<Redmine::Project> Redmine::API::getProjects(const std::uint32_t limit) const
{ {
nlohmann::json projectList = get("/projects.json"); nlohmann::json projectList = get("/projects.json?limit="+std::to_string(limit));
std::vector<Redmine::Project> projects; std::vector<Redmine::Project> projects;
for (auto it = projectList["projects"].begin(); it != projectList["projects"].end(); ++it) for (auto it = projectList["projects"].begin(); it != projectList["projects"].end(); ++it)
@ -242,4 +245,17 @@ void Redmine::API::uploadFileToProject(const std::uint64_t projectId, const std:
upload["file"] = file; upload["file"] = file;
post("/projects/"+std::to_string(projectId)+"/files.json", upload); post("/projects/"+std::to_string(projectId)+"/files.json", upload);
} }
std::vector<Redmine::Issue> Redmine::API::getIssues(const Redmine::Filter& filter) const
{
std::vector<Redmine::Issue> issues;
nlohmann::json issueList = get("/issues.json?" + filter.toQueryString());
for (auto &issue : issueList["issues"])
{
issues.push_back(Redmine::Issue(issue));
}
return issues;
}

75
src/Redmine/Filter.cpp Normal file
View File

@ -0,0 +1,75 @@
/*
* 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 <Redmine/Filter.hpp>
#include <sstream>
Redmine::Filter::Filter()
:
filterByProjectID_(false),
projectStatus_(Redmine::Filter::ProjectStatus::open),
filterByAssignedUser_(false)
{
}
void Redmine::Filter::filterByProjectID(const std::uint64_t id)
{
filterByProjectID_ = true;
projectID_ = id;
}
void Redmine::Filter::filterByAssignedUserId(const std::uint64_t id)
{
filterByAssignedUser_ = true;
assignedUserID_ = id;
}
std::string Redmine::Filter::toQueryString() const
{
std::stringstream filter;
if (filterByProjectID_)
{
filter << "project_id=" << projectID_ << "&";
}
switch (projectStatus_)
{
case Redmine::Filter::ProjectStatus::both:
filter << "status_id=*";
break;
case Redmine::Filter::ProjectStatus::open:
filter << "status=open";
break;
case Redmine::Filter::ProjectStatus::closed:
filter << "status=closed";
break;
}; // switch projectStatus
if (filterByAssignedUser_)
{
if (filter.rdbuf()->in_avail() > 0)
{
filter << "&";
}
filter << "assigned_to_id=" << assignedUserID_;
}
return filter.str();
}

68
src/Redmine/Issue.cpp Normal file
View File

@ -0,0 +1,68 @@
#include <Redmine/Issue.hpp>
#define LOGURU_WITH_STREAMS 1
#include <loguru.hpp>
Redmine::Issue::Issue(const nlohmann::json &issue) : Redmine::Object()
{
set(issue);
}
void Redmine::Issue::set(const nlohmann::json &data)
{
_verify(data);
data_=data;
}
nlohmann::json Redmine::Issue::get() const
{
return data_;
}
void Redmine::Issue::_verify(const nlohmann::json &data)
{
_baseVerify(data, "");
}
std::uint64_t Redmine::Issue::getId() const
{
return data_["id"].get<std::uint64_t>();
}
std::string Redmine::Issue::getProject() const
{
return data_["project"]["name"].get<std::string>();
}
std::uint64_t Redmine::Issue::getProjectId() const
{
return data_["project"]["id"].get<std::uint64_t>();
}
std::string Redmine::Issue::getStatus() const
{
return data_["status"]["name"].get<std::string>();
}
std::uint64_t Redmine::Issue::getStatusID() const
{
return data_["status"]["id"].get<std::uint64_t>();
}
std::string Redmine::Issue::getSubject() const
{
return data_["subject"].get<std::string>();
}
std::string Redmine::Issue::getDescription() const
{
return data_["description"].get<std::string>();
}
std::string Redmine::Issue::to_string() const
{
return data_.dump(2);
}

View File

@ -1,5 +1,4 @@
#ifndef __LIBCMS_CONFIG_HPP__ #pragma once
#define __LIBCMS_CONFIG_HPP__
/* /*
* Copyright (C) 2024 Dominik Meyer * Copyright (C) 2024 Dominik Meyer
* *
@ -24,5 +23,3 @@
#define VERSION_MINOR 0 #define VERSION_MINOR 0
#define VERSION_PATCH 1 #define VERSION_PATCH 1
#define PROJECT_VERSION "0.0.1" #define PROJECT_VERSION "0.0.1"
#endif