mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-02 10:58:45 -05:00
This API allows for managing message templates order. DB Migration: adding a new column to templates tables.
162 lines
5.8 KiB
C++
162 lines
5.8 KiB
C++
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
|
|
|
#include "Helpers.hpp"
|
|
|
|
#include <Utils.hpp>
|
|
#include <vector>
|
|
#include <set>
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
namespace
|
|
{
|
|
std::string readContent(const std::string &filename) noexcept
|
|
{
|
|
auto getFileSize = [](FILE *fd) -> std::size_t {
|
|
std::fseek(fd, 0, SEEK_END);
|
|
const auto size = std::ftell(fd);
|
|
std::rewind(fd);
|
|
return size;
|
|
};
|
|
|
|
std::vector<char> fileContent;
|
|
if (const auto fp = std::fopen(filename.c_str(), "r")) {
|
|
const auto fileSize = getFileSize(fp);
|
|
|
|
fileContent.reserve(fileSize + 1);
|
|
std::fread(fileContent.data(), 1, fileSize, fp);
|
|
std::fclose(fp);
|
|
fileContent[fileSize] = '\0';
|
|
return fileContent.data();
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::vector<std::string> readCommands(const std::filesystem::path &filePath)
|
|
{
|
|
const auto fileContent = readContent(filePath.c_str());
|
|
std::string currentStatement{};
|
|
std::vector<std::string> statements{};
|
|
|
|
std::string line{};
|
|
for (const auto &c : fileContent) {
|
|
if (c != '\n') {
|
|
line += c;
|
|
}
|
|
else {
|
|
if (line.empty() || utils::startsWith(line, "--")) {
|
|
line.clear();
|
|
continue;
|
|
}
|
|
if (utils::endsWith(line, ";")) {
|
|
statements.push_back(currentStatement + line);
|
|
currentStatement.clear();
|
|
line.clear();
|
|
continue;
|
|
}
|
|
currentStatement += line;
|
|
|
|
line.clear();
|
|
}
|
|
}
|
|
return statements;
|
|
}
|
|
|
|
std::set<std::filesystem::path> listVersionDirectories(const std::filesystem::path &path, const std::string &dbName)
|
|
{
|
|
std::set<std::filesystem::path> versions;
|
|
for (const auto &entry : std::filesystem::directory_iterator(path)) {
|
|
if (entry.is_directory() and entry.path().filename() == dbName) {
|
|
for (const auto &version : std::filesystem::directory_iterator(entry.path())) {
|
|
versions.insert(version.path());
|
|
}
|
|
}
|
|
}
|
|
return versions;
|
|
}
|
|
|
|
std::vector<std::filesystem::path> searchForScripts(const std::filesystem::path &pathToVersion,
|
|
const std::string &scriptName)
|
|
{
|
|
std::vector<std::filesystem::path> scripts{};
|
|
if (std::filesystem::exists(pathToVersion / scriptName)) {
|
|
scripts.push_back(pathToVersion / scriptName);
|
|
}
|
|
else {
|
|
for (const auto &subVersion : std::filesystem::directory_iterator(pathToVersion)) {
|
|
if (std::filesystem::exists(subVersion.path() / scriptName)) {
|
|
scripts.push_back(subVersion.path() / scriptName);
|
|
}
|
|
}
|
|
}
|
|
return scripts;
|
|
}
|
|
|
|
std::vector<std::filesystem::path> listFiles(const std::filesystem::path &path,
|
|
const std::string &prefix,
|
|
const bool withDevelopment)
|
|
{
|
|
constexpr auto up_sql = "up.sql";
|
|
constexpr auto devel_sql = "devel.sql";
|
|
std::vector<std::filesystem::path> files;
|
|
for (const auto &version : listVersionDirectories(path, prefix)) {
|
|
auto scriptsPath = searchForScripts(version, up_sql);
|
|
if (not scriptsPath.empty()) {
|
|
files.insert(files.end(), scriptsPath.begin(), scriptsPath.end());
|
|
}
|
|
if (withDevelopment) {
|
|
scriptsPath = searchForScripts(version, devel_sql);
|
|
if (not scriptsPath.empty()) {
|
|
files.insert(files.end(), scriptsPath.begin(), scriptsPath.end());
|
|
}
|
|
}
|
|
}
|
|
return files;
|
|
}
|
|
|
|
bool executeOnDb(Database &db, const std::vector<std::string> &statements)
|
|
{
|
|
return std::all_of(statements.begin(), statements.end(), [&db](const auto &statement) {
|
|
return db.execute(statement.c_str());
|
|
});
|
|
}
|
|
} // namespace
|
|
|
|
namespace db::tests
|
|
{
|
|
std::filesystem::path currentFileLocation()
|
|
{
|
|
const std::string path = __FILE__;
|
|
return std::filesystem::path{path.substr(0, path.rfind('/'))};
|
|
}
|
|
|
|
std::filesystem::path getScriptsPath()
|
|
{
|
|
return currentFileLocation() / "../databases/migration";
|
|
}
|
|
|
|
/// TODO:
|
|
/// This function is obviously wrong and should not exist in the first place. Unfortunately, due to the
|
|
/// current directory structure, product specific modules and some unit tests are placed in common directories.
|
|
/// After proper module-db and service-db split it wont' be needed as every product-specific db unit test will be
|
|
/// placed in the correct directory.
|
|
std::filesystem::path getPurePhoneScriptsPath()
|
|
{
|
|
return currentFileLocation() / "../../products/PurePhone/services/db/databases/migration";
|
|
}
|
|
|
|
bool DatabaseScripts::operator()(Database &db)
|
|
{
|
|
const std::filesystem::path dbpath = db.getName();
|
|
const std::string dbname = dbpath.filename().replace_extension();
|
|
|
|
const auto scripts = listFiles(directory, dbname, withDevelopment);
|
|
return std::all_of(scripts.begin(), scripts.end(), [&db](const auto &script) {
|
|
return executeOnDb(db, readCommands(script));
|
|
});
|
|
}
|
|
DatabaseScripts::DatabaseScripts(std::filesystem::path directory, bool withDevelopment)
|
|
: directory{std::move(directory)}, withDevelopment{withDevelopment}
|
|
{}
|
|
} // namespace db::tests
|