# HG changeset patch # User David Demelier # Date 1499271626 -7200 # Node ID 9be4f8a5cf1ac0dfbd8716f99ccd6a51f7931e21 # Parent f3c27790d0d1b8e3a4d4c23254c57666da2fa71d Irccdctl: implement rule-list diff -r f3c27790d0d1 -r 9be4f8a5cf1a doc/html/CMakeLists.txt --- a/doc/html/CMakeLists.txt Sun Jul 02 08:11:23 2017 +0200 +++ b/doc/html/CMakeLists.txt Wed Jul 05 18:20:26 2017 +0200 @@ -152,6 +152,7 @@ ${html_SOURCE_DIR}/irccdctl/command/plugin-load.md ${html_SOURCE_DIR}/irccdctl/command/plugin-reload.md ${html_SOURCE_DIR}/irccdctl/command/plugin-unload.md + ${html_SOURCE_DIR}/irccdctl/command/rule-list.md ${html_SOURCE_DIR}/irccdctl/command/server-cmode.md ${html_SOURCE_DIR}/irccdctl/command/server-cnotice.md ${html_SOURCE_DIR}/irccdctl/command/server-connect.md diff -r f3c27790d0d1 -r 9be4f8a5cf1a doc/html/irccdctl/command/index.md --- a/doc/html/irccdctl/command/index.md Sun Jul 02 08:11:23 2017 +0200 +++ b/doc/html/irccdctl/command/index.md Wed Jul 05 18:20:26 2017 +0200 @@ -12,6 +12,7 @@ - [plugin-load](plugin-load.html) - [plugin-reload](plugin-reload.html) - [plugin-unload](plugin-unload.html) + - [rule-list](rule-list.html) - [server-cmode](server-cmode.html) - [server-cnotice](server-cnotice.html) - [server-connect](server-connect.html) diff -r f3c27790d0d1 -r 9be4f8a5cf1a doc/html/irccdctl/command/rule-list.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/html/irccdctl/command/rule-list.md Wed Jul 05 18:20:26 2017 +0200 @@ -0,0 +1,14 @@ +--- +title: rule-list +guide: yes +--- + +# rule-list + +List all rules. + +## Usage + +````nohighlight +$ irccdctl rule-list +```` diff -r f3c27790d0d1 -r 9be4f8a5cf1a doc/html/irccdctl/usage.md --- a/doc/html/irccdctl/usage.md Sun Jul 02 08:11:23 2017 +0200 +++ b/doc/html/irccdctl/usage.md Wed Jul 05 18:20:26 2017 +0200 @@ -16,6 +16,7 @@ irccdctl plugin-load logger irccdctl plugin-reload plugin irccdctl plugin-unload plugin + irccdctl rule-list irccdctl server-cmode server channel mode irccdctl server-cnotice server channel message irccdctl server-connect [options] id host [port] diff -r f3c27790d0d1 -r 9be4f8a5cf1a irccd/main.cpp --- a/irccd/main.cpp Sun Jul 02 08:11:23 2017 +0200 +++ b/irccd/main.cpp Wed Jul 05 18:20:26 2017 +0200 @@ -320,6 +320,7 @@ instance->commands().add(std::make_unique()); instance->commands().add(std::make_unique()); instance->commands().add(std::make_unique()); + instance->commands().add(std::make_unique()); // Load Javascript API and plugin loader. #if defined(WITH_JS) diff -r f3c27790d0d1 -r 9be4f8a5cf1a irccdctl/cli.cpp --- a/irccdctl/cli.cpp Sun Jul 02 08:11:23 2017 +0200 +++ b/irccdctl/cli.cpp Wed Jul 05 18:20:26 2017 +0200 @@ -16,7 +16,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include +#include #include @@ -840,6 +842,72 @@ } /* + * RuleListCli. + * ------------------------------------------------------------------ + */ + +namespace { + +void showRule(const nlohmann::json& json, int index) +{ + assert(json.is_object()); + + auto unjoin = [] (auto array) { + std::ostringstream oss; + + for (auto it = array.begin(); it != array.end(); ++it) { + if (!it->is_string()) + continue; + + oss << it->template get() << " "; + } + + return oss.str(); + }; + auto unstr = [] (auto action) { + if (action.is_string() && action == "accept") + return "accept"; + else + return "drop"; + }; + + std::cout << "rule: " << index << std::endl; + std::cout << "servers: " << unjoin(json["servers"]) << std::endl; + std::cout << "channels: " << unjoin(json["channels"]) << std::endl; + std::cout << "plugins: " << unjoin(json["plugins"]) << std::endl; + std::cout << "events: " << unjoin(json["events"]) << std::endl; + std::cout << "action: " << unstr(json["action"]) << std::endl; + std::cout << std::endl; +} + +} // !namespace + +RuleListCli::RuleListCli() + : Cli("rule-list", + "list all rules", + "rule-list", + "List all rules.\n\n" + "Example:\n" + "\tirccdctl rule-list") +{ +} + +void RuleListCli::exec(Irccdctl &irccdctl, const std::vector &) +{ + auto response = request(irccdctl); + auto pos = 0; + + check(response); + + for (const auto &obj : response["list"]) { + if (!obj.is_object()) + continue; + + showRule(obj, pos++); + } +} + +/* * WatchCli. * ------------------------------------------------------------------ */ diff -r f3c27790d0d1 -r 9be4f8a5cf1a irccdctl/cli.hpp --- a/irccdctl/cli.hpp Sun Jul 02 08:11:23 2017 +0200 +++ b/irccdctl/cli.hpp Wed Jul 05 18:20:26 2017 +0200 @@ -586,6 +586,27 @@ }; /* + * RuleListCli. + * ------------------------------------------------------------------ + */ + +/** + * \brief Implementation of irccdctl rule-list. + */ +class RuleListCli : public Cli { +public: + /** + * Default constructor. + */ + RuleListCli(); + + /** + * \copydoc Cli::exec + */ + void exec(Irccdctl &client, const std::vector &args) override; +}; + +/* * WatchCli. * ------------------------------------------------------------------ */ diff -r f3c27790d0d1 -r 9be4f8a5cf1a irccdctl/main.cpp --- a/irccdctl/main.cpp Sun Jul 02 08:11:23 2017 +0200 +++ b/irccdctl/main.cpp Wed Jul 05 18:20:26 2017 +0200 @@ -522,6 +522,7 @@ commands.push_back(std::make_unique()); commands.push_back(std::make_unique()); commands.push_back(std::make_unique()); + commands.push_back(std::make_unique()); commands.push_back(std::make_unique()); } diff -r f3c27790d0d1 -r 9be4f8a5cf1a libirccd/irccd/command.cpp --- a/libirccd/irccd/command.cpp Sun Jul 02 08:11:23 2017 +0200 +++ b/libirccd/irccd/command.cpp Wed Jul 05 18:20:26 2017 +0200 @@ -70,6 +70,34 @@ }); } +nlohmann::json toJson(const Rule &rule) +{ + auto join = [] (const auto &set) { + auto array = nlohmann::json::array(); + + for (const auto &entry : set) + array.push_back(entry); + + return array; + }; + auto str = [] (auto action) { + switch (action) { + case RuleAction::Accept: + return "accept"; + default: + return "drop"; + } + }; + + return { + { "servers", join(rule.servers()) }, + { "channels", join(rule.channels()) }, + { "plugins", join(rule.plugins()) }, + { "events", join(rule.events()) }, + { "action", str(rule.action()) } + }; +} + } // !namespace PluginConfigCommand::PluginConfigCommand() @@ -421,6 +449,21 @@ client.success("server-topic"); } +RuleListCommand::RuleListCommand() + : Command("rule-list") +{ +} + +void RuleListCommand::exec(Irccd &irccd, TransportClient &client, const nlohmann::json &) +{ + auto array = nlohmann::json::array(); + + for (const auto& rule : irccd.rules().list()) + array.push_back(toJson(rule)); + + client.success("rule-list", {{ "list", std::move(array) }}); +} + } // !command } // !irccd diff -r f3c27790d0d1 -r 9be4f8a5cf1a libirccd/irccd/command.hpp --- a/libirccd/irccd/command.hpp Sun Jul 02 08:11:23 2017 +0200 +++ b/libirccd/irccd/command.hpp Wed Jul 05 18:20:26 2017 +0200 @@ -458,6 +458,22 @@ void exec(Irccd &irccd, TransportClient &client, const nlohmann::json &args) override; }; +/** + * \brief Implementation of rule-list transport command. + */ +class IRCCD_EXPORT RuleListCommand : public Command { +public: + /** + * Constructor. + */ + RuleListCommand(); + + /** + * \copydoc Command::exec + */ + void exec(Irccd &irccd, TransportClient &client, const nlohmann::json &args) override; +}; + } // !command } // !irccd diff -r f3c27790d0d1 -r 9be4f8a5cf1a tests/CMakeLists.txt --- a/tests/CMakeLists.txt Sun Jul 02 08:11:23 2017 +0200 +++ b/tests/CMakeLists.txt Wed Jul 05 18:20:26 2017 +0200 @@ -26,6 +26,7 @@ add_subdirectory(cmd-plugin-load) add_subdirectory(cmd-plugin-reload) add_subdirectory(cmd-plugin-unload) + add_subdirectory(cmd-rule-list) add_subdirectory(cmd-server-cmode) add_subdirectory(cmd-server-cnotice) add_subdirectory(cmd-server-connect) diff -r f3c27790d0d1 -r 9be4f8a5cf1a tests/cmd-rule-list/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/cmd-rule-list/CMakeLists.txt Wed Jul 05 18:20:26 2017 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2017 David Demelier +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME cmd-rule-list + SOURCES main.cpp + LIBRARIES libirccd libirccdctl +) diff -r f3c27790d0d1 -r 9be4f8a5cf1a tests/cmd-rule-list/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/cmd-rule-list/main.cpp Wed Jul 05 18:20:26 2017 +0200 @@ -0,0 +1,140 @@ +/* + * main.cpp -- test rule-list remote command + * + * Copyright (c) 2013-2017 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +using namespace irccd; +using namespace irccd::command; + +class RuleListCommandTest : public CommandTester { +protected: + nlohmann::json m_result; + + /* + * Rule sets are unordered so use this function to search a string in + * the JSON array. + */ + inline bool contains(const nlohmann::json &array, const std::string &str) + { + for (const auto &v : array) + if (v.is_string() && v == str) + return true; + + return false; + } + +public: + RuleListCommandTest() + : CommandTester(std::make_unique()) + { + m_irccd.rules().add(Rule( + { "s1", "s2" }, + { "c1", "c2" }, + { "o1", "o2" }, + { "p1", "p2" }, + { "onMessage", "onCommand" }, + RuleAction::Drop + )); + m_irccd.rules().add(Rule( + { "s1", }, + { "c1", }, + { "o1", }, + { "p1", }, + { "onMessage", }, + RuleAction::Accept + )); + m_irccdctl.client().request({{ "command", "rule-list" }}); + m_irccdctl.client().onMessage.connect([&] (auto result) { + m_result = result; + }); + } +}; + +TEST_F(RuleListCommandTest, basic) +{ + try { + poll([&] () { + return m_result.is_object(); + }); + + ASSERT_TRUE(m_result.is_object()); + ASSERT_TRUE(m_result["list"].is_array()); + ASSERT_EQ(2U, m_result["list"].size()); + + // Rule 0. + { + auto servers = m_result["list"][0]["servers"]; + auto channels = m_result["list"][0]["channels"]; + auto plugins = m_result["list"][0]["plugins"]; + auto events = m_result["list"][0]["events"]; + + ASSERT_TRUE(contains(servers, "s1")); + ASSERT_TRUE(contains(servers, "s2")); + ASSERT_TRUE(contains(channels, "c1")); + ASSERT_TRUE(contains(channels, "c2")); + ASSERT_TRUE(contains(plugins, "p1")); + ASSERT_TRUE(contains(plugins, "p2")); + ASSERT_TRUE(contains(events, "onMessage")); + ASSERT_TRUE(contains(events, "onCommand")); + ASSERT_EQ("drop", m_result["list"][0]["action"]); + } + + // Rule 1. + { + auto servers = m_result["list"][1]["servers"]; + auto channels = m_result["list"][1]["channels"]; + auto plugins = m_result["list"][1]["plugins"]; + auto events = m_result["list"][1]["events"]; + + ASSERT_TRUE(contains(servers, "s1")); + ASSERT_TRUE(contains(channels, "c1")); + ASSERT_TRUE(contains(plugins, "p1")); + ASSERT_TRUE(contains(events, "onMessage")); + ASSERT_EQ("accept", m_result["list"][1]["action"]); + } + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(RuleListCommandTest, empty) +{ + m_irccd.rules().remove(0); + m_irccd.rules().remove(0); + + try { + poll([&] () { + return m_result.is_object(); + }); + + ASSERT_TRUE(m_result.is_object()); + ASSERT_TRUE(m_result["list"].is_array()); + ASSERT_EQ(0U, m_result["list"].size()); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}