Status Config::genPack(const std::string& name, const std::string& source, const std::string& target) { // If the pack value is a string (and not a JSON object) then it is a // resource to be handled by the config plugin. PluginResponse response; PluginRequest request = { {"action", "genPack"}, {"name", name}, {"value", target}}; Registry::call("config", request, response); if (response.size() == 0 || response[0].count(name) == 0) { return Status(1, "Invalid plugin response"); } try { pt::ptree pack_tree; std::stringstream pack_stream; pack_stream << response[0][name]; pt::read_json(pack_stream, pack_tree); addPack(name, source, pack_tree); } catch (const pt::json_parser::json_parser_error& /* e */) { LOG(WARNING) << "Error parsing the pack JSON: " << name; } return Status(0); }
TEST_F(PacksTests, test_discovery_cache) { auto c = Config(); c.addPack("kernel", "", getPackWithValidDiscovery()); size_t query_count = 0; for (size_t i = 0; i < 5; i++) { c.scheduledQueries( ([&query_count](const std::string& name, const ScheduledQuery& query) { query_count++; })); } EXPECT_EQ(query_count, 5U); size_t pack_count = 0U; c.packs(([&pack_count](Pack& p) { pack_count++; EXPECT_EQ(p.getStats().total, 5); EXPECT_EQ(p.getStats().hits, 4); EXPECT_EQ(p.getStats().misses, 1); })); EXPECT_EQ(pack_count, 1U); }
TEST_F(PacksTests, test_discovery_cache) { auto c = Config(); // This pack and discovery query are valid, expect the SQL to execute. c.addPack("valid_discovery_pack", "", getPackWithValidDiscovery()); size_t query_count = 0U; size_t query_attemts = 5U; for (size_t i = 0; i < query_attemts; i++) { c.scheduledQueries( ([&query_count](const std::string& name, const ScheduledQuery& query) { query_count++; })); } EXPECT_EQ(query_count, query_attemts); size_t pack_count = 0U; c.packs(([&pack_count, query_attemts](Pack& p) { pack_count++; EXPECT_EQ(p.getStats().total, query_attemts); EXPECT_EQ(p.getStats().hits, query_attemts - 1); EXPECT_EQ(p.getStats().misses, 1U); })); EXPECT_EQ(pack_count, 1U); }
DefinitionManager::DefinitionManager(QWidget *parent) : QWidget(parent), isUpdating(false), dimensionManager(DimensionIdentifier::Instance()), blockManager(BlockIdentifier::Instance()), biomeManager(BiomeIdentifier::Instance()), entityManager(EntityIdentifier::Instance()), flatteningConverter(FlatteningConverter::Instance()) { setWindowFlags(Qt::Window); setWindowTitle(tr("Definitions")); QVBoxLayout *layout = new QVBoxLayout; QStringList labels; labels << tr("Name") << tr("Version") << tr("Type"); table = new QTableWidget(0, 3); table->setHorizontalHeaderLabels(labels); table->horizontalHeader()->setSectionResizeMode( QHeaderView::ResizeToContents); table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); table->horizontalHeader()->setHighlightSections(false); table->verticalHeader()->hide(); table->setShowGrid(false); table->setSelectionBehavior(QAbstractItemView::SelectRows); layout->addWidget(table, 1); QWidget *buttonBar = new QWidget; QHBoxLayout *buttons = new QHBoxLayout; QPushButton *add = new QPushButton(tr("Add Pack...")); connect(add, SIGNAL(clicked()), this, SLOT(addPack())); buttons->addWidget(add); QPushButton *remove = new QPushButton(tr("Remove Pack")); connect(remove, SIGNAL(clicked()), this, SLOT(removePack())); connect(this, SIGNAL(packSelected(bool)), remove, SLOT(setEnabled(bool))); buttons->addWidget(remove); QPushButton *save = new QPushButton(tr("Export Pack...")); connect(save, SIGNAL(clicked()), this, SLOT(exportPack())); connect(this, SIGNAL(packSelected(bool)), save, SLOT(setEnabled(bool))); buttons->addWidget(save); buttonBar->setLayout(buttons); layout->addWidget(buttonBar, 0); emit packSelected(false); setLayout(layout); QSettings settings; sorted = settings.value("packs").toList(); // clean old hashed files without extra seed QString destdir = QStandardPaths::writableLocation(QStandardPaths::DataLocation); const QStringList old_hashed_list { "1050220429", "1241760321", "1443276275", "1798448990", "2422344665" }; for ( const auto& old_hashed_file : old_hashed_list ) { QString old_path = destdir + "/" + old_hashed_file + ".json"; QFile::remove(old_path); sorted.removeOne(old_path); } // copy over built-in definitions if necessary QDir dir; dir.mkpath(destdir); QDirIterator it(":/definitions", QDir::Files | QDir::Readable); while (it.hasNext()) { it.next(); installJson(it.filePath(), false, false); } settings.setValue("packs", sorted); // we load the definitions backwards for priority. for (int i = sorted.length() - 1; i >= 0; i--) loadDefinition(sorted[i].toString()); // hook up table selection signal connect(table, SIGNAL(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)), this, SLOT(selectedPack(QTableWidgetItem*, QTableWidgetItem*))); // fill out table refresh(); }
Status Config::updateSource(const std::string& source, const std::string& json) { // Compute a 'synthesized' hash using the content before it is parsed. hashSource(source, json); // Remove all packs from this source. schedule_->removeAll(source); // Remove all files from this source. removeFiles(source); // load the config (source.second) into a pt::ptree pt::ptree tree; try { auto clone = json; stripConfigComments(clone); std::stringstream json_stream; json_stream << clone; pt::read_json(json_stream, tree); } catch (const pt::json_parser::json_parser_error& /* e */) { return Status(1, "Error parsing the config JSON"); } // extract the "schedule" key and store it as the main pack if (tree.count("schedule") > 0 && !Registry::external()) { auto& schedule = tree.get_child("schedule"); pt::ptree main_pack; main_pack.add_child("queries", schedule); addPack("main", source, main_pack); } if (tree.count("scheduledQueries") > 0 && !Registry::external()) { auto& scheduled_queries = tree.get_child("scheduledQueries"); pt::ptree queries; for (const std::pair<std::string, pt::ptree>& query : scheduled_queries) { auto query_name = query.second.get<std::string>("name", ""); if (query_name.empty()) { return Status(1, "Error getting name from legacy scheduled query"); } queries.add_child(query_name, query.second); } pt::ptree legacy_pack; legacy_pack.add_child("queries", queries); addPack("legacy_main", source, legacy_pack); } // extract the "packs" key into additional pack objects if (tree.count("packs") > 0 && !Registry::external()) { auto& packs = tree.get_child("packs"); for (const auto& pack : packs) { auto value = packs.get<std::string>(pack.first, ""); if (value.empty()) { // The pack is a JSON object, treat the content as pack data. addPack(pack.first, source, pack.second); } else { genPack(pack.first, source, value); } } } applyParsers(source, tree, false); return Status(0, "OK"); }
Status Config::updateSource(const std::string& name, const std::string& json) { // Compute a 'synthesized' hash using the content before it is parsed. hashSource(name, json); // load the config (source.second) into a pt::ptree pt::ptree tree; try { auto clone = json; stripConfigComments(clone); std::stringstream json_stream; json_stream << clone; pt::read_json(json_stream, tree); } catch (const pt::json_parser::json_parser_error& e) { return Status(1, "Error parsing the config JSON"); } // extract the "schedule" key and store it as the main pack if (tree.count("schedule") > 0 && !Registry::external()) { auto& schedule = tree.get_child("schedule"); pt::ptree main_pack; main_pack.add_child("queries", schedule); addPack("main", name, main_pack); } if (tree.count("scheduledQueries") > 0 && !Registry::external()) { auto& scheduled_queries = tree.get_child("scheduledQueries"); pt::ptree queries; for (const std::pair<std::string, pt::ptree>& query : scheduled_queries) { auto query_name = query.second.get<std::string>("name", ""); if (query_name.empty()) { return Status(1, "Error getting name from legacy scheduled query"); } queries.add_child(query_name, query.second); } pt::ptree legacy_pack; legacy_pack.add_child("queries", queries); addPack("legacy_main", name, legacy_pack); } // extract the "packs" key into additional pack objects if (tree.count("packs") > 0 && !Registry::external()) { auto& packs = tree.get_child("packs"); for (const auto& pack : packs) { auto value = packs.get<std::string>(pack.first, ""); if (value.empty()) { addPack(pack.first, name, pack.second); } else { PluginResponse response; PluginRequest request = { {"action", "genPack"}, {"name", pack.first}, {"value", value}}; Registry::call("config", request, response); if (response.size() == 0 || response[0].count(pack.first) == 0) { continue; } try { pt::ptree pack_tree; std::stringstream pack_stream; pack_stream << response[0][pack.first]; pt::read_json(pack_stream, pack_tree); addPack(pack.first, name, pack_tree); } catch (const pt::json_parser::json_parser_error& e) { LOG(WARNING) << "Error parsing the pack JSON: " << pack.first; } } } } // Iterate each parser. for (const auto& plugin : Registry::all("config_parser")) { std::shared_ptr<ConfigParserPlugin> parser = nullptr; try { parser = std::dynamic_pointer_cast<ConfigParserPlugin>(plugin.second); } catch (const std::bad_cast& e) { LOG(ERROR) << "Error casting config parser plugin: " << plugin.first; } if (parser == nullptr || parser.get() == nullptr) { continue; } // For each key requested by the parser, add a property tree reference. std::map<std::string, pt::ptree> parser_config; for (const auto& key : parser->keys()) { if (tree.count(key) > 0) { parser_config[key] = tree.get_child(key); } else { parser_config[key] = pt::ptree(); } } // The config parser plugin will receive a copy of each property tree for // each top-level-config key. The parser may choose to update the config's // internal state parser->update(name, parser_config); } return Status(0, "OK"); }
Status Config::updateSource(const std::string& name, const std::string& json) { // Compute a 'synthesized' hash using the content before it is parsed. hashSource(name, json); // Remove all packs from this source. schedule_->removeAll(name); // Remove all files from this source. removeFiles(name); // load the config (source.second) into a pt::ptree pt::ptree tree; try { auto clone = json; stripConfigComments(clone); std::stringstream json_stream; json_stream << clone; pt::read_json(json_stream, tree); } catch (const pt::json_parser::json_parser_error& e) { return Status(1, "Error parsing the config JSON"); } // extract the "schedule" key and store it as the main pack if (tree.count("schedule") > 0 && !Registry::external()) { auto& schedule = tree.get_child("schedule"); pt::ptree main_pack; main_pack.add_child("queries", schedule); addPack("main", name, main_pack); } if (tree.count("scheduledQueries") > 0 && !Registry::external()) { auto& scheduled_queries = tree.get_child("scheduledQueries"); pt::ptree queries; for (const std::pair<std::string, pt::ptree>& query : scheduled_queries) { auto query_name = query.second.get<std::string>("name", ""); if (query_name.empty()) { return Status(1, "Error getting name from legacy scheduled query"); } queries.add_child(query_name, query.second); } pt::ptree legacy_pack; legacy_pack.add_child("queries", queries); addPack("legacy_main", name, legacy_pack); } // extract the "packs" key into additional pack objects if (tree.count("packs") > 0 && !Registry::external()) { auto& packs = tree.get_child("packs"); for (const auto& pack : packs) { auto value = packs.get<std::string>(pack.first, ""); if (value.empty()) { // The pack is a JSON object, treat the content as pack data. addPack(pack.first, name, pack.second); } else { // If the pack value is a string (and not a JSON object) then it is a // resource to be handled by the config plugin. PluginResponse response; PluginRequest request = { {"action", "genPack"}, {"name", pack.first}, {"value", value}}; Registry::call("config", request, response); if (response.size() == 0 || response[0].count(pack.first) == 0) { continue; } try { pt::ptree pack_tree; std::stringstream pack_stream; pack_stream << response[0][pack.first]; pt::read_json(pack_stream, pack_tree); addPack(pack.first, name, pack_tree); } catch (const pt::json_parser::json_parser_error& e) { LOG(WARNING) << "Error parsing the pack JSON: " << pack.first; } } } } applyParsers(name, tree, false); return Status(0, "OK"); }
DefinitionManager::DefinitionManager(QWidget *parent) : QWidget(parent) { setWindowFlags(Qt::Window); setWindowTitle(tr("Definitions")); QVBoxLayout *layout=new QVBoxLayout; QStringList labels; labels<<tr("Name")<<tr("Version")<<tr("Type"); //<<tr("Active"); table=new QTableWidget(0,3); table->setHorizontalHeaderLabels(labels); table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); table->horizontalHeader()->setSectionResizeMode(0,QHeaderView::Stretch); table->horizontalHeader()->setHighlightSections(false); table->verticalHeader()->hide(); table->setShowGrid(false); table->setSelectionBehavior(QAbstractItemView::SelectRows); layout->addWidget(table,1); QWidget *buttonBar=new QWidget; QHBoxLayout *buttons=new QHBoxLayout; QPushButton *add=new QPushButton(tr("Add Pack...")); connect(add,SIGNAL(clicked()), this,SLOT(addPack())); buttons->addWidget(add); QPushButton *remove=new QPushButton(tr("Remove Pack")); connect(remove,SIGNAL(clicked()), this,SLOT(removePack())); connect(this,SIGNAL(packSelected(bool)), remove,SLOT(setEnabled(bool))); buttons->addWidget(remove); QPushButton *save=new QPushButton(tr("Export Pack...")); connect(save,SIGNAL(clicked()), this,SLOT(exportPack())); connect(this,SIGNAL(packSelected(bool)), save,SLOT(setEnabled(bool))); buttons->addWidget(save); buttonBar->setLayout(buttons); layout->addWidget(buttonBar,0); emit packSelected(false); setLayout(layout); dimensionList = new Dimensions; blocks=new BlockIdentifier; biomes=new BiomeIdentifier; QSettings settings; sorted=settings.value("packs").toList(); lastUpdated=settings.value("packupdates").toHash(); //copy over built-in definitions if necessary QString defdir=QStandardPaths::writableLocation(QStandardPaths::DataLocation); QDir dir; dir.mkpath(defdir); QDirIterator it(":/definitions",QDir::Files|QDir::Readable); while (it.hasNext()) { it.next(); installJson(it.filePath(),false,false); } settings.setValue("packs",sorted); // we load the definitions backwards for priority. for (int i=sorted.length()-1;i>=0;i--) loadDefinition(sorted[i].toString()); //hook up table selection signal connect(table,SIGNAL(currentItemChanged(QTableWidgetItem*,QTableWidgetItem*)), this, SLOT(selectedPack(QTableWidgetItem*,QTableWidgetItem*))); //fill out table refresh(); }