Refactor ObjectQueryHandler to use new JSON stream encoder

This commit is contained in:
Johannes Schmidt 2025-06-27 12:46:15 +02:00
parent 62b2dadbac
commit bb75d73012

View File

@ -1,6 +1,8 @@
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
#include "remote/objectqueryhandler.hpp" #include "remote/objectqueryhandler.hpp"
#include "base/generator.hpp"
#include "base/json.hpp"
#include "remote/httputility.hpp" #include "remote/httputility.hpp"
#include "remote/filterutility.hpp" #include "remote/filterutility.hpp"
#include "base/serializer.hpp" #include "base/serializer.hpp"
@ -9,6 +11,7 @@
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
#include <set> #include <set>
#include <unordered_map> #include <unordered_map>
#include <memory>
using namespace icinga; using namespace icinga;
@ -144,6 +147,22 @@ bool ObjectQueryHandler::HandleRequest(
return true; return true;
} }
bool includeUsedBy = false;
bool includeLocation = false;
if (umetas) {
ObjectLock olock(umetas);
for (String meta : umetas) {
if (meta == "used_by") {
includeUsedBy = true;
} else if (meta == "location") {
includeLocation = true;
} else {
HttpUtility::SendJsonError(response, params, 400, "Invalid field specified for meta: " + meta);
return true;
}
}
}
bool allJoins = HttpUtility::GetLastParameter(params, "all_joins"); bool allJoins = HttpUtility::GetLastParameter(params, "all_joins");
params->Set("type", type->GetName()); params->Set("type", type->GetName());
@ -165,10 +184,7 @@ bool ObjectQueryHandler::HandleRequest(
return true; return true;
} }
ArrayData results; std::set<int> joinAttrs;
results.reserve(objs.size());
std::set<String> joinAttrs;
std::set<String> userJoinAttrs; std::set<String> userJoinAttrs;
if (ujoins) { if (ujoins) {
@ -187,70 +203,63 @@ bool ObjectQueryHandler::HandleRequest(
if (!allJoins && userJoinAttrs.find(field.NavigationName) == userJoinAttrs.end()) if (!allJoins && userJoinAttrs.find(field.NavigationName) == userJoinAttrs.end())
continue; continue;
joinAttrs.insert(field.Name); joinAttrs.insert(fid);
} }
std::unordered_map<Type*, std::pair<bool, std::unique_ptr<Expression>>> typePermissions; std::unordered_map<Type*, std::pair<bool, std::unique_ptr<Expression>>> typePermissions;
std::unordered_map<Object*, bool> objectAccessAllowed; std::unordered_map<Object*, bool> objectAccessAllowed;
for (ConfigObject::Ptr obj : objs) { auto it = objs.begin();
auto generatorFunc = [&]() -> std::optional<Value> {
if (it == objs.end()) {
return std::nullopt;
}
ConfigObject::Ptr obj = *it;
++it;
DictionaryData result1{ DictionaryData result1{
{ "name", obj->GetName() }, { "name", obj->GetName() },
{ "type", obj->GetReflectionType()->GetName() } { "type", obj->GetReflectionType()->GetName() }
}; };
DictionaryData metaAttrs; DictionaryData metaAttrs;
if (includeUsedBy) {
Array::Ptr used_by = new Array();
metaAttrs.emplace_back("used_by", used_by);
if (umetas) { for (auto& configObj : DependencyGraph::GetChildren(obj)) {
ObjectLock olock(umetas); used_by->Add(new Dictionary({
for (String meta : umetas) { {"type", configObj->GetReflectionType()->GetName()},
if (meta == "used_by") { {"name", configObj->GetName()}
Array::Ptr used_by = new Array(); }));
metaAttrs.emplace_back("used_by", used_by);
for (auto& configObj : DependencyGraph::GetChildren(obj)) {
used_by->Add(new Dictionary({
{ "type", configObj->GetReflectionType()->GetName() },
{ "name", configObj->GetName() }
}));
}
} else if (meta == "location") {
metaAttrs.emplace_back("location", obj->GetSourceLocation());
} else {
HttpUtility::SendJsonError(response, params, 400, "Invalid field specified for meta: " + meta);
return true;
}
} }
} }
if (includeLocation) {
metaAttrs.emplace_back("location", obj->GetSourceLocation());
}
result1.emplace_back("meta", new Dictionary(std::move(metaAttrs))); result1.emplace_back("meta", new Dictionary(std::move(metaAttrs)));
try { try {
result1.emplace_back("attrs", SerializeObjectAttrs(obj, String(), uattrs, false, false)); result1.emplace_back("attrs", SerializeObjectAttrs(obj, String(), uattrs, false, false));
} catch (const ScriptError& ex) { } catch (const ScriptError& ex) {
HttpUtility::SendJsonError(response, params, 400, ex.what()); return new Dictionary{
return true; {"type", type->GetName()},
{"name", obj->GetName()},
{"code", 400},
{"status", ex.what()}
};
} }
DictionaryData joins; DictionaryData joins;
for (const String& joinAttr : joinAttrs) { for (auto joinAttr : joinAttrs) {
Object::Ptr joinedObj; Object::Ptr joinedObj;
int fid = type->GetFieldId(joinAttr); Field field = type->GetFieldInfo(joinAttr);
if (fid < 0) { joinedObj = obj->NavigateField(joinAttr);
HttpUtility::SendJsonError(response, params, 400, "Invalid field specified for join: " + joinAttr);
return true;
}
Field field = type->GetFieldInfo(fid);
if (!(field.Attributes & FANavigation)) {
HttpUtility::SendJsonError(response, params, 400, "Not a joinable field: " + joinAttr);
return true;
}
joinedObj = obj->NavigateField(fid);
if (!joinedObj) if (!joinedObj)
continue; continue;
@ -303,22 +312,29 @@ bool ObjectQueryHandler::HandleRequest(
try { try {
joins.emplace_back(prefix, SerializeObjectAttrs(joinedObj, prefix, ujoins, true, allJoins)); joins.emplace_back(prefix, SerializeObjectAttrs(joinedObj, prefix, ujoins, true, allJoins));
} catch (const ScriptError& ex) { } catch (const ScriptError& ex) {
HttpUtility::SendJsonError(response, params, 400, ex.what()); return new Dictionary{
return true; {"type", type->GetName()},
{"name", obj->GetName()},
{"code", 400},
{"status", ex.what()}
};
} }
} }
result1.emplace_back("joins", new Dictionary(std::move(joins))); result1.emplace_back("joins", new Dictionary(std::move(joins)));
results.push_back(new Dictionary(std::move(result1))); return new Dictionary{std::move(result1)};
} };
Dictionary::Ptr result = new Dictionary({
{ "results", new Array(std::move(results)) }
});
response.result(http::status::ok); response.result(http::status::ok);
HttpUtility::SendJsonBody(response, params, result); response.set(http::field::content_type, "application/json");
response.StartStreaming();
Dictionary::Ptr results = new Dictionary{{"results", new ValueGenerator{generatorFunc}}};
results->Freeze();
bool pretty = HttpUtility::GetLastParameter(params, "pretty");
response.GetJsonEncoder(pretty).Encode(results, &yc);
return true; return true;
} }