got tgid editing via REST mostly working (#49)

pull/51/head
Patrick W3AXL 2 years ago committed by GitHub
parent 5d6c99da43
commit d8eff1f1f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -8,11 +8,13 @@
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
* *
* Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2024 Patrick McDonnell, W3AXL
* *
*/ */
#include "lookups/TalkgroupRulesLookup.h" #include "lookups/TalkgroupRulesLookup.h"
#include "Log.h" #include "Log.h"
#include "Timer.h" #include "Timer.h"
#include "Utils.h"
using namespace lookups; using namespace lookups;
@ -291,9 +293,9 @@ TalkgroupRuleGroupVoice TalkgroupRulesLookup::findByRewrite(uint32_t peerId, uin
/// <summary> /// <summary>
/// Saves loaded talkgroup rules. /// Saves loaded talkgroup rules.
/// </summary> /// </summary>
void TalkgroupRulesLookup::commit() bool TalkgroupRulesLookup::commit()
{ {
// bryanb: TODO TODO TODO return save();
} }
/// <summary> /// <summary>
@ -305,6 +307,67 @@ bool TalkgroupRulesLookup::getACL()
return m_acl; return m_acl;
} }
void TalkgroupRuleGroupVoice::getYaml(yaml::Node &node)
{
// Get all the properties
node["name"] = m_name;
yaml::Node config, source;
m_config.getYaml(config);
m_source.getYaml(source);
node["config"] = config;
node["source"] = source;
}
void TalkgroupRuleConfig::getYaml(yaml::Node &node)
{
// We have to convert the bools back to strings to pass to the yaml node
node["active"] = __BOOL_STR(m_active);
node["affiliated"] = __BOOL_STR(m_affiliated);
node["parrot"] = __BOOL_STR(m_parrot);
// Get the lists
yaml::Node inclusionList;
if (m_inclusion.size() > 0U) {
for (auto inc : m_inclusion) {
yaml::Node& newIn = inclusionList.push_back();
newIn = __INT_STR(inc);
}
}
node["inclusion"] = inclusionList;
yaml::Node exclusionList;
if (m_exclusion.size() > 0U) {
for (auto exc : m_exclusion) {
yaml::Node& newEx = exclusionList.push_back();
newEx = __INT_STR(exc);
}
}
node["exclusion"] = exclusionList;
yaml::Node rewriteList;
if (m_rewrite.size() > 0U) {
for (auto rule : m_rewrite) {
yaml::Node& rewrite = rewriteList.push_back();
rule.getYaml(rewrite);
}
}
node["rewrite"] = rewriteList;
}
void TalkgroupRuleGroupVoiceSource::getYaml(yaml::Node &node)
{
node["tgid"] = __INT_STR(m_tgId);
node["slot"] = __INT_STR(m_tgSlot);
}
void TalkgroupRuleRewrite::getYaml(yaml::Node &node)
{
node["peerid"] = __INT_STR(m_peerId);
node["tgid"] = __INT_STR(m_tgId);
node["slot"] = __INT_STR(m_tgSlot);
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -322,7 +385,7 @@ bool TalkgroupRulesLookup::load()
try { try {
bool ret = yaml::Parse(m_rules, m_rulesFile.c_str()); bool ret = yaml::Parse(m_rules, m_rulesFile.c_str());
if (!ret) { if (!ret) {
LogError(LOG_HOST, "Cannot open the talkgroup rules lookup file - %s", m_rulesFile.c_str()); LogError(LOG_HOST, "Cannot open the talkgroup rules lookup file - %s - error parsing YML", m_rulesFile.c_str());
return false; return false;
} }
} }
@ -374,3 +437,52 @@ bool TalkgroupRulesLookup::load()
return true; return true;
} }
/// <summary>
/// Saves the table to the passed lookup table file.
/// </summary>
/// <returns>True, if lookup table was saved, otherwise false.</returns>
bool TalkgroupRulesLookup::save()
{
// Make sure file is valid
if (m_rulesFile.length() <= 0) {
return false;
}
m_mutex.lock();
// New list for our new group voice rules
yaml::Node groupVoiceList;
yaml::Node newRules;
for (auto entry : m_groupVoice) {
yaml::Node& gv = groupVoiceList.push_back();
entry.getYaml(gv);
LogDebug(LOG_HOST, "Added TGID %s to yaml TG list", gv["name"].as<std::string>().c_str());
}
LogDebug(LOG_HOST, "Got final GroupVoiceList YAML size of %u", groupVoiceList.size());
// Set the new rules
newRules["groupVoice"] = groupVoiceList;
m_mutex.unlock();
// Make sure we actually did stuff right
if (newRules["groupVoice"].size() != m_groupVoice.size()) {
LogError(LOG_HOST, "Generated YAML node for group lists did not match loaded group size! (%u != %u)", newRules["groupVoice"].size(), m_groupVoice.size());
return false;
}
try {
LogDebug(LOG_HOST, "Saving TGID file to %s", m_rulesFile.c_str());
yaml::Serialize(newRules, m_rulesFile.c_str());
LogDebug(LOG_HOST, "Saved TGID config file to %s", m_rulesFile.c_str());
}
catch (yaml::OperationException const& e) {
LogError(LOG_HOST, "Cannot open the talkgroup rules lookup file - %s (%s)", m_rulesFile.c_str(), e.message());
return false;
}
return true;
}

