void ConfigObject::DumpModifiedAttributes(const std::function<void(const ConfigObject::Ptr&, const String&, const Value&)>& callback) { for (const Type::Ptr& type : Type::GetAllTypes()) { auto *dtype = dynamic_cast<ConfigType *>(type.get()); if (!dtype) continue; for (const ConfigObject::Ptr& object : dtype->GetObjects()) { Dictionary::Ptr originalAttributes = object->GetOriginalAttributes(); if (!originalAttributes) continue; ObjectLock olock(originalAttributes); for (const Dictionary::Pair& kv : originalAttributes) { String key = kv.first; Type::Ptr type = object->GetReflectionType(); std::vector<String> tokens = key.Split("."); String fieldName = tokens[0]; int fid = type->GetFieldId(fieldName); Value currentValue = object->GetField(fid); Value modifiedValue; if (tokens.size() > 1) { Value current = currentValue; for (std::vector<String>::size_type i = 1; i < tokens.size() - 1; i++) { if (!current.IsObjectType<Dictionary>()) BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary.")); Dictionary::Ptr dict = current; const String& key = tokens[i]; if (!dict->Contains(key)) break; current = dict->Get(key); } if (!current.IsObjectType<Dictionary>()) BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary.")); Dictionary::Ptr dict = current; const String& key = tokens[tokens.size() - 1]; modifiedValue = dict->Get(key); } else modifiedValue = currentValue; callback(object, key, modifiedValue); } } } }
String ConfigObjectUtility::CreateObjectConfig(const Type::Ptr& type, const String& fullName, bool ignoreOnError, const Array::Ptr& templates, const Dictionary::Ptr& attrs) { NameComposer *nc = dynamic_cast<NameComposer *>(type.get()); Dictionary::Ptr nameParts; String name; if (nc) { nameParts = nc->ParseName(fullName); name = nameParts->Get("name"); } else name = fullName; Dictionary::Ptr allAttrs = new Dictionary(); if (attrs) { attrs->CopyTo(allAttrs); ObjectLock olock(attrs); BOOST_FOREACH(const Dictionary::Pair& kv, attrs) { int fid = type->GetFieldId(kv.first.SubStr(0, kv.first.FindFirstOf("."))); if (fid < 0) BOOST_THROW_EXCEPTION(ScriptError("Invalid attribute specified: " + kv.first)); Field field = type->GetFieldInfo(fid); if (!(field.Attributes & FAConfig) || kv.first == "name") BOOST_THROW_EXCEPTION(ScriptError("Attribute is marked for internal use only and may not be set: " + kv.first)); } }
static void TypeRegisterAttributeHandler(const String& fieldName, const Function::Ptr& callback) { ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); Type::Ptr self = static_cast<Type::Ptr>(vframe->Self); int fid = self->GetFieldId(fieldName); self->RegisterAttributeHandler(fid, boost::bind(&InvokeAttributeHandlerHelper, callback, _1, _2)); }
bool Object::HasOwnField(const String& field) const { Type::Ptr type = GetReflectionType(); if (!type) return false; return type->GetFieldId(field) != -1; }
int PrimitiveType::GetFieldId(const String& name) const { Type::Ptr base = GetBaseType(); if (base) return base->GetFieldId(name); else return -1; }
bool Object::GetOwnField(const String& field, Value *result) const { Type::Ptr type = GetReflectionType(); if (!type) return false; int tid = type->GetFieldId(field); if (tid == -1) return false; *result = GetField(tid); return true; }
String ConfigObjectUtility::CreateObjectConfig(const Type::Ptr& type, const String& fullName, bool ignoreOnError, const Array::Ptr& templates, const Dictionary::Ptr& attrs) { NameComposer *nc = dynamic_cast<NameComposer *>(type.get()); Dictionary::Ptr nameParts; String name; if (nc) { nameParts = nc->ParseName(fullName); name = nameParts->Get("name"); } else name = fullName; Dictionary::Ptr allAttrs = new Dictionary(); if (attrs) { attrs->CopyTo(allAttrs); ObjectLock olock(attrs); for (const Dictionary::Pair& kv : attrs) { int fid = type->GetFieldId(kv.first.SubStr(0, kv.first.FindFirstOf("."))); if (fid < 0) BOOST_THROW_EXCEPTION(ScriptError("Invalid attribute specified: " + kv.first)); Field field = type->GetFieldInfo(fid); if (!(field.Attributes & FAConfig) || kv.first == "name") BOOST_THROW_EXCEPTION(ScriptError("Attribute is marked for internal use only and may not be set: " + kv.first)); } } if (nameParts) nameParts->CopyTo(allAttrs); allAttrs->Remove("name"); /* update the version for config sync */ allAttrs->Set("version", Utility::GetTime()); std::ostringstream config; ConfigWriter::EmitConfigItem(config, type->GetName(), name, false, ignoreOnError, templates, allAttrs); ConfigWriter::EmitRaw(config, "\n"); return config.str(); }
Value Object::GetFieldByName(const String& field, bool sandboxed, const DebugInfo& debugInfo) const { Type::Ptr type = GetReflectionType(); if (!type) return Empty; int fid = type->GetFieldId(field); if (fid == -1) return GetPrototypeField(const_cast<Object *>(this), field, true, debugInfo); if (sandboxed) { Field fieldInfo = type->GetFieldInfo(fid); if (fieldInfo.Attributes & FANoUserView) BOOST_THROW_EXCEPTION(ScriptError("Accessing the field '" + field + "' for type '" + type->GetName() + "' is not allowed in sandbox mode.", debugInfo)); } return GetField(fid); }
void Object::SetFieldByName(const String& field, const Value& value, const DebugInfo& debugInfo) { Type::Ptr type = GetReflectionType(); if (!type) BOOST_THROW_EXCEPTION(ScriptError("Cannot set field on object.", debugInfo)); int fid = type->GetFieldId(field); if (fid == -1) BOOST_THROW_EXCEPTION(ScriptError("Attribute '" + field + "' does not exist.", debugInfo)); try { SetField(fid, value); } catch (const boost::bad_lexical_cast&) { Field fieldInfo = type->GetFieldInfo(fid); Type::Ptr ftype = Type::GetByName(fieldInfo.TypeName); BOOST_THROW_EXCEPTION(ScriptError("Attribute '" + field + "' cannot be set to value of type '" + value.GetTypeName() + "', expected '" + ftype->GetName() + "'", debugInfo)); } catch (const std::bad_cast&) { Field fieldInfo = type->GetFieldInfo(fid); Type::Ptr ftype = Type::GetByName(fieldInfo.TypeName); BOOST_THROW_EXCEPTION(ScriptError("Attribute '" + field + "' cannot be set to value of type '" + value.GetTypeName() + "', expected '" + ftype->GetName() + "'", debugInfo)); } }
void ConfigObject::RestoreAttribute(const String& attr, bool updateVersion) { Type::Ptr type = GetReflectionType(); std::vector<String> tokens = attr.Split("."); String fieldName = tokens[0]; int fid = type->GetFieldId(fieldName); Value currentValue = GetField(fid); Dictionary::Ptr original_attributes = GetOriginalAttributes(); if (!original_attributes) return; Value oldValue = original_attributes->Get(attr); Value newValue; if (tokens.size() > 1) { newValue = currentValue.Clone(); Value current = newValue; if (current.IsEmpty()) BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot restore non-existent object attribute")); String prefix = tokens[0]; for (std::vector<String>::size_type i = 1; i < tokens.size() - 1; i++) { if (!current.IsObjectType<Dictionary>()) BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary.")); Dictionary::Ptr dict = current; const String& key = tokens[i]; prefix += "." + key; if (!dict->Contains(key)) BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot restore non-existent object attribute")); current = dict->Get(key); } if (!current.IsObjectType<Dictionary>()) BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary.")); Dictionary::Ptr dict = current; const String& key = tokens[tokens.size() - 1]; prefix += "." + key; std::vector<String> restoredAttrs; { ObjectLock olock(original_attributes); for (const auto& kv : original_attributes) { std::vector<String> originalTokens = String(kv.first).Split("."); if (tokens.size() > originalTokens.size()) continue; bool match = true; for (std::vector<String>::size_type i = 0; i < tokens.size(); i++) { if (tokens[i] != originalTokens[i]) { match = false; break; } } if (!match) continue; Dictionary::Ptr dict; if (tokens.size() == originalTokens.size()) dict = current; else { Value currentSub = current; for (std::vector<String>::size_type i = tokens.size() - 1; i < originalTokens.size() - 1; i++) { dict = currentSub; currentSub = dict->Get(originalTokens[i]); if (!currentSub.IsObjectType<Dictionary>()) { currentSub = new Dictionary(); dict->Set(originalTokens[i], currentSub); } } dict = currentSub; } dict->Set(originalTokens[originalTokens.size() - 1], kv.second); restoredAttrs.push_back(kv.first); } } for (const String& attr : restoredAttrs) original_attributes->Remove(attr); } else { newValue = oldValue; } original_attributes->Remove(attr); SetField(fid, newValue); if (updateVersion) SetVersion(Utility::GetTime()); }
void ConfigObject::ModifyAttribute(const String& attr, const Value& value, bool updateVersion) { Dictionary::Ptr original_attributes = GetOriginalAttributes(); bool updated_original_attributes = false; Type::Ptr type = GetReflectionType(); std::vector<String> tokens = attr.Split("."); String fieldName = tokens[0]; int fid = type->GetFieldId(fieldName); Field field = type->GetFieldInfo(fid); if (field.Attributes & FANoUserModify) BOOST_THROW_EXCEPTION(std::invalid_argument("Attribute cannot be modified.")); if (field.Attributes & FAConfig) { if (!original_attributes) { original_attributes = new Dictionary(); SetOriginalAttributes(original_attributes, true); } } Value oldValue = GetField(fid); Value newValue; if (tokens.size() > 1) { newValue = oldValue.Clone(); Value current = newValue; if (current.IsEmpty()) { current = new Dictionary(); newValue = current; } String prefix = tokens[0]; for (std::vector<String>::size_type i = 1; i < tokens.size() - 1; i++) { if (!current.IsObjectType<Dictionary>()) BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary.")); Dictionary::Ptr dict = current; const String& key = tokens[i]; prefix += "." + key; if (!dict->Get(key, ¤t)) { current = new Dictionary(); dict->Set(key, current); } } if (!current.IsObjectType<Dictionary>()) BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary.")); Dictionary::Ptr dict = current; const String& key = tokens[tokens.size() - 1]; prefix += "." + key; /* clone it for original attributes */ oldValue = dict->Get(key).Clone(); if (field.Attributes & FAConfig) { updated_original_attributes = true; if (oldValue.IsObjectType<Dictionary>()) { Dictionary::Ptr oldDict = oldValue; ObjectLock olock(oldDict); for (const auto& kv : oldDict) { String key = prefix + "." + kv.first; if (!original_attributes->Contains(key)) original_attributes->Set(key, kv.second); } /* store the new value as null */ if (value.IsObjectType<Dictionary>()) { Dictionary::Ptr valueDict = value; ObjectLock olock(valueDict); for (const auto& kv : valueDict) { String key = attr + "." + kv.first; if (!original_attributes->Contains(key)) original_attributes->Set(key, Empty); } } } else if (!original_attributes->Contains(attr)) original_attributes->Set(attr, oldValue); } dict->Set(key, value); } else { newValue = value; if (field.Attributes & FAConfig) { if (!original_attributes->Contains(attr)) { updated_original_attributes = true; original_attributes->Set(attr, oldValue); } } } ModAttrValidationUtils utils; ValidateField(fid, Lazy<Value>{newValue}, utils); SetField(fid, newValue); if (updateVersion && (field.Attributes & FAConfig)) SetVersion(Utility::GetTime()); if (updated_original_attributes) NotifyOriginalAttributes(); }
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); for (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); for (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); } } DictionaryData resultAttrs; resultAttrs.reserve(fids.size()); for (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 && !(field.Attributes & (FAConfig | FAState))) continue; Value sval = Serialize(val, FAConfig | FAState); resultAttrs.emplace_back(field.Name, sval); } return new Dictionary(std::move(resultAttrs)); }
bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4) return false; if (request.RequestMethod != "GET") return false; Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[2]); if (!type) { HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); return true; } QueryDescription qd; qd.Types.insert(type->GetName()); qd.Permission = "objects/query/" + type->GetName(); Array::Ptr uattrs, ujoins, umetas; try { uattrs = params->Get("attrs"); } catch (const std::exception&) { HttpUtility::SendJsonError(response, params, 400, "Invalid type for 'attrs' attribute specified. Array type is required."); return true; } try { ujoins = params->Get("joins"); } catch (const std::exception&) { HttpUtility::SendJsonError(response, params, 400, "Invalid type for 'joins' attribute specified. Array type is required."); return true; } try { umetas = params->Get("meta"); } catch (const std::exception&) { HttpUtility::SendJsonError(response, params, 400, "Invalid type for 'meta' attribute specified. Array type is required."); return true; } bool allJoins = HttpUtility::GetLastParameter(params, "all_joins"); params->Set("type", type->GetName()); if (request.RequestUrl->GetPath().size() >= 4) { String attr = type->GetName(); boost::algorithm::to_lower(attr); params->Set(attr, request.RequestUrl->GetPath()[3]); } std::vector<Value> objs; try { objs = FilterUtility::GetFilterTargets(qd, params, user); } catch (const std::exception& ex) { HttpUtility::SendJsonError(response, params, 404, "No objects found.", DiagnosticInformation(ex)); return true; } ArrayData results; results.reserve(objs.size()); std::set<String> joinAttrs; std::set<String> userJoinAttrs; if (ujoins) { ObjectLock olock(ujoins); for (const String& ujoin : ujoins) { userJoinAttrs.insert(ujoin.SubStr(0, ujoin.FindFirstOf("."))); } } for (int fid = 0; fid < type->GetFieldCount(); fid++) { Field field = type->GetFieldInfo(fid); if (!(field.Attributes & FANavigation)) continue; if (!allJoins && userJoinAttrs.find(field.NavigationName) == userJoinAttrs.end()) continue; joinAttrs.insert(field.Name); } for (const ConfigObject::Ptr& obj : objs) { DictionaryData result1{ { "name", obj->GetName() }, { "type", obj->GetReflectionType()->GetName() } }; DictionaryData metaAttrs; if (umetas) { ObjectLock olock(umetas); for (const String& meta : umetas) { if (meta == "used_by") { Array::Ptr used_by = new Array(); metaAttrs.emplace_back("used_by", used_by); for (const Object::Ptr& pobj : DependencyGraph::GetParents((obj))) { ConfigObject::Ptr configObj = dynamic_pointer_cast<ConfigObject>(pobj); if (!configObj) continue; 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; } } } result1.emplace_back("meta", new Dictionary(std::move(metaAttrs))); try { result1.emplace_back("attrs", SerializeObjectAttrs(obj, String(), uattrs, false, false)); } catch (const ScriptError& ex) { HttpUtility::SendJsonError(response, params, 400, ex.what()); return true; } DictionaryData joins; for (const String& joinAttr : joinAttrs) { Object::Ptr joinedObj; int fid = type->GetFieldId(joinAttr); if (fid < 0) { 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) continue; String prefix = field.NavigationName; try { joins.emplace_back(prefix, SerializeObjectAttrs(joinedObj, prefix, ujoins, true, allJoins)); } catch (const ScriptError& ex) { HttpUtility::SendJsonError(response, params, 400, ex.what()); return true; } } result1.emplace_back("joins", new Dictionary(std::move(joins))); results.push_back(new Dictionary(std::move(result1))); } Dictionary::Ptr result = new Dictionary({ { "results", new Array(std::move(results)) } }); response.SetStatus(200, "OK"); HttpUtility::SendJsonBody(response, params, result); return true; }