From 6b5492156b7ce7599550b8e798fb62e65428c237 Mon Sep 17 00:00:00 2001 From: Dominik Meyer Date: Mon, 5 Aug 2024 23:16:52 +0200 Subject: [PATCH] feat: support listing projects --- CMakeLists.txt | 5 ++ include/Redmine/API.hpp | 14 +++- include/Redmine/Project.hpp | 30 ++++--- src/Redmine-CLI/Command/Project.cpp | 116 ++++++++++++++++++++++++++++ src/Redmine-CLI/Command/Project.hpp | 81 +++++++++++++++++++ src/Redmine-CLI/Redmine.cpp | 1 + src/Redmine-CLI/Redmine.hpp | 5 +- src/Redmine/API.cpp | 18 ++++- src/Redmine/Project.cpp | 47 +++++++++++ 9 files changed, 302 insertions(+), 15 deletions(-) create mode 100644 src/Redmine-CLI/Command/Project.cpp create mode 100644 src/Redmine-CLI/Command/Project.hpp create mode 100644 src/Redmine/Project.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fdca91..3044490 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,9 @@ SET(REDMINE_API_SOURCES include/Redmine/User.hpp src/Redmine/User.cpp + include/Redmine/Project.hpp + src/Redmine/Project.cpp + include/Redmine/IssueStatus.hpp src/Redmine/IssueStatus.cpp @@ -119,6 +122,8 @@ add_executable(redmine-cli src/Redmine-CLI/Command/Version.cpp src/Redmine-CLI/Command/MyAccount.hpp src/Redmine-CLI/Command/MyAccount.cpp + src/Redmine-CLI/Command/Project.hpp + src/Redmine-CLI/Command/Project.cpp src/Redmine-CLI/Command/IssueStatus.hpp src/Redmine-CLI/Command/IssueStatus.cpp ) diff --git a/include/Redmine/API.hpp b/include/Redmine/API.hpp index 745067f..1b99d88 100644 --- a/include/Redmine/API.hpp +++ b/include/Redmine/API.hpp @@ -19,10 +19,10 @@ #include "Redmine/IssueStatus.hpp" #include "nlohmann/json_fwd.hpp" #include -#include #include #include #include +#include #include /** @@ -61,7 +61,7 @@ namespace Redmine */ /*@{*/ /** - * @brief Only Constructor of the Readmine::API t + * @brief Only Constructor of the Readmine::API * * Only this constructor exists to ensure url and token are available * @@ -108,8 +108,14 @@ namespace Redmine * */ std::vector getIssueStatusList(); - - + + /** + * @brief Return a list of all available projects for the logged in user + * + * @return std::vector + */ + std::vector getProjects() const; + /*@}*/ }; diff --git a/include/Redmine/Project.hpp b/include/Redmine/Project.hpp index 0692e67..838edc1 100644 --- a/include/Redmine/Project.hpp +++ b/include/Redmine/Project.hpp @@ -33,41 +33,53 @@ namespace Redmine * @brief This class represents a user within the redmine server. * */ - class Issue : public Redmine::Object + class Project : public Redmine::Object { private: /// the data store for the issue nlohmann::json data_; /** - * @brief verify issue data + * @brief verify project data * - * @param data - the json object containing the issue data + * @param data - the json object containing the project data */ void _verify(const nlohmann::json &data); public: - explicit Issue(const nlohmann::json &issue); + explicit Project(const nlohmann::json &project); /** - * @brief set the issue object from a json object + * @brief set the project object from a json object * * @param data */ void set(const nlohmann::json &data); /** - * @brief return a json object from the issue object + * @brief return a json object from the project object * * @return nlohmann::json */ nlohmann::json get() const; - + /** + * @brief Get the Id of the project + * + * @return std::uint64_t + */ std::uint64_t getId() const; + std::string getName() const; + + std::string getStringID() const; - - }; // class Issue + /** + * @brief convert the project object to a string + * + * @return std::string + */ + std::string to_string() const; + }; // class Project }; // namespace Redmine \ No newline at end of file diff --git a/src/Redmine-CLI/Command/Project.cpp b/src/Redmine-CLI/Command/Project.cpp new file mode 100644 index 0000000..1cc6405 --- /dev/null +++ b/src/Redmine-CLI/Command/Project.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 "nlohmann/json_fwd.hpp" + + +Command::Project::Project(RedmineCLI::Redmine *r) +: +redmine_(r), +output_(TEXT) +{ + + CLI::App* project = redmine_->getCLI().add_subcommand("project", + "Api Calls on projects accessible by token owner"); + + + list_ = project->add_subcommand("list", "List all projects accessible by the token owner"); + list_->needs(redmine_->getTokenOption())->needs(redmine_->getUrlOption()); + 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)); + +} + +void Command::Project::getList_() const +{ + Redmine::API client{redmine_->getUrl(), redmine_->getToken()}; + + auto projects = client.getProjects(); + + bool hasName = std::find(fields_.begin(), fields_.end(),"name") != fields_.end() || fields_.empty(); + + bool hasId = std::find(fields_.begin(), fields_.end(),"id") != fields_.end() || fields_.empty(); + + bool hasStrI = std::find(fields_.begin(), fields_.end(),"strid") != fields_.end() || fields_.empty(); + + + nlohmann::json data = nlohmann::json::array(); + + std::cout.setf(std::ios::fixed | std::ios::right); + + for (auto it = projects.begin(); it!= projects.end(); ++it) + { + nlohmann::json project; + + if (hasId) + { + if (output_==TEXT) + { + std::cout << it->getId() << " "; + } + else if (output_ == YAML) + { + project["id"]=it->getId(); + } + } + + if (hasStrI) + { + if (output_==TEXT) + { + std::cout << it->getStringID() << " "; + } + else if (output_ == YAML) + { + project["identifier"]=it->getStringID(); + } + } + + + if (hasName) + { + if (output_==TEXT) + { + std::cout << "\"" << it->getName() << "\" "; + } + else if (output_ == YAML) + { + project["name"]=it->getName(); + } + } + + + + if (output_ == TEXT) + { + std::cout << std::endl; + } + else if (output_==YAML) + { + data.push_back(project); + } + } + + if (output_ == YAML) + { + std::cout << data.dump(2); + } +} \ No newline at end of file diff --git a/src/Redmine-CLI/Command/Project.hpp b/src/Redmine-CLI/Command/Project.hpp new file mode 100644 index 0000000..d5bb85a --- /dev/null +++ b/src/Redmine-CLI/Command/Project.hpp @@ -0,0 +1,81 @@ +#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 +#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 Project + { + + 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 yaml + YAML + }; + + /// requested output type + outputType output_; + + std::map outputMap{{"text", TEXT}, {"yaml", YAML}}; + + /// vector to hold requested attribute fields + std::vector fields_; + + /// pointer to the list subcommand + CLI::App* list_; + + /// the actual implementation of the list command + void getList_() const; + + public: + /** + * @brief Construct a new CLIProcessor object + * + */ + explicit Project(RedmineCLI::Redmine *r); + + }; // class Project +}; // namespace Command \ No newline at end of file diff --git a/src/Redmine-CLI/Redmine.cpp b/src/Redmine-CLI/Redmine.cpp index 6765bdf..c8bc010 100644 --- a/src/Redmine-CLI/Redmine.cpp +++ b/src/Redmine-CLI/Redmine.cpp @@ -38,6 +38,7 @@ version_(nullptr) // add all commands here version_ = std::make_unique(this); myAccount_ = std::make_unique(this); + project_ = std::make_unique(this); issueStatus_ = 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 f652083..070d94e 100644 --- a/src/Redmine-CLI/Redmine.hpp +++ b/src/Redmine-CLI/Redmine.hpp @@ -17,6 +17,7 @@ */ #include "Redmine-CLI/Command/IssueStatus.hpp" #include "Redmine-CLI/Command/MyAccount.hpp" +#include "Redmine-CLI/Command/Project.hpp" #include "Redmine-CLI/Command/Version.hpp" #include #include @@ -59,7 +60,9 @@ namespace RedmineCLI /// pointer to the issues subcommand std::unique_ptr issueStatus_; - + + /// pointer to the project subcommand + std::unique_ptr project_; public: /** diff --git a/src/Redmine/API.cpp b/src/Redmine/API.cpp index 08dfe08..f2006cb 100644 --- a/src/Redmine/API.cpp +++ b/src/Redmine/API.cpp @@ -15,6 +15,7 @@ * along with this program. If not, see . */ #include "Redmine/IssueStatus.hpp" +#include "Redmine/Project.hpp" #include "nlohmann/json_fwd.hpp" #include #include @@ -143,6 +144,21 @@ void Redmine::API::setMyAccount(const Redmine::User &user) const } +std::vector Redmine::API::getProjects() const +{ + nlohmann::json projectList = get("/projects"); + std::vector projects; + + for (auto it = projectList["projects"].begin(); it != projectList["projects"].end(); ++it) + { + Redmine::Project p{*it}; + projects.push_back(p); + } + + return projects; +} + + std::vector Redmine::API::getIssueStatusList() { nlohmann::json statusList = get("/issue_statuses"); @@ -164,7 +180,7 @@ bool Redmine::API::ready() const { try { getMyAccount(); - } catch (std::exception &e) + } catch (const std::exception &e) { return false; } diff --git a/src/Redmine/Project.cpp b/src/Redmine/Project.cpp new file mode 100644 index 0000000..f8a4dec --- /dev/null +++ b/src/Redmine/Project.cpp @@ -0,0 +1,47 @@ +#include +#define LOGURU_WITH_STREAMS 1 +#include + + +Redmine::Project::Project(const nlohmann::json &project) : Redmine::Object() +{ + set(project); +} + +void Redmine::Project::set(const nlohmann::json &data) +{ + _verify(data); + data_=data; +} + +nlohmann::json Redmine::Project::get() const +{ + return data_; +} + +void Redmine::Project::_verify(const nlohmann::json &data) +{ + + _baseVerify(data, ""); + +} + +std::uint64_t Redmine::Project::getId() const +{ + return data_["id"].get(); +} + +std::string Redmine::Project::getName() const +{ + return data_["name"].get(); +} + +std::string Redmine::Project::getStringID() const +{ + return data_["identifier"].get(); +} + +std::string Redmine::Project::to_string() const +{ + return data_.dump(2); +} \ No newline at end of file