Change output format for object queries

fixes #10551
This commit is contained in:
Gunnar Beutner 2015-11-06 11:04:58 +01:00
parent 4cfde2d7ef
commit a177e5e05a
6 changed files with 208 additions and 144 deletions

View File

@ -112,23 +112,15 @@ void MainForm::OnTypeSelected(wxTreeEvent& event)
ApiType::Ptr type = m_Types[typeName.ToStdString()];
std::vector<String> attrs;
attrs.push_back(type->Name.ToLower() + ".__name");
attrs.push_back("__name");
m_ApiClient->GetObjects(type->PluralName, boost::bind(&MainForm::ObjectsCompletionHandler, this, _1, _2, true),
std::vector<String>(), attrs);
}
static bool ApiObjectLessComparer(const String& nameAttr, const ApiObject::Ptr& o1, const ApiObject::Ptr& o2)
static bool ApiObjectLessComparer(const ApiObject::Ptr& o1, const ApiObject::Ptr& o2)
{
std::map<String, Value>::const_iterator it1 = o1->Attrs.find(nameAttr);
if (it1 == o1->Attrs.end())
return false;
std::map<String, Value>::const_iterator it2 = o2->Attrs.find(nameAttr);
if (it2 == o2->Attrs.end())
return false;
return it1->second < it2->second;
return o1->Name < o2->Name;
}
void MainForm::ObjectsCompletionHandler(boost::exception_ptr eptr, const std::vector<ApiObject::Ptr>& objects, bool forward)
@ -151,21 +143,12 @@ void MainForm::ObjectsCompletionHandler(boost::exception_ptr eptr, const std::ve
}
}
wxTreeItemId selectedId = m_TypesTree->GetSelection();
wxString typeName = m_TypesTree->GetItemText(selectedId);
ApiType::Ptr type = m_Types[typeName.ToStdString()];
String nameAttr = type->Name.ToLower() + ".__name";
std::vector<ApiObject::Ptr> sortedObjects = objects;
std::sort(sortedObjects.begin(), sortedObjects.end(), boost::bind(ApiObjectLessComparer, nameAttr, _2, _1));
std::sort(sortedObjects.begin(), sortedObjects.end(), ApiObjectLessComparer);
BOOST_FOREACH(const ApiObject::Ptr& object, sortedObjects) {
std::map<String, Value>::const_iterator it = object->Attrs.find(nameAttr);
if (it == object->Attrs.end())
continue;
String name = it->second;
m_ObjectsList->InsertItem(0, name.GetData());
std::string name = object->Name;
m_ObjectsList->InsertItem(0, name);
}
}
@ -190,7 +173,8 @@ void MainForm::OnObjectSelected(wxListEvent& event)
std::vector<String> names;
names.push_back(objectName);
m_ApiClient->GetObjects(type->PluralName, boost::bind(&MainForm::ObjectDetailsCompletionHandler, this, _1, _2, true), names);
m_ApiClient->GetObjects(type->PluralName, boost::bind(&MainForm::ObjectDetailsCompletionHandler, this, _1, _2, true),
names, std::vector<String>(), std::vector<String>(), true);
}
wxPGProperty *MainForm::ValueToProperty(const String& name, const Value& value)

View File