@ -8,6 +8,7 @@
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
* *
* Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2024 Patrick McDonnell, W3AXL
* *
*/ */
#if !defined(__TALKGROUP_RULES_LOOKUP_H__) #if !defined(__TALKGROUP_RULES_LOOKUP_H__)
@ -58,6 +59,8 @@ namespace lookups
return *this; return *this;
} }
void getYaml(yaml::Node &node);
public: public:
/// <summary>Talkgroup ID.</summary> /// <summary>Talkgroup ID.</summary>
__PROPERTY_PLAIN(uint32_t, tgId); __PROPERTY_PLAIN(uint32_t, tgId);
@ -102,6 +105,8 @@ namespace lookups
return *this; return *this;
} }
void getYaml(yaml::Node &node);
public: public:
/// <summary>Peer ID.</summary> /// <summary>Peer ID.</summary>
__PROPERTY_PLAIN(uint32_t, peerId); __PROPERTY_PLAIN(uint32_t, peerId);
@ -178,6 +183,8 @@ namespace lookups
return *this; return *this;
} }
void getYaml(yaml::Node &node);
public: public:
/// <summary>Flag indicating whether the rule is active.</summary> /// <summary>Flag indicating whether the rule is active.</summary>
__PROPERTY_PLAIN(bool, active); __PROPERTY_PLAIN(bool, active);
@ -238,6 +245,8 @@ namespace lookups
return false; return false;
} }
void getYaml(yaml::Node &node);
public: public:
/// <summary>Textual name for the routing rule.</summary> /// <summary>Textual name for the routing rule.</summary>
__PROPERTY_PLAIN(std::string, name); __PROPERTY_PLAIN(std::string, name);
@ -283,7 +292,7 @@ namespace lookups
virtual TalkgroupRuleGroupVoice findByRewrite(uint32_t peerId, uint32_t id, uint8_t slot = 0U); virtual TalkgroupRuleGroupVoice findByRewrite(uint32_t peerId, uint32_t id, uint8_t slot = 0U);
/// <summary>Saves loaded talkgroup rules.</summary> /// <summary>Saves loaded talkgroup rules.</summary>
void commit(); bool commit();
/// <summary>Flag indicating whether talkgroup ID access control is enabled or not.</summary> /// <summary>Flag indicating whether talkgroup ID access control is enabled or not.</summary>
bool getACL(); bool getACL();
@ -301,6 +310,9 @@ namespace lookups
/// <summary>Loads the table from the passed lookup table file.</summary> /// <summary>Loads the table from the passed lookup table file.</summary>
/// <returns>True, if lookup table was loaded, otherwise false.</returns> /// <returns>True, if lookup table was loaded, otherwise false.</returns>
bool load(); bool load();
/// <summary>Saves the table to the passed lookup table file.</summary>
/// <returns>True, if lookup table was saved, otherwise false.</returns>
bool save();
public: public:
/// <summary>Number indicating the number of seconds to hang on a talkgroup.</summary> /// <summary>Number indicating the number of seconds to hang on a talkgroup.</summary>

