bool EnoceanDevice::getProfileVariants(ApiValuePtr aApiObjectValue) { // check if current profile is one of the interchangeable ones const ProfileVariantEntry *currentVariant = profileVariantsTable(); while (currentVariant && currentVariant->profileGroup!=0) { // look for current EEP in the list of variants if (getEEProfile()==currentVariant->eep) { // create string from all other variants (same profileGroup), if any bool anyVariants = false; const ProfileVariantEntry *variant = profileVariantsTable(); while (variant->profileGroup!=0) { if (variant->profileGroup==currentVariant->profileGroup) { if (variant->eep!=getEEProfile()) anyVariants = true; // another variant than just myself aApiObjectValue->add(string_format("%d",variant->eep), aApiObjectValue->newString(variant->description)); } variant++; } // there are variants return anyVariants; } currentVariant++; } return false; // no variants }
ErrorPtr PropertyContainer::accessProperty(PropertyAccessMode aMode, ApiValuePtr aQueryObject, ApiValuePtr aResultObject, int aDomain, PropertyDescriptorPtr aParentDescriptor) { ErrorPtr err; #if DEBUGFOCUSLOGGING FOCUSLOG("\naccessProperty: entered with query = %s\n", aQueryObject->description().c_str()); if (aParentDescriptor) { FOCUSLOG("- parentDescriptor '%s' (%s), fieldKey=%u, objectKey=%u\n", aParentDescriptor->name(), aParentDescriptor->isStructured() ? "structured" : "scalar", aParentDescriptor->fieldKey(), aParentDescriptor->objectKey()); } #endif // for reading, NULL query is like query { "":NULL } if (aQueryObject->isNull() && aMode==access_read) { aQueryObject->setType(apivalue_object); aQueryObject->add("", aQueryObject->newValue(apivalue_null)); } // aApiObject must be of type apivalue_object if (!aQueryObject->isType(apivalue_object)) return ErrorPtr(new VdcApiError(415, "Query or Value written must be object")); if (aMode==access_read) { if (!aResultObject) return ErrorPtr(new VdcApiError(415, "accessing property for read must provide result object")); aResultObject->setType(apivalue_object); // must be object } // Iterate trough elements of query object aQueryObject->resetKeyIteration(); string queryName; ApiValuePtr queryValue; string errorMsg; while (aQueryObject->nextKeyValue(queryName, queryValue)) { FOCUSLOG("- starting to process query element named '%s' : %s\n", queryName.c_str(), queryValue->description().c_str()); if (aMode==access_read && queryName=="#") { // asking for number of elements at this level -> generate and return int value queryValue = queryValue->newValue(apivalue_int64); // integer queryValue->setInt32Value(numProps(aDomain, aParentDescriptor)); aResultObject->add(queryName, queryValue); } else { // accessing an element or series of elements at this level bool wildcard = isMatchAll(queryName); // - find all descriptor(s) for this queryName PropertyDescriptorPtr propDesc; int propIndex = 0; bool foundone = false; do { propDesc = getDescriptorByName(queryName, propIndex, aDomain, aParentDescriptor); if (propDesc) { foundone = true; // found at least one descriptor for this query element FOCUSLOG(" - processing descriptor '%s' (%s), fieldKey=%u, objectKey=%u\n", propDesc->name(), propDesc->isStructured() ? "structured" : "scalar", propDesc->fieldKey(), propDesc->objectKey()); // actually access by descriptor if (propDesc->isStructured()) { ApiValuePtr subQuery; // property is a container. Now check the value if (queryValue->isType(apivalue_object)) { subQuery = queryValue; // query specifies next level, just use it } else if (queryName!="*" && (!wildcard || propDesc->isWildcardAddressable())) { // don't recurse deeper when query name is "*" or property is not wildcard-adressable // special case is "*" as leaf in query - only recurse if it is not present // - autocreate subquery subQuery = queryValue->newValue(apivalue_object); subQuery->add("", queryValue->newValue(apivalue_null)); } if (subQuery) { // addressed property is a container by itself -> recurse // - get the PropertyContainer int containerDomain = aDomain; // default to same, but getContainer may modify it PropertyDescriptorPtr containerPropDesc = propDesc; PropertyContainerPtr container = getContainer(containerPropDesc, containerDomain); if (container) { FOCUSLOG(" - container for '%s' is 0x%p\n", propDesc->name(), container.get()); FOCUSLOG(" >>>> RECURSING into accessProperty()\n"); if (aMode==access_read) { // read needs a result object ApiValuePtr resultValue = queryValue->newValue(apivalue_object); err = container->accessProperty(aMode, subQuery, resultValue, containerDomain, containerPropDesc); if (Error::isOK(err)) { // add to result with actual name (from descriptor) FOCUSLOG("\n <<<< RETURNED from accessProperty() recursion\n"); FOCUSLOG(" - accessProperty of container for '%s' returns %s\n", propDesc->name(), resultValue->description().c_str()); aResultObject->add(propDesc->name(), resultValue); } } else { // for write, just pass the query value err = container->accessProperty(aMode, subQuery, ApiValuePtr(), containerDomain, containerPropDesc); FOCUSLOG(" <<<< RETURNED from accessProperty() recursion\n", propDesc->name(), container.get()); } if ((aMode!=access_read) && Error::isOK(err)) { // give this container a chance to post-process write access err = writtenProperty(aMode, propDesc, aDomain, container); } // 404 errors are collected, but dont abort the query if (Error::isError(err, VdcApiError::domain(), 404)) { if (!errorMsg.empty()) errorMsg += "; "; errorMsg += string_format("Error(s) accessing subproperties of '%s' : { %s }", queryName.c_str(), err->description().c_str()); err.reset(); // forget the error on this level } } } } else { // addressed (and known by descriptor!) property is a simple value field -> access it if (aMode==access_read) { // read access: create a new apiValue and have it filled ApiValuePtr fieldValue = queryValue->newValue(propDesc->type()); // create a value of correct type to get filled bool accessOk = accessField(aMode, fieldValue, propDesc); // read // for read, not getting an OK from accessField means: property does not exist (even if known per descriptor), // so it will not be added to the result if (accessOk) { // add to result with actual name (from descriptor) aResultObject->add(propDesc->name(), fieldValue); } FOCUSLOG(" - accessField for '%s' returns %s\n", propDesc->name(), fieldValue->description().c_str()); } else { // write access: just pass the value if (!accessField(aMode, queryValue, propDesc)) { // write err = ErrorPtr(new VdcApiError(403,string_format("Write access to '%s' denied", propDesc->name()))); } } } } else { // no descriptor found for this query element // Note: this means that property is not KNOWN, which IS not the same as getting false from accessField // (latter means that property IS known, but has no value in the context it was queried) // HOWEVER, for the vdc API it was decided that for reading, these cases should NOT be // distinguished for getProperty. If a property does not exist for either reason, the return tree just does // no contain that property. // Also note that not having a property is not the same as not having a property VALUE: // latter case will explicitly return a NULL value if (!wildcard && !foundone && aMode!=access_read) { // query did address a specific property but it is unknown -> report as error (for read AND write!) // - collect error message, but do not abort query processing if (!errorMsg.empty()) errorMsg += "; "; errorMsg += string_format("Unknown property '%s' -> ignored", queryName.c_str()); } } } while (Error::isOK(err) && propIndex!=PROPINDEX_NONE); } // now generate error if we have collected a non-empty error message if (!errorMsg.empty()) { err = ErrorPtr(new VdcApiError(404,errorMsg)); } #if DEBUGLOGGING if (aMode==access_read) { FOCUSLOG("- query element named '%s' now has result object: %s\n", queryName.c_str(), aResultObject->description().c_str()); } #endif } return err; }
// access to vdc API methods and notifications via web requests ErrorPtr P44VdcHost::processVdcRequest(JsonCommPtr aJsonComm, JsonObjectPtr aRequest) { ErrorPtr err; string cmd; bool isMethod = false; // get method/notification and params JsonObjectPtr m = aRequest->get("method"); if (m) { // is a method call, expects answer isMethod = true; } else { // not method, may be notification m = aRequest->get("notification"); } if (!m) { err = Error::err<P44VdcError>(400, "invalid request, must specify 'method' or 'notification'"); } else { // get method/notification name cmd = m->stringValue(); // get params // Note: the "method" or "notification" param will also be in the params, but should not cause any problem ApiValuePtr params = JsonApiValue::newValueFromJson(aRequest); P44JsonApiRequestPtr request = P44JsonApiRequestPtr(new P44JsonApiRequest(aJsonComm)); if (Error::isOK(err)) { // operation method if (isMethod) { // create request // check for old-style name/index and generate basic query (1 or 2 levels) ApiValuePtr query = params->newObject(); ApiValuePtr name = params->get("name"); if (name) { ApiValuePtr index = params->get("index"); ApiValuePtr subquery = params->newNull(); if (index) { // subquery subquery->setType(apivalue_object); subquery->add(index->stringValue(), subquery->newNull()); } string nm = trimWhiteSpace(name->stringValue()); // to allow a single space for deep recursing wildcard query->add(nm, subquery); params->add("query", query); } // have method handled err = handleMethodForParams(request, cmd, params); // Note: if method returns NULL, it has sent or will send results itself. // Otherwise, even if Error is ErrorOK we must send a generic response } else { // handle notification err = handleNotificationForParams(request->connection(), cmd, params); // Notifications are always immediately confirmed, so make sure there's an explicit ErrorOK if (!err) { err = ErrorPtr(new Error(Error::OK)); } } } } // returning NULL means caller should not do anything more // returning an Error object (even ErrorOK) means caller should return status return err; }