#include "store/rules.hpp" #include #include #include namespace velox::daemon::store { namespace proto = velox::proto; namespace { proto::Rule project_row(Stmt& st) { proto::Rule r; r.ruleId = st.column_text(0); if (!st.column_is_null(1)) r.name = st.column_text(1); r.enabled = st.column_int(2) != 0; r.priority = st.column_int(3); if (auto j = nlohmann::json::parse(st.column_text(4), nullptr, false); !j.is_discarded()) { if (auto m = proto::parse(j, "match")) r.match = *m; } if (auto j = nlohmann::json::parse(st.column_text(5), nullptr, false); !j.is_discarded()) { if (auto a = proto::parse(j, "action")) r.action = *a; } return r; } std::string new_rule_id() { std::random_device rd; std::uniform_int_distribution d; char buf[17]; std::snprintf(buf, sizeof(buf), "%016llx", static_cast(d(rd))); return std::string(buf); } } // namespace DbResult> Rules::list() { auto st = db_.prepare( "SELECT rule_id, name, enabled, priority, match, action FROM rules " "ORDER BY priority, rule_id"); if (!st) return std::unexpected(st.error()); std::vector out; for (;;) { auto row = st->step(); if (!row) return std::unexpected(row.error()); if (!*row) break; out.push_back(project_row(*st)); } return out; } DbResult> Rules::apply(std::vector upsert, const std::vector& remove) { auto txn = db_.transaction([&]() -> DbResult { for (const auto& id : remove) { auto del = db_.prepare("DELETE FROM rules WHERE rule_id = ?1"); if (!del) return std::unexpected(del.error()); if (auto b = del->bind(1, std::string_view(id)); !b) return std::unexpected(b.error()); if (auto r = del->step(); !r) return std::unexpected(r.error()); } for (auto& rule : upsert) { if (rule.ruleId.empty()) rule.ruleId = new_rule_id(); nlohmann::json match_json = rule.match; nlohmann::json action_json = rule.action; auto st = db_.prepare( "INSERT INTO rules(rule_id, name, enabled, priority, match, action) " "VALUES(?1,?2,?3,?4,?5,?6) " "ON CONFLICT(rule_id) DO UPDATE SET " "name=excluded.name, enabled=excluded.enabled, priority=excluded.priority, " "match=excluded.match, action=excluded.action"); if (!st) return std::unexpected(st.error()); if (auto b = st->bind(1, std::string_view(rule.ruleId)); !b) return std::unexpected(b.error()); if (auto r = rule.name ? st->bind(2, std::string_view(*rule.name)) : st->bind_null(2); !r) return std::unexpected(r.error()); if (auto b = st->bind(3, static_cast(rule.enabled)); !b) return std::unexpected(b.error()); if (auto b = st->bind(4, rule.priority); !b) return std::unexpected(b.error()); if (auto b = st->bind(5, std::string_view(match_json.dump())); !b) return std::unexpected(b.error()); if (auto b = st->bind(6, std::string_view(action_json.dump())); !b) return std::unexpected(b.error()); if (auto r = st->step(); !r) return std::unexpected(r.error()); } return {}; }); if (!txn) return std::unexpected(txn.error()); return list(); } } // namespace velox::daemon::store