From 0336b38a4529fa1cc7a63311b04859f2b18a6389 Mon Sep 17 00:00:00 2001 From: Dominik Meyer Date: Thu, 10 Oct 2024 13:41:07 +0200 Subject: [PATCH] feat: support updating, getting, and deleting wiki pages --- CMakeLists.txt | 2 + README.md | 4 +- include/Redmine/API.hpp | 37 ++++++++++ src/Redmine-CLI/Command/Wiki.cpp | 101 +++++++++++++++++++++++++++ src/Redmine-CLI/Command/Wiki.hpp | 113 +++++++++++++++++++++++++++++++ src/Redmine-CLI/Redmine.cpp | 2 + src/Redmine-CLI/Redmine.hpp | 4 ++ src/Redmine/API.cpp | 62 ++++++++++++++++- 8 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 src/Redmine-CLI/Command/Wiki.cpp create mode 100644 src/Redmine-CLI/Command/Wiki.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 35b19b9..6fbd6b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,6 +139,8 @@ add_executable(redmine-cli src/Redmine-CLI/Command/Issue.cpp src/Redmine-CLI/Command/Upload.hpp src/Redmine-CLI/Command/Upload.cpp + src/Redmine-CLI/Command/Wiki.hpp + src/Redmine-CLI/Command/Wiki.cpp ) target_link_libraries(redmine-cli redmine-api-cpp-static loguru CLI11 tableprinter::tableprinter) target_include_directories(redmine-cli diff --git a/README.md b/README.md index 7d25bad..fad1ac3 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,9 @@ It also provides a small CLI application to interact with a remine server throug |News | * | no | |Issue Relation | * | no | |Versions | * | no | -|Wiki Pages | * | no | +|Wiki Pages | list | no | +|Wiki Pages | create/update | yes | +|Wiki Pages | delete | yes | |Queries | * | no | |Attachments | * | no | |Issue Statuses | * | no | diff --git a/include/Redmine/API.hpp b/include/Redmine/API.hpp index 8aced1b..64a2e14 100644 --- a/include/Redmine/API.hpp +++ b/include/Redmine/API.hpp @@ -58,6 +58,7 @@ namespace Redmine nlohmann::json get(const std::string &path) const; void put(const std::string &path, const nlohmann::json &data) const; void post(const std::string &path, const nlohmann::json &data) const; + void del(const std::string &path) const; void processGenericErrors_(httplib::Result &res) const; @@ -141,7 +142,43 @@ namespace Redmine * @return std::string - the redmine internal token (reference) for the uploaded file */ std::string upload(const std::filesystem::path &file, const std::string &filename) const; + + + /** + * @brief Get a Wiki Page from the redmine server + * + * @param projectIdentifier - the project identifier (only string not number) + * @param page - the name of the page + * @return std::string - the content of the wiki page + */ + std::string getWikiPage(const std::string &projectIdentifier, const std::string &page); + /** + * @brief update a Wiki Page on the redmine server + * + * @param projectIdentifier - the project identifier (only string not number) + * @param page - the name of the page + * @param content - the content of the page + */ + void updateWikiPage(const std::string &projectIdentifier, const std::string &page, const std::string &content); + + /** + * @brief update a Wiki Page on the redmine server with attachments + * + * @param projectIdentifier - the project identifier (only string not number) + * @param page - the name of the page + * @param attachements - vector of attachment paths + * @param content - the content of the page + */ + void updateWikiPage(const std::string &projectIdentifier, const std::string &page, const std::string &content, const std::vector &attachments ); + + /** + * @brief delete a Wiki Page on the redmine server + * + * @param projectIdentifier - the project identifier (only string not number) + * @param page - the name of the page + */ + void deleteWikiPage(const std::string &projectIdentifier, const std::string &page); /*@}*/ }; diff --git a/src/Redmine-CLI/Command/Wiki.cpp b/src/Redmine-CLI/Command/Wiki.cpp new file mode 100644 index 0000000..1a9c5db --- /dev/null +++ b/src/Redmine-CLI/Command/Wiki.cpp @@ -0,0 +1,101 @@ +/* +* 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" +#include +#include +#include +#include + +Command::Wiki::Wiki(RedmineCLI::Redmine *r) +: +redmine_(r), +output_(TEXT) +{ + + CLI::App* wiki = redmine_->getCLI().add_subcommand("wiki", + "Api Calls on wikis accessible by token owner"); + + + getPage_ = wiki->add_subcommand("getPage", "get a specific Wiki page from server"); + getPage_->needs(redmine_->getTokenOption())->needs(redmine_->getUrlOption()); + getPage_->callback([&](){getWikiPage_();}); + getPage_->add_option("-p,--project", projectIdentifier_,"string identifier of the project to get wiki page from")->required(); + getPage_->add_option("--page",pageIdentifier_,"string identifier of the page to get")->required(); + + deletePage_ = wiki->add_subcommand("deletePage", "delete^ a specific Wiki page from server"); + deletePage_->needs(redmine_->getTokenOption())->needs(redmine_->getUrlOption()); + deletePage_->callback([&](){deleteWikiPage_();}); + deletePage_->add_option("-p,--project", projectIdentifier_,"string identifier of the project to delete wiki page from")->required(); + deletePage_->add_option("--page",pageIdentifier_,"string identifier of the page to delete")->required(); + + + updatePage_ = wiki->add_subcommand("updatePage", "Update a specific Wiki page on the server"); + updatePage_->needs(redmine_->getTokenOption())->needs(redmine_->getUrlOption()); + updatePage_->add_option("-p,--project", projectIdentifier_,"string identifier of the project to update the wiki page in")->required(); + updatePage_->add_option("--page",pageIdentifier_,"string identifier of the page to update")->required(); + updatePage_->add_option("-c,--content", content_,"the new content of the wiki page"); + updatePage_->add_option("-f,--file", filePath_,"the new content of the wiki page as a file"); + updatePage_->add_option("-a",attachments_,"list of file paths to attach to the wiki page"); + updatePage_->callback([&](){updateWikiPage_();}); +} + + +void Command::Wiki::getWikiPage_() +{ + Redmine::API client{redmine_->getUrl(), redmine_->getToken()}; + + std::cout << client.getWikiPage(projectIdentifier_, pageIdentifier_) << std::endl; + +} + +void Command::Wiki::deleteWikiPage_() +{ + Redmine::API client{redmine_->getUrl(), redmine_->getToken()}; + + client.deleteWikiPage(projectIdentifier_, pageIdentifier_); + +} + +void Command::Wiki::updateWikiPage_() +{ + Redmine::API client{redmine_->getUrl(), redmine_->getToken()}; + + + if (!content_.empty()) { + client.updateWikiPage(projectIdentifier_, pageIdentifier_, content_); + } + else if (!filePath_.empty()) + { + std::filesystem::path path = filePath_; + if (!std::filesystem::exists(path)) + { + LOG_S(ERROR) << "File does not exist: " << filePath_; + throw std::runtime_error("File does not exist: " + filePath_); + } + + std::ifstream file(filePath_); + std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + client.updateWikiPage(projectIdentifier_, pageIdentifier_, content, attachments_); + } + + +} + diff --git a/src/Redmine-CLI/Command/Wiki.hpp b/src/Redmine-CLI/Command/Wiki.hpp new file mode 100644 index 0000000..7063592 --- /dev/null +++ b/src/Redmine-CLI/Command/Wiki.hpp @@ -0,0 +1,113 @@ +#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 +#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 Wiki + { + + 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}}; + + /// command line argument project identifier + std::string projectIdentifier_; + + /// command line argument page identifier + std::string pageIdentifier_; + + /// command line argument page content + std::string content_; + + /// command line argument file path for page content + std::string filePath_; + + /// command line argument file path for attachments + std::vector attachments_; + + /// pointer to the getPage subcommand + CLI::App* getPage_; + + /// pointer to the updatePage subcommand + CLI::App* updatePage_; + + /// pointer to the deletePage subcommand + CLI::App* deletePage_; + + /** + * @brief Get a Wiki Page from the server + * + */ + void getWikiPage_(); + + /** + * @brief update a wiki page on the server + * + */ + void updateWikiPage_(); + + /** + * @brief delete a wiki page on the server + * + */ + void deleteWikiPage_(); + + public: + /** + * @brief Construct a new CLIProcessor object + * + */ + explicit Wiki(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 f05f687..86b0ccd 100644 --- a/src/Redmine-CLI/Redmine.cpp +++ b/src/Redmine-CLI/Redmine.cpp @@ -17,6 +17,7 @@ #include #include "Redmine-CLI/Command/IssueStatus.hpp" #include "Redmine-CLI/Command/Version.hpp" +#include "Redmine-CLI/Command/Wiki.hpp" #include #include #define LOGURU_WITH_STREAMS 1 @@ -42,4 +43,5 @@ version_(nullptr) issueStatus_ = std::make_unique(this); issue_ = std::make_unique(this); upload_ = std::make_unique(this); + wiki_ = 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 8fb80bc..44750a0 100644 --- a/src/Redmine-CLI/Redmine.hpp +++ b/src/Redmine-CLI/Redmine.hpp @@ -21,6 +21,7 @@ #include "Redmine-CLI/Command/Project.hpp" #include "Redmine-CLI/Command/Upload.hpp" #include "Redmine-CLI/Command/Version.hpp" +#include "Redmine-CLI/Command/Wiki.hpp" #include #include #include @@ -72,6 +73,9 @@ namespace RedmineCLI /// pointer to the upload subcommand std::unique_ptr upload_; + /// pointer to the wiki subcommand + std::unique_ptr wiki_; + public: /** diff --git a/src/Redmine/API.cpp b/src/Redmine/API.cpp index 844d538..fc46979 100644 --- a/src/Redmine/API.cpp +++ b/src/Redmine/API.cpp @@ -131,6 +131,19 @@ void Redmine::API::put(const std::string &path, const nlohmann::json &data) con DLOG_S(INFO) << "put successful"; } + + void Redmine::API::del(const std::string &path) const + { + DLOG_S(INFO) << "deleting API endpoint " << path << " from " <(); + } + + void Redmine::API::updateWikiPage(const std::string &projectIdentifier, const std::string &page, const std::string &content, const std::vector &attachments ) + { + nlohmann::json wikiPage; + wikiPage["wiki_page"]["text"] = content; + wikiPage["wiki_page"]["uploads"]=nlohmann::json::array(); + + for (auto attachment : attachments) + { + std::filesystem::path attachmentPath = attachment; + if (!std::filesystem::exists(attachmentPath)) + { + LOG_S(ERROR) << "Attachment " << attachmentPath.string() << " does not exist."; + throw std::runtime_error("Attachment " + attachmentPath.string() + " does not exist."); + } + + std::string token = upload(attachmentPath, attachmentPath.filename().string()); + nlohmann::json attachmentJson; + attachmentJson["token"] = token; + attachmentJson["filename"] = attachmentPath.filename().string(); + + wikiPage["wiki_page"]["uploads"].push_back(attachmentJson); + } + + put("/projects/" + projectIdentifier + "/wiki/" + page + ".json", wikiPage); + } + + void Redmine::API::updateWikiPage(const std::string& project, const std::string& page, const std::string &content) + { + std::vector attachments; + + updateWikiPage(project, page, content, attachments); + + } + + void Redmine::API::deleteWikiPage(const std::string& project, const std::string& page) + { + + del("/projects/" + project + "/wiki/" + page + ".json"); + + }