@ -8,6 +8,7 @@
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
* *
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL * Copyright (C) 2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2024 Patrick McDonnell, W3AXL
* *
*/ */
#include "fne/Defines.h" #include "fne/Defines.h"
@ -187,7 +188,7 @@ json::object tgToJson(const TalkgroupRuleGroupVoice& groupVoice)
uint8_t tgSlot = rewrEntry.tgSlot(); uint8_t tgSlot = rewrEntry.tgSlot();
rewrite["slot"].set<uint8_t>(tgSlot); rewrite["slot"].set<uint8_t>(tgSlot);
exclusions.push_back(json::value(rewrite)); rewrites.push_back(json::value(rewrite));
} }
} }
config["rewrite"].set<json::array>(rewrites); config["rewrite"].set<json::array>(rewrites);
@ -209,6 +210,7 @@ TalkgroupRuleGroupVoice jsonToTG(json::object& req, HTTPPayload& reply)
// validate parameters // validate parameters
if (!req["name"].is<std::string>()) { if (!req["name"].is<std::string>()) {
errorPayload(reply, "TG \"name\" was not a valid string"); errorPayload(reply, "TG \"name\" was not a valid string");
LogDebug(LOG_REST, "TG \"name\" was not a valid string");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
@ -218,17 +220,20 @@ TalkgroupRuleGroupVoice jsonToTG(json::object& req, HTTPPayload& reply)
{ {
if (!req["source"].is<json::object>()) { if (!req["source"].is<json::object>()) {
errorPayload(reply, "TG \"source\" was not a valid JSON object"); errorPayload(reply, "TG \"source\" was not a valid JSON object");
LogDebug(LOG_REST, "TG \"source\" was not a valid JSON object");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
json::object sourceObj = req["source"].get<json::object>(); json::object sourceObj = req["source"].get<json::object>();
if (!sourceObj["tgid"].is<uint32_t>()) { if (!sourceObj["tgid"].is<uint32_t>()) {
errorPayload(reply, "TG source \"tgid\" was not a valid number"); errorPayload(reply, "TG source \"tgid\" was not a valid number");
LogDebug(LOG_REST, "TG source \"tgid\" was not a valid number");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
if (!sourceObj["slot"].is<uint8_t>()) { if (!sourceObj["slot"].is<uint8_t>()) {
errorPayload(reply, "TG source \"slot\" was not a valid number"); errorPayload(reply, "TG source \"slot\" was not a valid number");
LogDebug(LOG_REST, "TG source \"slot\" was not a valid number");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
@ -243,22 +248,26 @@ TalkgroupRuleGroupVoice jsonToTG(json::object& req, HTTPPayload& reply)
{ {
if (!req["config"].is<json::object>()) { if (!req["config"].is<json::object>()) {
errorPayload(reply, "TG \"config\" was not a valid JSON object"); errorPayload(reply, "TG \"config\" was not a valid JSON object");
LogDebug(LOG_REST, "TG \"config\" was not a valid JSON object");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
json::object configObj = req["config"].get<json::object>(); json::object configObj = req["config"].get<json::object>();
if (!configObj["active"].is<bool>()) { if (!configObj["active"].is<bool>()) {
errorPayload(reply, "TG configuration \"active\" was not a valid boolean"); errorPayload(reply, "TG configuration \"active\" was not a valid boolean");
LogDebug(LOG_REST, "TG configuration \"active\" was not a valid boolean");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
if (!configObj["affiliated"].is<bool>()) { if (!configObj["affiliated"].is<bool>()) {
errorPayload(reply, "TG configuration \"affiliated\" was not a valid boolean"); errorPayload(reply, "TG configuration \"affiliated\" was not a valid boolean");
LogDebug(LOG_REST, "TG configuration \"affiliated\" was not a valid boolean");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
if (!configObj["parrot"].is<bool>()) { if (!configObj["parrot"].is<bool>()) {
errorPayload(reply, "TG configuration \"parrot\" slot was not a valid boolean"); errorPayload(reply, "TG configuration \"parrot\" slot was not a valid boolean");
LogDebug(LOG_REST, "TG configuration \"parrot\" slot was not a valid boolean");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
@ -267,17 +276,19 @@ TalkgroupRuleGroupVoice jsonToTG(json::object& req, HTTPPayload& reply)
config.affiliated(configObj["affiliated"].get<bool>()); config.affiliated(configObj["affiliated"].get<bool>());
config.parrot(configObj["parrot"].get<bool>()); config.parrot(configObj["parrot"].get<bool>());
if (!req["inclusion"].is<json::array>()) { if (!configObj["inclusion"].is<json::array>()) {
errorPayload(reply, "TG \"inclusion\" was not a valid JSON array"); errorPayload(reply, "TG configuration \"inclusion\" was not a valid JSON array");
LogDebug(LOG_REST, "TG configuration \"inclusion\" was not a valid JSON array");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
json::array inclusions = req["inclusion"].get<json::array>(); json::array inclusions = configObj["inclusion"].get<json::array>();
std::vector<uint32_t> inclusion = groupVoice.config().inclusion(); std::vector<uint32_t> inclusion = groupVoice.config().inclusion();
if (inclusions.size() > 0) { if (inclusions.size() > 0) {
for (auto inclEntry : inclusions) { for (auto inclEntry : inclusions) {
if (!inclEntry.is<uint32_t>()) { if (!inclEntry.is<uint32_t>()) {
errorPayload(reply, "TG inclusion value was not a valid number"); errorPayload(reply, "TG configuration inclusion value was not a valid number");
LogDebug(LOG_REST, "TG configuration inclusion value was not a valid number (was %s)", inclEntry.to_type().c_str());
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
@ -286,17 +297,19 @@ TalkgroupRuleGroupVoice jsonToTG(json::object& req, HTTPPayload& reply)
config.inclusion(inclusion); config.inclusion(inclusion);
} }
if (!req["exclusion"].is<json::array>()) { if (!configObj["exclusion"].is<json::array>()) {
errorPayload(reply, "TG \"exclusion\" was not a valid JSON array"); errorPayload(reply, "TG configuration \"exclusion\" was not a valid JSON array");
LogDebug(LOG_REST, "TG configuration \"exclusion\" was not a valid JSON array");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
json::array exclusions = req["exclusion"].get<json::array>(); json::array exclusions = configObj["exclusion"].get<json::array>();
std::vector<uint32_t> exclusion = groupVoice.config().exclusion(); std::vector<uint32_t> exclusion = groupVoice.config().exclusion();
if (exclusions.size() > 0) { if (exclusions.size() > 0) {
for (auto exclEntry : exclusions) { for (auto exclEntry : exclusions) {
if (!exclEntry.is<uint32_t>()) { if (!exclEntry.is<uint32_t>()) {
errorPayload(reply, "TG exclusion value was not a valid number"); errorPayload(reply, "TG configuration exclusion value was not a valid number");
LogDebug(LOG_REST, "TG configuration exclusion value was not a valid number");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
@ -305,17 +318,19 @@ TalkgroupRuleGroupVoice jsonToTG(json::object& req, HTTPPayload& reply)
config.exclusion(exclusion); config.exclusion(exclusion);
} }
if (!req["rewrites"].is<json::array>()) { if (!configObj["rewrite"].is<json::array>()) {
errorPayload(reply, "TG \"rewrites\" was not a valid JSON array"); errorPayload(reply, "TG configuration \"rewrite\" was not a valid JSON array");
LogDebug(LOG_REST, "TG configuration \"rewrite\" was not a valid JSON array");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
json::array rewrites = req["rewrites"].get<json::array>(); json::array rewrites = configObj["rewrite"].get<json::array>();
std::vector<lookups::TalkgroupRuleRewrite> rewrite = groupVoice.config().rewrite(); std::vector<lookups::TalkgroupRuleRewrite> rewrite = groupVoice.config().rewrite();
if (rewrites.size() > 0) { if (rewrites.size() > 0) {
for (auto rewrEntry : rewrites) { for (auto rewrEntry : rewrites) {
if (!rewrEntry.is<json::object>()) { if (!rewrEntry.is<json::object>()) {
errorPayload(reply, "TG rewrite value was not a valid JSON object"); errorPayload(reply, "TG rewrite value was not a valid JSON object");
LogDebug(LOG_REST, "TG rewrite value was not a valid JSON object");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
json::object rewriteObj = rewrEntry.get<json::object>(); json::object rewriteObj = rewrEntry.get<json::object>();
@ -324,16 +339,19 @@ TalkgroupRuleGroupVoice jsonToTG(json::object& req, HTTPPayload& reply)
if (!rewriteObj["peerid"].is<uint32_t>()) { if (!rewriteObj["peerid"].is<uint32_t>()) {
errorPayload(reply, "TG rewrite rule \"peerid\" was not a valid number"); errorPayload(reply, "TG rewrite rule \"peerid\" was not a valid number");
LogDebug(LOG_REST, "TG rewrite rule \"peerid\" was not a valid number");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
if (!rewriteObj["tgid"].is<uint32_t>()) { if (!rewriteObj["tgid"].is<uint32_t>()) {
errorPayload(reply, "TG rewrite rule \"tgid\" was not a valid number"); errorPayload(reply, "TG rewrite rule \"tgid\" was not a valid number");
LogDebug(LOG_REST, "TG rewrite rule \"tgid\" was not a valid number");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
if (!rewriteObj["slot"].is<uint8_t>()) { if (!rewriteObj["slot"].is<uint8_t>()) {
errorPayload(reply, "TG rewrite rule \"slot\" was not a valid number"); errorPayload(reply, "TG rewrite rule \"slot\" was not a valid number");
LogDebug(LOG_REST, "TG rewrite rule \"slot\" was not a valid number");
return TalkgroupRuleGroupVoice(); return TalkgroupRuleGroupVoice();
} }
@ -691,6 +709,7 @@ void RESTAPI::restAPI_GetPeerQuery(const HTTPPayload& request, HTTPPayload& repl
uint32_t peerId = entry.first; uint32_t peerId = entry.first;
network::FNEPeerConnection* peer = entry.second; network::FNEPeerConnection* peer = entry.second;
if (peer != nullptr) { if (peer != nullptr) {
LogDebug(LOG_REST, "Preparing Peer %u (%s) for REST API query", peerId, peer->address().c_str());
json::object peerObj = json::object(); json::object peerObj = json::object();
peerObj["peerId"].set<uint32_t>(peerId); peerObj["peerId"].set<uint32_t>(peerId);
@ -716,6 +735,12 @@ void RESTAPI::restAPI_GetPeerQuery(const HTTPPayload& request, HTTPPayload& repl
} }
} }
} }
else {
LogDebug(LOG_REST, "No peers connected to this FNE");
}
}
else {
LogDebug(LOG_REST, "Network not set up, no peers to return");
} }
response["peers"].set<json::array>(peers); response["peers"].set<json::array>(peers);
@ -941,6 +966,7 @@ void RESTAPI::restAPI_PutTGAdd(const HTTPPayload& request, HTTPPayload& reply, c
TalkgroupRuleGroupVoice groupVoice = jsonToTG(req, reply); TalkgroupRuleGroupVoice groupVoice = jsonToTG(req, reply);
if (groupVoice.isInvalid()) { if (groupVoice.isInvalid()) {
::LogError(LOG_REST, "Unable to parse TG JSON from REST TgAdd");
return; return;
} }
@ -993,9 +1019,15 @@ void RESTAPI::restAPI_PutTGDelete(const HTTPPayload& request, HTTPPayload& reply
return; return;
} }
// Validate slot
if (!req["slot"].is<uint8_t>()) {
errorPayload(reply, "slot was not a valid char");
}
uint32_t tgid = req["tgid"].get<uint32_t>(); uint32_t tgid = req["tgid"].get<uint32_t>();
uint8_t slot = req["slot"].get<uint8_t>();
TalkgroupRuleGroupVoice groupVoice = m_tidLookup->find(tgid); TalkgroupRuleGroupVoice groupVoice = m_tidLookup->find(tgid, slot);
if (groupVoice.isInvalid()) { if (groupVoice.isInvalid()) {
errorPayload(reply, "failed to find specified TGID to delete"); errorPayload(reply, "failed to find specified TGID to delete");
return; return;
@ -1024,7 +1056,10 @@ void RESTAPI::restAPI_GetTGCommit(const HTTPPayload& request, HTTPPayload& reply
json::object response = json::object(); json::object response = json::object();
setResponseDefaultStatus(response); setResponseDefaultStatus(response);
m_tidLookup->commit(); if(!m_tidLookup->commit()) {
errorPayload(reply, "failed to write new TGID file");
return;
}
reply.payload(response); reply.payload(response);
} }

Loading…
Cancel
Save

Powered by TurnKey Linux.