Compare commits

..

2 Commits

Author SHA1 Message Date
6b5492156b
feat: support listing projects 2024-08-05 23:16:52 +02:00
65484f57fd
fix: some small fixes 2024-08-05 20:57:45 +02:00
10 changed files with 302 additions and 19 deletions

View File

@ -74,6 +74,9 @@ SET(REDMINE_API_SOURCES
include/Redmine/User.hpp include/Redmine/User.hpp
src/Redmine/User.cpp src/Redmine/User.cpp
include/Redmine/Project.hpp
src/Redmine/Project.cpp
include/Redmine/IssueStatus.hpp include/Redmine/IssueStatus.hpp
src/Redmine/IssueStatus.cpp src/Redmine/IssueStatus.cpp
@ -119,6 +122,8 @@ add_executable(redmine-cli
src/Redmine-CLI/Command/Version.cpp src/Redmine-CLI/Command/Version.cpp
src/Redmine-CLI/Command/MyAccount.hpp src/Redmine-CLI/Command/MyAccount.hpp
src/Redmine-CLI/Command/MyAccount.cpp 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.hpp
src/Redmine-CLI/Command/IssueStatus.cpp src/Redmine-CLI/Command/IssueStatus.cpp
) )

View File

@ -19,10 +19,10 @@
#include "Redmine/IssueStatus.hpp" #include "Redmine/IssueStatus.hpp"
#include "nlohmann/json_fwd.hpp" #include "nlohmann/json_fwd.hpp"
#include <httplib.h> #include <httplib.h>
#include <memory>
#include <string> #include <string>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <Redmine/User.hpp> #include <Redmine/User.hpp>
#include <Redmine/Project.hpp>
#include <vector> #include <vector>
/** /**
@ -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 * Only this constructor exists to ensure url and token are available
* *
@ -109,6 +109,12 @@ namespace Redmine
*/ */
std::vector<Redmine::IssueStatus> getIssueStatusList(); std::vector<Redmine::IssueStatus> getIssueStatusList();
/**
* @brief Return a list of all available projects for the logged in user
*
* @return std::vector<Redmine::Project>
*/
std::vector<Redmine::Project> getProjects() const;
/*@}*/ /*@}*/
}; };

View File

@ -33,41 +33,53 @@ namespace Redmine
* @brief This class represents a user within the redmine server. * @brief This class represents a user within the redmine server.
* *
*/ */
class Issue : public Redmine::Object class Project : public Redmine::Object
{ {
private: private:
/// the data store for the issue /// the data store for the issue
nlohmann::json data_; 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); void _verify(const nlohmann::json &data);
public: 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 * @param data
*/ */
void set(const nlohmann::json &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 * @return nlohmann::json
*/ */
nlohmann::json get() const; nlohmann::json get() const;
/**
* @brief Get the Id of the project
*
* @return std::uint64_t
*/
std::uint64_t getId() const; 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 }; // 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/Project.hpp>
#include <Redmine-CLI/Redmine.hpp>
#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);
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#include <CLI/App.hpp>
#include <Redmine/API.hpp>
#include <atomic>
#include <cstdint>
#include <memory>
#include <string>
#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 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<std::string, outputType> outputMap{{"text", TEXT}, {"yaml", YAML}};
/// vector to hold requested attribute fields
std::vector<std::string> 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

View File

@ -38,6 +38,7 @@ version_(nullptr)
// add all commands here // add all commands here
version_ = std::make_unique<Command::Version>(this); version_ = std::make_unique<Command::Version>(this);
myAccount_ = std::make_unique<Command::MyAccount>(this); myAccount_ = std::make_unique<Command::MyAccount>(this);
project_ = std::make_unique<Command::Project>(this);
issueStatus_ = std::make_unique<Command::IssueStatus>(this); issueStatus_ = std::make_unique<Command::IssueStatus>(this);
} }

View File

@ -17,6 +17,7 @@
*/ */
#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/Version.hpp" #include "Redmine-CLI/Command/Version.hpp"
#include <CLI/App.hpp> #include <CLI/App.hpp>
#include <CLI/Option.hpp> #include <CLI/Option.hpp>
@ -60,6 +61,8 @@ namespace RedmineCLI
/// pointer to the issues subcommand /// pointer to the issues subcommand
std::unique_ptr<Command::IssueStatus> issueStatus_; std::unique_ptr<Command::IssueStatus> issueStatus_;
/// pointer to the project subcommand
std::unique_ptr<Command::Project> project_;
public: public:
/** /**

View File

@ -16,15 +16,12 @@
*/ */
#include "Redmine-CLI/Redmine.hpp" #include "Redmine-CLI/Redmine.hpp"
#include "config.hpp"
#include "config.hpp.in"
#include <CLI/App.hpp> #include <CLI/App.hpp>
#include <CLI/Option.hpp> #include <CLI/Option.hpp>
#include <Redmine/API.hpp> #include <Redmine/API.hpp>
#include <Redmine/Exception/Api.hpp> #include <Redmine/Exception/Api.hpp>
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
#include <vector>
#define LOGURU_WITH_STREAMS 1 #define LOGURU_WITH_STREAMS 1
#include <loguru.hpp> #include <loguru.hpp>
#include <CLI/CLI.hpp> #include <CLI/CLI.hpp>

View File

@ -15,11 +15,11 @@
* 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/IssueStatus.hpp" #include "Redmine/IssueStatus.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 <exception> #include <exception>
#include <memory>
#include <stdexcept> #include <stdexcept>
#include <vector> #include <vector>
#define LOGURU_WITH_STREAMS 1 #define LOGURU_WITH_STREAMS 1
@ -144,6 +144,21 @@ void Redmine::API::setMyAccount(const Redmine::User &user) const
} }
std::vector<Redmine::Project> Redmine::API::getProjects() const
{
nlohmann::json projectList = get("/projects");
std::vector<Redmine::Project> 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::IssueStatus> Redmine::API::getIssueStatusList() std::vector<Redmine::IssueStatus> Redmine::API::getIssueStatusList()
{ {
nlohmann::json statusList = get("/issue_statuses"); nlohmann::json statusList = get("/issue_statuses");
@ -165,7 +180,7 @@ bool Redmine::API::ready() const
{ {
try { try {
getMyAccount(); getMyAccount();
} catch (std::exception &e) } catch (const std::exception &e)
{ {
return false; return false;
} }

47
src/Redmine/Project.cpp Normal file
View File

@ -0,0 +1,47 @@
#include <Redmine/Project.hpp>
#define LOGURU_WITH_STREAMS 1
#include <loguru.hpp>
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::uint64_t>();
}
std::string Redmine::Project::getName() const
{
return data_["name"].get<std::string>();
}
std::string Redmine::Project::getStringID() const
{
return data_["identifier"].get<std::string>();
}
std::string Redmine::Project::to_string() const
{
return data_.dump(2);
}