changeset 448:9be4f8a5cf1a

Irccdctl: implement rule-list
author David Demelier <markand@malikania.fr>
date Wed, 05 Jul 2017 18:20:26 +0200
parents f3c27790d0d1
children 9968eac538e6
files doc/html/CMakeLists.txt doc/html/irccdctl/command/index.md doc/html/irccdctl/command/rule-list.md doc/html/irccdctl/usage.md irccd/main.cpp irccdctl/cli.cpp irccdctl/cli.hpp irccdctl/main.cpp libirccd/irccd/command.cpp libirccd/irccd/command.hpp tests/CMakeLists.txt tests/cmd-rule-list/CMakeLists.txt tests/cmd-rule-list/main.cpp
diffstat 13 files changed, 331 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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)
--- /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
+````
--- 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]
--- 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<command::ServerPartCommand>());
     instance->commands().add(std::make_unique<command::ServerReconnectCommand>());
     instance->commands().add(std::make_unique<command::ServerTopicCommand>());
+    instance->commands().add(std::make_unique<command::RuleListCommand>());
 
     // Load Javascript API and plugin loader.
 #if defined(WITH_JS)
--- 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 <cassert>
 #include <iostream>
+#include <sstream>
 
 #include <json.hpp>
 
@@ -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<std::string>() << " ";
+        }
+
+        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<std::string> &)
+{
+    auto response = request(irccdctl);
+    auto pos = 0;
+
+    check(response);
+
+    for (const auto &obj : response["list"]) {
+        if (!obj.is_object())
+            continue;
+
+        showRule(obj, pos++);
+    }
+}
+
+/*
  * WatchCli.
  * ------------------------------------------------------------------
  */
--- 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<std::string> &args) override;
+};
+
+/*
  * WatchCli.
  * ------------------------------------------------------------------
  */
--- 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<cli::ServerPartCli>());
     commands.push_back(std::make_unique<cli::ServerReconnectCli>());
     commands.push_back(std::make_unique<cli::ServerTopicCli>());
+    commands.push_back(std::make_unique<cli::RuleListCli>());
     commands.push_back(std::make_unique<cli::WatchCli>());
 }
 
--- 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
--- 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
--- 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)
--- /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 <markand@malikania.fr>
+#
+# 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
+)
--- /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 <markand@malikania.fr>
+ *
+ * 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 <command.hpp>
+#include <command-tester.hpp>
+#include <service.hpp>
+
+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<RuleListCommand>())
+    {
+        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();
+}