Skip to content

Commit

Permalink
Added register alarm, fetch alarms and register translation
Browse files Browse the repository at this point in the history
  • Loading branch information
Ómar Högni Guðmarsson committed Jun 18, 2024
1 parent 53fbcc4 commit 4faefa3
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 15 deletions.
104 changes: 97 additions & 7 deletions exes/themis/inc/alarm_database.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
#pragma once

#include <fmt/format.h>
#include <openssl/sha.h>
#include <sqlite_modern_cpp.h>
#include <array>
#include <filesystem>
#include <string>
#include <tfc/progbase.hpp>
#include <sqlite_modern_cpp.h>
#include <fmt/format.h>
#include <tfc/snitch/common.hpp>

namespace tfc::themis {

using enum tfc::snitch::level_e;
using enum tfc::snitch::api::active_e;

inline auto config_file_name_populate_dir() -> std::string {
auto const file{ base::make_config_file_name(base::get_exe_name(), "db") };
std::filesystem::create_directories(file.parent_path());
Expand All @@ -29,10 +36,11 @@ CREATE TABLE IF NOT EXISTS Alarms(
)";
db_ << R"(
CREATE TABLE IF NOT EXISTS AlarmTranslation(
sha1sum TEXT PRIMARY KEY,
sha1sum TEXT,
locale TEXT NOT NULL,
short_msg TEXT NOT NULL,
msg TEXT NOT NULL,
PRIMARY KEY(sha1sum, locale) ON CONFLICT REPLACE,
FOREIGN KEY(sha1sum) REFERENCES Alarms(sha1sum)
);
)";
Expand Down Expand Up @@ -72,11 +80,93 @@ CREATE TABLE IF NOT EXISTS AlarmVariables(
* @param alarm_level The alarm level
* @return The alarm ID
*/
[[nodiscard]] auto register_alarm(std::string_view tfc_id, std::string_view msg, std::string_view short_msg, bool latching, std::uint32_t alarm_level) -> std::int64_t {
db_ << fmt::format("INSERT INTO Alarms(tfc_id, sha1sum, alarm_level, short_msg_en, msg_en, alarm_latching) VALUES('{}','{}',{},'{}','{}',{});", tfc_id, msg, alarm_level, short_msg, msg, latching ? 1 : 0);
return db_.last_insert_rowid();
[[nodiscard]] auto register_alarm(std::string_view tfc_id,
std::string_view msg,
std::string_view short_msg,
bool latching,
tfc::snitch::level_e alarm_level) -> std::uint64_t {
std::string sha1_ascii = get_sha1(fmt::format("{}{}", msg, short_msg));
db_ << fmt::format(
"INSERT INTO Alarms(tfc_id, sha1sum, alarm_level, short_msg_en, msg_en, alarm_latching) "
"VALUES('{}','{}',{},'{}','{}',{});",
tfc_id, sha1_ascii, static_cast<std::uint8_t>(alarm_level), short_msg, msg, latching ? 1 : 0);
std::int64_t last_insert_rowid = db_.last_insert_rowid();
if (last_insert_rowid < 0) {
throw std::runtime_error("Failed to insert alarm into database");
}
return static_cast<std::uint64_t>(last_insert_rowid);
}

[[nodiscard]] auto list_alarms() -> std::vector<tfc::snitch::api::alarm> {
std::unordered_map<std::uint64_t, tfc::snitch::api::alarm> alarms;
std::string query = R"(
SELECT
alarm_id,
tfc_id,
Alarms.sha1sum,
alarm_level,
Alarms.short_msg_en,
Alarms.msg_en,
alarm_latching,
locale,
AlarmTranslation.short_msg,
AlarmTranslation.msg
FROM Alarms
LEFT JOIN AlarmTranslation
ON Alarms.sha1sum = AlarmTranslation.sha1sum;
)";
// Accept the second table paramters as unique ptr's as the values can be null, and we want to preserve that
db_ << query >> [&](std::uint64_t alarm_id, std::string tfc_id, std::string sha1sum, std::uint8_t alarm_level,
std::string short_msg_en, std::string msg_en, bool alarm_latching,
std::unique_ptr<std::string> locale, std::unique_ptr<std::string> translated_short_msg,
std::unique_ptr<std::string> translated_msg) {
auto iterator = alarms.find(alarm_id);
if (iterator == alarms.end()) {
alarms.insert({ alarm_id, tfc::snitch::api::alarm{ alarm_id,
tfc_id,
short_msg_en,
msg_en,
sha1sum,
static_cast<tfc::snitch::level_e>(alarm_level),
alarm_latching,
{} } });
}
if (locale != nullptr && translated_short_msg != nullptr && translated_msg != nullptr) {
alarms[alarm_id].translations.insert({ *locale, { *translated_short_msg, *translated_msg } });
}
};
std::vector<tfc::snitch::api::alarm> alarm_list;
for (auto& [_, alarm] : alarms) {
alarm_list.push_back(alarm);
}
return alarm_list;
}

auto add_alarm_translation(std::string_view sha1sum,
std::string_view locale,
std::string_view short_msg,
std::string_view msg) -> void {
db_ << fmt::format("INSERT INTO AlarmTranslation(sha1sum, locale, short_msg, msg) VALUES('{}','{}','{}','{}');", sha1sum,
locale, short_msg, msg);
}

// Note. Use `echo -n "value" | sha1sum` to not hash the newline character and
// match the results from this function.
[[nodiscard]] static auto get_sha1(std::string_view msg) -> std::string {
// Calculate sha1
std::array<unsigned char, SHA_DIGEST_LENGTH> sha1_res;
SHA1(reinterpret_cast<const unsigned char*>(msg.data()), msg.size(), sha1_res.data());

// Convert sha1 to ascii
std::string sha1_ascii;
sha1_ascii.reserve(SHA_DIGEST_LENGTH * 2);
for (size_t i = 0; i < SHA_DIGEST_LENGTH; i++) {
sha1_ascii += fmt::format("{:02x}", sha1_res[i]);
}
return sha1_ascii;
}

private:
sqlite::database db_;
};
}
} // namespace tfc::themis
3 changes: 3 additions & 0 deletions exes/themis/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ find_path(BEXT_SML_INCLUDE_DIRS "boost/sml.hpp")

