diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7040f07..72b67df 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -85,6 +85,12 @@ SET(REDMINE_API_SOURCES
include/Redmine/API.hpp
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})
@@ -129,6 +135,8 @@ add_executable(redmine-cli
src/Redmine-CLI/Command/Project.cpp
src/Redmine-CLI/Command/IssueStatus.hpp
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_include_directories(redmine-cli
diff --git a/include/Redmine/API.hpp b/include/Redmine/API.hpp
index e614371..6821442 100644
--- a/include/Redmine/API.hpp
+++ b/include/Redmine/API.hpp
@@ -16,6 +16,8 @@
* along with this program. If not, see .
*/
+#include "Redmine/Filter.hpp"
+#include "Redmine/Issue.hpp"
#include "Redmine/IssueStatus.hpp"
#include "nlohmann/json_fwd.hpp"
#include
@@ -127,9 +129,19 @@ namespace Redmine
*
* @return std::vector
*/
- std::vector getProjects() const;
+ std::vector 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;
+
+ /**
+ * @brief Get issues with the given filters from the redmine server
+ *
+ * @param filter - the filter to apply on the issues
+ * @return std::vector
+ */
+ std::vector getIssues(const Redmine::Filter& filter = {}) const;
+
+
/*@}*/
};
diff --git a/include/Redmine/Filter.hpp b/include/Redmine/Filter.hpp
new file mode 100644
index 0000000..7de4c74
--- /dev/null
+++ b/include/Redmine/Filter.hpp
@@ -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 .
+*/
+
+/**
+ * @brief The main namespace of this library for Redmine related datatypes, classes, and functions
+ *
+ */
+#include
+#include
+#include
+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
\ No newline at end of file
diff --git a/include/Redmine/Issue.hpp b/include/Redmine/Issue.hpp
index 0692e67..099fc4a 100644
--- a/include/Redmine/Issue.hpp
+++ b/include/Redmine/Issue.hpp
@@ -63,10 +63,20 @@ namespace Redmine
*/
nlohmann::json get() const;
-
+ /**
+ * @brief return the id of the issue
+ *
+ * @return std::uint64_t
+ */
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
diff --git a/include/Redmine/Tracker.hpp b/include/Redmine/Tracker.hpp
index e69de29..0a131c2 100644
--- a/include/Redmine/Tracker.hpp
+++ b/include/Redmine/Tracker.hpp
@@ -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 .
+*/
+
+#include "nlohmann/json_fwd.hpp"
+#include
+#include
+#include
+#include
+
+/**
+ * @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
\ No newline at end of file
diff --git a/src/Redmine-CLI/Command/Issue.cpp b/src/Redmine-CLI/Command/Issue.cpp
new file mode 100644
index 0000000..438e759
--- /dev/null
+++ b/src/Redmine-CLI/Command/Issue.cpp
@@ -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 .
+*/
+
+#include
+#include
+#include "Redmine/API.hpp"
+#include "Redmine/Filter.hpp"
+#include "nlohmann/json_fwd.hpp"
+#include
+
+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 &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 &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;
+}
\ No newline at end of file
diff --git a/src/Redmine-CLI/Command/Issue.hpp b/src/Redmine-CLI/Command/Issue.hpp
new file mode 100644
index 0000000..71f2ac6
--- /dev/null
+++ b/src/Redmine-CLI/Command/Issue.hpp
@@ -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 .
+*/
+#include "Redmine/Filter.hpp"
+#include "Redmine/Project.hpp"
+#include "nlohmann/json_fwd.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#define LOGURU_WITH_STREAMS 1
+#include
+#include
+
+
+// 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 outputMap{{"text", TEXT}, {"json", JSON}};
+
+ /// vector to hold requested attribute fields
+ std::vector 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 &issues) const;
+
+ void printIssueListText_(const std::vector &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
\ No newline at end of file
diff --git a/src/Redmine-CLI/Command/Project.cpp b/src/Redmine-CLI/Command/Project.cpp
index 208c901..c92471a 100644
--- a/src/Redmine-CLI/Command/Project.cpp
+++ b/src/Redmine-CLI/Command/Project.cpp
@@ -19,12 +19,14 @@
#include
#include "Redmine/API.hpp"
#include "nlohmann/json_fwd.hpp"
+#include
#include
Command::Project::Project(RedmineCLI::Redmine *r)
:
redmine_(r),
-output_(TEXT)
+output_(TEXT),
+limit_(100)
{
CLI::App* project = redmine_->getCLI().add_subcommand("project",
@@ -36,6 +38,7 @@ output_(TEXT)
list_->callback([&](){getList_();});
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("-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");
@@ -81,8 +84,8 @@ void Command::Project::printProjectListText_(const std::vector
{
{
{ tableprinter::name { "ID" } , tableprinter::width { 8 } } ,
- { tableprinter::name { "Identifier" } , tableprinter::width { 20 } } ,
- { tableprinter::name { "Name" } , tableprinter::width { 30 } } ,
+ { tableprinter::name { "Identifier" } , tableprinter::width { 30 } } ,
+ { tableprinter::name { "Name" } , tableprinter::width { 40 } } ,
} ,
{ std::cout }
};
@@ -145,9 +148,9 @@ void Command::Project::getList_() const
{
Redmine::API client{redmine_->getUrl(), redmine_->getToken()};
- auto projects = client.getProjects();
+ auto projects = client.getProjects(limit_);
printProjectList_(projects);
return;
-}
\ No newline at end of file
+}
diff --git a/src/Redmine-CLI/Command/Project.hpp b/src/Redmine-CLI/Command/Project.hpp
index 49ec723..2646bb0 100644
--- a/src/Redmine-CLI/Command/Project.hpp
+++ b/src/Redmine-CLI/Command/Project.hpp
@@ -82,12 +82,19 @@ namespace Command
/// the version of the uploading files
std::uint32_t fileVersion_;
+ /// the maximum number of projects to list
+ std::uint32_t limit_;
+
/// pointer to the list subcommand
CLI::App* list_;
/// pointer to the upload subcommand
CLI::App* upload_;
+
+ /// point to the list issues subcommand
+ CLI::App* issuesList_;
+
/**
* @brief method to print the project list
*
diff --git a/src/Redmine-CLI/Redmine.cpp b/src/Redmine-CLI/Redmine.cpp
index c8bc010..6c5a5d7 100644
--- a/src/Redmine-CLI/Redmine.cpp
+++ b/src/Redmine-CLI/Redmine.cpp
@@ -40,5 +40,5 @@ version_(nullptr)
myAccount_ = std::make_unique(this);
project_ = std::make_unique(this);
issueStatus_ = std::make_unique(this);
-
+ issue_ = std::make_unique(this);
}
\ No newline at end of file
diff --git a/src/Redmine-CLI/Redmine.hpp b/src/Redmine-CLI/Redmine.hpp
index 070d94e..770dab0 100644
--- a/src/Redmine-CLI/Redmine.hpp
+++ b/src/Redmine-CLI/Redmine.hpp
@@ -15,6 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+#include "Redmine-CLI/Command/Issue.hpp"
#include "Redmine-CLI/Command/IssueStatus.hpp"
#include "Redmine-CLI/Command/MyAccount.hpp"
#include "Redmine-CLI/Command/Project.hpp"
@@ -63,6 +64,10 @@ namespace RedmineCLI
/// pointer to the project subcommand
std::unique_ptr project_;
+
+ /// pointer to the issue subcommand
+ std::unique_ptr issue_;
+
public:
/**
diff --git a/src/Redmine/API.cpp b/src/Redmine/API.cpp
index 93717db..9f3f515 100644
--- a/src/Redmine/API.cpp
+++ b/src/Redmine/API.cpp
@@ -14,13 +14,16 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+#include "Redmine/Issue.hpp"
#include "Redmine/IssueStatus.hpp"
#include "Redmine/Project.hpp"
#include "nlohmann/json_fwd.hpp"
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
@@ -185,9 +188,9 @@ void Redmine::API::setMyAccount(const Redmine::User &user) const
}
-std::vector Redmine::API::getProjects() const
+std::vector 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 projects;
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;
post("/projects/"+std::to_string(projectId)+"/files.json", upload);
-}
\ No newline at end of file
+}
+
+ std::vector Redmine::API::getIssues(const Redmine::Filter& filter) const
+ {
+ std::vector issues;
+
+ nlohmann::json issueList = get("/issues.json?" + filter.toQueryString());
+ for (auto &issue : issueList["issues"])
+ {
+ issues.push_back(Redmine::Issue(issue));
+ }
+
+ return issues;
+ }
\ No newline at end of file
diff --git a/src/Redmine/Filter.cpp b/src/Redmine/Filter.cpp
new file mode 100644
index 0000000..e927996
--- /dev/null
+++ b/src/Redmine/Filter.cpp
@@ -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 .
+*/
+#include
+#include
+
+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();
+}
\ No newline at end of file
diff --git a/src/Redmine/Issue.cpp b/src/Redmine/Issue.cpp
new file mode 100644
index 0000000..4b49506
--- /dev/null
+++ b/src/Redmine/Issue.cpp
@@ -0,0 +1,68 @@
+#include
+#define LOGURU_WITH_STREAMS 1
+#include
+
+
+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::string Redmine::Issue::getProject() const
+{
+ return data_["project"]["name"].get();
+}
+
+std::uint64_t Redmine::Issue::getProjectId() const
+{
+ return data_["project"]["id"].get();
+}
+
+std::string Redmine::Issue::getStatus() const
+{
+ return data_["status"]["name"].get();
+}
+
+std::uint64_t Redmine::Issue::getStatusID() const
+{
+ return data_["status"]["id"].get();
+}
+
+std::string Redmine::Issue::getSubject() const
+{
+ return data_["subject"].get();
+}
+
+std::string Redmine::Issue::getDescription() const
+{
+ return data_["description"].get();
+}
+
+
+std::string Redmine::Issue::to_string() const
+{
+ return data_.dump(2);
+}
\ No newline at end of file
diff --git a/src/config.hpp b/src/config.hpp
index 3e4403e..101d459 100644
--- a/src/config.hpp
+++ b/src/config.hpp
@@ -1,5 +1,4 @@
-#ifndef __LIBCMS_CONFIG_HPP__
-#define __LIBCMS_CONFIG_HPP__
+#pragma once
/*
* Copyright (C) 2024 Dominik Meyer
*
@@ -24,5 +23,3 @@
#define VERSION_MINOR 0
#define VERSION_PATCH 1
#define PROJECT_VERSION "0.0.1"
-
-#endif