@ -105,7 +105,7 @@ void ApiClient::TypesHttpCompletionCallback(HttpRequest& request, HttpResponse&
}
void ApiClient::GetObjects(const String& pluralType, const ObjectsCompletionCallback& callback,
const std::vector<String>& names, const std::vector<String>& attrs) const
const std::vector<String>& names, const std::vector<String>& attrs, const std::vector<String>& joins, bool all_joins) const
{
Url::Ptr url = new Url();
url->SetScheme("https");
@ -128,6 +128,12 @@ void ApiClient::GetObjects(const String& pluralType, const ObjectsCompletionCall
params["attrs"].push_back(attr);
}
BOOST_FOREACH(const String& join, joins) {
params["joins"].push_back(join);
}
params["all_joins"].push_back(all_joins ? "1" : "0");
url->SetQuery(params);
try {
@ -169,26 +175,42 @@ void ApiClient::ObjectsHttpCompletionCallback(HttpRequest& request,
if (results) {
ObjectLock olock(results);
BOOST_FOREACH(const Dictionary::Ptr objectInfo, results)
{
BOOST_FOREACH(const Dictionary::Ptr objectInfo, results) {
ApiObject::Ptr object = new ApiObject();
object->Name = objectInfo->Get("name");
object->Type = objectInfo->Get("type");
Dictionary::Ptr attrs = objectInfo->Get("attrs");
{
if (attrs) {
ObjectLock olock(attrs);
BOOST_FOREACH(const Dictionary::Pair& kv, attrs)
{
object->Attrs[kv.first] = kv.second;
BOOST_FOREACH(const Dictionary::Pair& kv, attrs) {
object->Attrs[object->Type.ToLower() + "." + kv.first] = kv.second;
}
}
Dictionary::Ptr joins = objectInfo->Get("joins");
if (joins) {
ObjectLock olock(joins);
BOOST_FOREACH(const Dictionary::Pair& kv, joins) {
Dictionary::Ptr attrs = kv.second;
if (attrs) {
ObjectLock olock(attrs);
BOOST_FOREACH(const Dictionary::Pair& kv2, attrs) {
object->Attrs[kv.first + "." + kv2.first] = kv2.second;
}
}
}
}
Array::Ptr used_by = objectInfo->Get("used_by");
{
if (used_by) {
ObjectLock olock(used_by);
BOOST_FOREACH(const Dictionary::Ptr& refInfo, used_by)
{
BOOST_FOREACH(const Dictionary::Ptr& refInfo, used_by) {
ApiObjectReference ref;
ref.Name = refInfo->Get("name");
ref.Type = refInfo->Get("type");

View File

@ -32,7 +32,9 @@ struct ApiFieldAttributes
{
public:
bool Config;
bool Internal;
bool Navigation;
bool NoUserModify;
bool NouserView;
bool Required;
bool State;
};
@ -76,6 +78,8 @@ struct I2_REMOTE_API ApiObject : public Object
public:
DECLARE_PTR_TYPEDEFS(ApiObject);
String Name;
String Type;
std::map<String, Value> Attrs;
std::vector<ApiObjectReference> UsedBy;
};
@ -94,7 +98,8 @@ public:
typedef boost::function<void(boost::exception_ptr, const std::vector<ApiObject::Ptr>&)> ObjectsCompletionCallback;
void GetObjects(const String& pluralType, const ObjectsCompletionCallback& callback,
const std::vector<String>& names = std::vector<String>(),
const std::vector<String>& attrs = std::vector<String>()) const;
const std::vector<String>& attrs = std::vector<String>(),
const std::vector<String>& joins = std::vector<String>(), bool all_joins = false) const;
typedef boost::function<void(boost::exception_ptr, const Value&)> ExecuteScriptCompletionCallback;
void ExecuteScript(const String& session, const String& command, bool sandboxed,

View File

@ -153,7 +153,7 @@ void HttpServerConnection::ProcessMessageAsync(HttpRequest& request)
user.reset();
}
String requestUrl = request.RequestUrl->Format();
String requestUrl = request.RequestUrl->Format();
Log(LogInformation, "HttpServerConnection")
<< "Request: " << request.RequestMethod << " " << requestUrl

View File

@ -30,6 +30,84 @@ using namespace icinga;
REGISTER_URLHANDLER("/v1/objects", ObjectQueryHandler);
Dictionary::Ptr ObjectQueryHandler::SerializeObjectAttrs(const Object::Ptr& object,
const String& attrPrefix, const Array::Ptr& attrs, bool isJoin, bool allAttrs)
{
Type::Ptr type = object->GetReflectionType();
std::vector<int> fids;
if (isJoin && attrs) {
ObjectLock olock(attrs);
BOOST_FOREACH(const String& attr, attrs) {
if (attr == attrPrefix) {
allAttrs = true;
break;
}
}
}
if (!isJoin && (!attrs || attrs->GetLength() == 0))
allAttrs = true;
if (allAttrs) {
for (int fid = 0; fid < type->GetFieldCount(); fid++) {
fids.push_back(fid);
}
} else if (attrs) {
ObjectLock olock(attrs);
BOOST_FOREACH(const String& attr, attrs) {
String userAttr;
if (isJoin) {
String::SizeType dpos = attr.FindFirstOf(".");
if (dpos == String::NPos)
continue;
String userJoinAttr = attr.SubStr(0, dpos);
if (userJoinAttr != attrPrefix)
continue;
userAttr = attr.SubStr(dpos + 1);
} else
userAttr = attr;
int fid = type->GetFieldId(userAttr);
if (fid < 0)
BOOST_THROW_EXCEPTION(ScriptError("Invalid field specified: " + userAttr));
fids.push_back(fid);
}
}
Dictionary::Ptr resultAttrs = new Dictionary();
BOOST_FOREACH(int& fid, fids)
{
Field field = type->GetFieldInfo(fid);
Value val = object->GetField(fid);
/* hide attributes which shouldn't be user-visible */
if (field.Attributes & FANoUserView)
continue;
/* hide internal navigation fields */
if (field.Attributes & FANavigation) {
Value nval = object->NavigateField(fid);
if (val == nval)
continue;
}
Value sval = Serialize(val, FAConfig | FAState);
resultAttrs->Set(field.Name, sval);
}
return resultAttrs;
}
bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
{
if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4)
@ -51,44 +129,10 @@ bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
std::set<String> userJoinAttrs;
std::set<String> attrs;
Array::Ptr uattrs = params->Get("attrs");
if (uattrs) {
ObjectLock olock(uattrs);
BOOST_FOREACH(const String& uattr, uattrs) {
attrs.insert(uattr);
String::SizeType dpos = uattr.FindFirstOf(".");
if (dpos == String::NPos) {
HttpUtility::SendJsonError(response, 400, "Attribute name must contain '.'.");
return true;
}
String userJoinAttr = uattr.SubStr(0, dpos);
if (userJoinAttr == type->GetName().ToLower())
userJoinAttr = "";
userJoinAttrs.insert(userJoinAttr);
}
}
std::vector<String> joinAttrs;
joinAttrs.push_back("");
for (int fid = 0; fid < type->GetFieldCount(); fid++) {
Field field = type->GetFieldInfo(fid);
if (!(field.Attributes & FANavigation))
continue;
if (!userJoinAttrs.empty() && userJoinAttrs.find(field.Name) == userJoinAttrs.end())
continue;
joinAttrs.push_back(field.Name);
}
Array::Ptr ujoins = params->Get("joins");
Array::Ptr umetas = params->Get("meta");
bool allJoins = HttpUtility::GetLastParameter(params, "all_joins");
params->Set("type", type->GetName());
@ -103,93 +147,99 @@ bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
Array::Ptr results = new Array();
results->Reserve(objs.size());
std::set<String> joinAttrs;
if (allJoins) {
for (int fid = 0; fid < type->GetFieldCount(); fid++) {
Field field = type->GetFieldInfo(fid);
if (field.Attributes & FANavigation)
joinAttrs.insert(field.Name);
}
} else if (ujoins) {
ObjectLock olock(ujoins);
BOOST_FOREACH(const String& ujoin, ujoins) {
joinAttrs.insert(ujoin.SubStr(0, ujoin.FindFirstOf(".")));
}
}
BOOST_FOREACH(const ConfigObject::Ptr& obj, objs) {
Dictionary::Ptr result1 = new Dictionary();
results->Add(result1);
Dictionary::Ptr resultAttrs = new Dictionary();
result1->Set("attrs", resultAttrs);
result1->Set("name", obj->GetName());
result1->Set("type", obj->GetReflectionType()->GetName());
Dictionary::Ptr metaAttrs = new Dictionary();
result1->Set("meta", metaAttrs);
if (umetas) {
ObjectLock olock(umetas);
BOOST_FOREACH(const String& meta, umetas) {
if (meta == "used_by") {
Array::Ptr used_by = new Array();
metaAttrs->Set("used_by", used_by);
BOOST_FOREACH(const Object::Ptr& pobj, DependencyGraph::GetParents((obj)))
{
ConfigObject::Ptr configObj = dynamic_pointer_cast<ConfigObject>(pobj);
if (!configObj)
continue;
Dictionary::Ptr refInfo = new Dictionary();
refInfo->Set("type", configObj->GetType()->GetName());
refInfo->Set("name", configObj->GetName());
used_by->Add(refInfo);
}
} else {
HttpUtility::SendJsonError(response, 400, "Invalid field specified for meta: " + meta);
return true;
}
}
}
try {
result1->Set("attrs", SerializeObjectAttrs(obj, String(), uattrs, false, false));
} catch (const ScriptError& ex) {
HttpUtility::SendJsonError(response, 400, ex.what());
return true;
}
Dictionary::Ptr joins = new Dictionary();
result1->Set("joins", joins);
BOOST_FOREACH(const String& joinAttr, joinAttrs) {
Object::Ptr joinedObj;
String prefix;
if (joinAttr.IsEmpty()) {
joinedObj = obj;
prefix = type->GetName();
} else {
int fid = type->GetFieldId(joinAttr);
joinedObj = obj->NavigateField(fid);
int fid = type->GetFieldId(joinAttr);
if (!joinedObj)
continue;
Field field = type->GetFieldInfo(fid);
prefix = field.NavigationName;
if (fid < 0) {
HttpUtility::SendJsonError(response, 400, "Invalid field specified for join: " + joinAttr);
return true;
}
boost::algorithm::to_lower(prefix);
Field field = type->GetFieldInfo(fid);
Type::Ptr joinedType = joinedObj->GetReflectionType();
std::vector<int> fids;
if (attrs.empty()) {
for (int fid = 0; fid < joinedType->GetFieldCount(); fid++) {
fids.push_back(fid);
}
} else {
BOOST_FOREACH(const String& aname, attrs) {
String::SizeType dpos = aname.FindFirstOf(".");
ASSERT(dpos != String::NPos);
String userJoinAttr = aname.SubStr(0, dpos);
if (userJoinAttr != prefix)
continue;
String userAttr = aname.SubStr(dpos + 1);
int fid = joinedType->GetFieldId(userAttr);
fids.push_back(fid);
}
if (!(field.Attributes & FANavigation)) {
HttpUtility::SendJsonError(response, 400, "Not a joinable field: " + joinAttr);
return true;
}
BOOST_FOREACH(int& fid, fids) {
Field field = joinedType->GetFieldInfo(fid);
String aname = prefix + "." + field.Name;
joinedObj = obj->NavigateField(fid);
Value val = joinedObj->GetField(fid);
/* hide attributes which shouldn't be user-visible */
if (field.Attributes & FANoUserView)
continue;
/* hide internal navigation fields */
if (field.Attributes & FANavigation) {
Value nval = joinedObj->NavigateField(fid);
if (val == nval)
continue;
}
Value sval = Serialize(val, FAConfig | FAState);
resultAttrs->Set(aname, sval);
}
}
Array::Ptr used_by = new Array();
result1->Set("used_by", used_by);
BOOST_FOREACH(const Object::Ptr& pobj, DependencyGraph::GetParents((obj))) {
ConfigObject::Ptr configObj = dynamic_pointer_cast<ConfigObject>(pobj);
if (!configObj)
if (!joinedObj)
continue;
Dictionary::Ptr refInfo = new Dictionary();
refInfo->Set("type", configObj->GetType()->GetName());
refInfo->Set("name", configObj->GetName());
used_by->Add(refInfo);
prefix = field.NavigationName;
boost::algorithm::to_lower(prefix);
try {
joins->Set(prefix, SerializeObjectAttrs(joinedObj, prefix, ujoins, true, allJoins));
} catch (const ScriptError& ex) {
HttpUtility::SendJsonError(response, 400, ex.what());
return true;
}
}
}
@ -201,4 +251,3 @@ bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
return true;
}

View File

@ -31,6 +31,10 @@ public:
DECLARE_PTR_TYPEDEFS(ObjectQueryHandler);
virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override;
private:
static Dictionary::Ptr SerializeObjectAttrs(const Object::Ptr& object, const String& attrPrefix,
const Array::Ptr& attrs, bool isJoin, bool allAttrs);
};
}