add_executable(themis_database_test themis_database_test.cpp)
find_package(unofficial-sqlite3 CONFIG REQUIRED)
find_package(OpenSSL CONFIG REQUIRED)
find_path(SQLITE_MODERN_CPP_INCLUDE_DIRS "sqlite_modern_cpp.h")

target_link_libraries(themis_database_test
PRIVATE
Boost::ut
tfc::base
tfc::snitch
unofficial::sqlite3::sqlite3
OpenSSL::Crypto
)

target_include_directories(themis_database_test
Expand Down
60 changes: 53 additions & 7 deletions exes/themis/tests/themis_database_test.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,64 @@
#include <alarm_database.hpp>
#include <boost/ut.hpp>
#include <iostream>
#include <tfc/progbase.hpp>
#include <boost/ut.hpp>
#include <alarm_database.hpp>
#include <tfc/snitch/common.hpp>

namespace ut = boost::ut;

using boost::ut::operator""_test;
using boost::ut::operator|;
using boost::ut::operator/;
using boost::ut::expect;
using tfc::themis::alarm_database;

auto main(int argc, char** argv) -> int {
tfc::base::init(argc, argv);

//"Database correctness check"_test = [] {
tfc::themis::alarm_database alarm_db(false);
auto insert_id = alarm_db.register_alarm("tfc_id", "msg", "short msg", false, 0);
std::cout << insert_id << std::endl;
//};
"sha1sum calculations correctness"_test =
[](auto& test_data) {
std::string input = test_data.first;
std::string expected = test_data.second;
expect(alarm_database::get_sha1(input) == expected)
<< fmt::format("{} != {}", alarm_database::get_sha1(input), expected);
} |
std::vector<std::pair<std::string, std::string>>{ { "msgshort msg", "654e9cca8eb076653201325a5cf07ed24ee71b52" },
{ "this is a test", "fa26be19de6bff93f70bc2308434e4a440bbad02" },
{ "another test", "afc8edc74ae9e7b8d290f945a6d613f1d264a2b2" } };

"Database correctness check"_test = [] {
tfc::themis::alarm_database alarm_db(true);

// Inserted alarm matches fetched alarm
expect(alarm_db.list_alarms().size() == 0);
auto insert_id = alarm_db.register_alarm("tfc_id", "msg", "short msg", false, tfc::snitch::level_e::info);
auto alarms = alarm_db.list_alarms();
expect(alarms.size() == 1);
expect(alarms.at(0).tfc_id == "tfc_id");
expect(alarms.at(0).translations.size() == 0);
expect(alarms.at(0).sha1sum == alarm_database::get_sha1("msgshort msg"));
expect(alarms.at(0).lvl == tfc::snitch::level_e::info);
expect(alarms.at(0).alarm_id == insert_id);

// Translations are attached to alarms
alarm_db.add_alarm_translation(alarm_db.list_alarms()[0].sha1sum, "es", "some spanish maybe",
"this is is also spanish believe me please");
alarms = alarm_db.list_alarms();
expect(alarms.size() == 1);
expect(alarms.at(0).translations.size() == 1);
auto iterator = alarms.at(0).translations.find("es");
expect(iterator != alarms.at(0).translations.end());
expect(iterator->second.description == "some spanish maybe");
expect(iterator->second.details == "this is is also spanish believe me please");

alarm_db.add_alarm_translation(alarm_db.list_alarms()[0].sha1sum, "is", "Some icelandic really",
"This is really some icelandic");
alarms = alarm_db.list_alarms();
expect(alarms.size() == 1);
expect(alarms.at(0).translations.size() == 2);
iterator = alarms.at(0).translations.find("is");
expect(iterator != alarms.at(0).translations.end());
expect(iterator->second.description == "Some icelandic really");
expect(iterator->second.details == "This is really some icelandic");
};
}
6 changes: 5 additions & 1 deletion libs/snitch/inc/public/tfc/snitch/common.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include <cstdint>
#include <string>
#include <unordered_map>

namespace tfc::snitch {

Expand All @@ -23,9 +25,11 @@ enum struct active_e : std::int32_t {
};

struct alarm {
std::uint64_t alarm_id;
std::string tfc_id;
std::string description;
std::string details;
bool active{};
std::string sha1sum;
level_e lvl{ level_e::unknown };
bool latching{};
struct translation {
Expand Down

0 comments on commit 4faefa3

Please sign in to comment.