/* **************************************************************************** * * addContextProviders - * * This function takes a CRR vector and adds the Context Providers in the CER vector * (except the ones corresponding to some locally found attribute, i.e. info already in the * CER vector) * * The limitReached parameter is to prevent the addition of new entities, which is needed in the case of the pagination * limit has been reached with local entities. * * The enP parameter is optional. If not NULL, then before adding a CPr the function checks that the * containting CRR matches the entity (this is used for funcionality related to "generic queries", see * processGenericEntities() function) * */ void addContextProviders(ContextElementResponseVector& cerV, ContextRegistrationResponseVector& crrV, bool limitReached, EntityId* enP = NULL) { for (unsigned int ix = 0; ix < crrV.size(); ++ix) { ContextRegistration cr = crrV.get(ix)->contextRegistration; /* In the case a "filtering" entity was provided, check that the current CRR matches or skip to next CRR */ if (enP != NULL && !matchEntityInCrr(cr, enP)) { continue; } if (cr.contextRegistrationAttributeVector.size() == 0) { if (!limitReached) { /* Registration without attributes */ for (unsigned int eIx = 0; eIx < cr.entityIdVector.size(); ++eIx) { addContextProviderEntity(cerV, cr.entityIdVector.get(eIx), cr.providingApplication); } } } else { /* Registration with attributes */ for (unsigned int eIx = 0; eIx < cr.entityIdVector.size(); ++eIx) { for (unsigned int aIx = 0; aIx < cr.contextRegistrationAttributeVector.size(); ++aIx) { addContextProviderAttribute(cerV, cr.entityIdVector.get(eIx), cr.contextRegistrationAttributeVector.get(aIx), cr.providingApplication, limitReached); } } } } }
/* **************************************************************************** * * all - */ TEST(ContextRegistrationResponseVector, all) { ContextRegistrationResponse crr; ContextRegistrationResponseVector crrV; std::string rendered; crr.contextRegistration.providingApplication.set("10.1.1.1://nada"); // Empty vector gives empty rendered result rendered = crrV.render(XML, ""); EXPECT_EQ("", rendered); crrV.push_back(&crr); // presenting - just to exercise the code crrV.present(""); // check OK rendered = crrV.check(RegisterContext, XML, "", "", 0); EXPECT_EQ("OK", rendered); // Now telling the crr that we've found an instance of '<entityIdList></entityIdList> // but without any entities inside the vector crr.contextRegistration.entityIdVectorPresent = true; rendered = crrV.check(RegisterContext, XML, "", "", 0); EXPECT_EQ("Empty entityIdVector", rendered); EntityId eId; // Empty ID crr.contextRegistration.entityIdVector.push_back(&eId); rendered = crrV.check(RegisterContext, XML, "", "", 0); EXPECT_EQ("empty entityId:id", rendered); }
/* **************************************************************************** * * equalContextRegistrationResponseVector - */ static bool equalContextRegistrationResponseVector ( ContextRegistrationResponseVector crrExpectedV, ContextRegistrationResponseVector crrArgV ) { /* Check vector size */ if (crrExpectedV.size() != crrArgV.size()) { LM_M(("different sizes: expected %d, actual %d", crrExpectedV.size(), crrArgV.size())); return false; } /* Check that every context registration in 'crrArgV' is in 'crrExpectedV'. Order doesn't matter */ for (unsigned int ix = 0; ix < crrArgV.size(); ++ix) { bool contextRegistrationMatch = false; for (unsigned int jx = 0; jx < crrExpectedV.size(); ++jx) { ContextRegistration crArg = crrArgV[ix]->contextRegistration; ContextRegistration crExpected = crrExpectedV[jx]->contextRegistration; LM_M(("%d == %d?", ix, jx)); if (!equalEntityIdVector(crExpected.entityIdVector, crArg.entityIdVector)) { LM_M(("entity vector doesn't match in ContextRegistrationResponseVector comparison, continue...")); continue; /* loop in jx */ } if (!equalContextRegistrationAttributeVector(crExpected.contextRegistrationAttributeVector, crArg.contextRegistrationAttributeVector)) { LM_M(("context registration attribute vector doesn't match in ContextRegistrationResponseVector comparison")); continue; /* loop in jx */ } if (crExpected.providingApplication.get() == crArg.providingApplication.get()) { contextRegistrationMatch = true; LM_M(("context registration match in ContextRegistrationResponseVector comparison, check next one...")); break; /* loop in jx */ } } if (!contextRegistrationMatch) { LM_M(("after looking everyone, context registration doesn't matches in ContextElementResponseVector comparison")); return false; } } LM_M(("ContextElementResponseVector comparison ok")); return true; }
/* **************************************************************************** * * associationsDiscoverContextAvailability - */ static HttpStatusCode associationsDiscoverContextAvailability ( DiscoverContextAvailabilityRequest* requestP, DiscoverContextAvailabilityResponse* responseP, const std::string& scope, const std::string& tenant, int offset, int limit, bool details, const std::vector<std::string>& servicePathV ) { if (scope == SCOPE_VALUE_ASSOC_ALL) { LM_W(("Bad Input (%s scope not supported)", SCOPE_VALUE_ASSOC_ALL)); responseP->errorCode.fill(SccNotImplemented, std::string("Not supported scope: '") + SCOPE_VALUE_ASSOC_ALL + "'"); return SccOk; } MetadataVector mdV; std::string err; if (!associationsQuery(&requestP->entityIdVector, &requestP->attributeList, scope, &mdV, &err, tenant, offset, limit, details, servicePathV)) { mdV.release(); responseP->errorCode.fill(SccReceiverInternalError, std::string("Database error: ") + err); return SccOk; } LM_T(LmtPagination, ("Offset: %d, Limit: %d, Details: %s", offset, limit, (details == true)? "true" : "false")); /* Query for associated entities */ for (unsigned int ix = 0; ix < mdV.size(); ++ix) { /* Each association involves a registrationsQuery() operation, accumulating the answer in * responseP->responseVector */ Metadata* md = mdV.get(ix); EntityIdVector enV; AttributeList attrL; EntityId en; if (scope == SCOPE_VALUE_ASSOC_SOURCE) { en = EntityId(md->association.entityAssociation.source.id, md->association.entityAssociation.source.type); } else { // SCOPE_VALUE_ASSOC_TARGET en = EntityId(md->association.entityAssociation.target.id, md->association.entityAssociation.target.type); } enV.push_back(&en); for (unsigned int jx = 0; jx < md->association.attributeAssociationList.size(); ++jx) { if (scope == SCOPE_VALUE_ASSOC_SOURCE) { attrL.push_back(md->association.attributeAssociationList.get(jx)->source); } else { attrL.push_back(md->association.attributeAssociationList.get(jx)->target); } } ContextRegistrationResponseVector crrV; if (!registrationsQuery(enV, attrL, &crrV, &err, tenant, servicePathV)) { responseP->errorCode.fill(SccReceiverInternalError, err); mdV.release(); return SccOk; } /* Accumulate in responseP */ for (unsigned int jx = 0; jx < crrV.size(); ++jx) { responseP->responseVector.push_back(crrV.get(jx)); } } if (responseP->responseVector.size() == 0) { mdV.release(); responseP->errorCode.fill(SccContextElementNotFound, "Could not query association with combination of entity/attribute"); LM_RE(SccOk, (responseP->errorCode.details.c_str())); } /* Set association metadata as final ContextRegistrationResponse */ ContextRegistrationResponse* crrMd = new ContextRegistrationResponse(); crrMd->contextRegistration.providingApplication.set("http://www.fi-ware.eu/NGSI/association"); crrMd->contextRegistration.registrationMetadataVector = mdV; responseP->responseVector.push_back(crrMd); return SccOk; }
/* **************************************************************************** * * mongoQueryContext - * * NOTE * If the in/out-parameter countP is non-NULL then the number of matching entities * must be returned in *countP. * * This replaces the 'uriParams[URI_PARAM_PAGINATION_DETAILS]' way of passing this information. * The old method was one-way, using the new method */ HttpStatusCode mongoQueryContext ( QueryContextRequest* requestP, QueryContextResponse* responseP, const std::string& tenant, const std::vector<std::string>& servicePathV, std::map<std::string, std::string>& uriParams, long long* countP ) { int offset = atoi(uriParams[URI_PARAM_PAGINATION_OFFSET].c_str()); int limit = atoi(uriParams[URI_PARAM_PAGINATION_LIMIT].c_str()); std::string detailsString = uriParams[URI_PARAM_PAGINATION_DETAILS]; bool details = (strcasecmp("on", detailsString.c_str()) == 0)? true : false; LM_T(LmtMongo, ("QueryContext Request")); LM_T(LmtPagination, ("Offset: %d, Limit: %d, Details: %s", offset, limit, (details == true)? "true" : "false")); /* FIXME: restriction not supported for the moment */ if (!requestP->restriction.attributeExpression.isEmpty()) { LM_W(("Bad Input (restriction found, but restrictions are not supported by mongo backend)")); } std::string err; bool ok; bool limitReached = false; bool reqSemTaken; ContextElementResponseVector rawCerV; reqSemTake(__FUNCTION__, "ngsi10 query request", SemReadOp, &reqSemTaken); ok = entitiesQuery(requestP->entityIdVector, requestP->attributeList, requestP->restriction, &rawCerV, &err, true, tenant, servicePathV, offset, limit, &limitReached, countP); if (!ok) { responseP->errorCode.fill(SccReceiverInternalError, err); rawCerV.release(); reqSemGive(__FUNCTION__, "ngsi10 query request", reqSemTaken); return SccOk; } ContextRegistrationResponseVector crrV; /* In the case of empty response, if only generic processing is needed */ if (rawCerV.size() == 0) { if (registrationsQuery(requestP->entityIdVector, requestP->attributeList, &crrV, &err, tenant, servicePathV, 0, 0, false)) { if (crrV.size() > 0) { processGenericEntities(requestP->entityIdVector, rawCerV, crrV, limitReached); } } else { /* Different from errors in DB at entitiesQuery(), DB fails at registrationsQuery() are not considered "critical" */ LM_E(("Database Error (%s)", err.c_str())); } crrV.release(); } /* First CPr lookup (in the case some CER is not found): looking in E-A registrations */ if (someContextElementNotFound(rawCerV)) { if (registrationsQuery(requestP->entityIdVector, requestP->attributeList, &crrV, &err, tenant, servicePathV, 0, 0, false)) { if (crrV.size() > 0) { fillContextProviders(rawCerV, crrV); processGenericEntities(requestP->entityIdVector, rawCerV, crrV, limitReached); } } else { /* Different from errors in DB at entitiesQuery(), DB fails at registrationsQuery() are not considered "critical" */ LM_E(("Database Error (%s)", err.c_str())); } crrV.release(); } /* Second CPr lookup (in the case some element stills not being found): looking in E-<null> registrations */ AttributeList attrNullList; if (someContextElementNotFound(rawCerV)) { if (registrationsQuery(requestP->entityIdVector, attrNullList, &crrV, &err, tenant, servicePathV, 0, 0, false)) { if (crrV.size() > 0) { fillContextProviders(rawCerV, crrV); } } else { /* Different from errors in DB at entitiesQuery(), DB fails at registrationsQuery() are not considered "critical" */ LM_E(("Database Error (%s)", err.c_str())); } crrV.release(); } /* Special case: request with <null> attributes. In that case, entitiesQuery() may have captured some local attribute, but * the list need to be completed. Note that in the case of having this request someContextElementNotFound() is always false * so we efficient not invoking registrationQuery() too much times */ if (requestP->attributeList.size() == 0) { if (registrationsQuery(requestP->entityIdVector, requestP->attributeList, &crrV, &err, tenant, servicePathV, 0, 0, false)) { if (crrV.size() > 0) { addContextProviders(rawCerV, crrV, limitReached); } } else { /* Different from fails in DB at entitiesQuery(), DB fails at registrationsQuery() are not considered "critical" */ LM_E(("Database Error (%s)", err.c_str())); } crrV.release(); } /* Prune "not found" CERs */ pruneContextElements(rawCerV, &responseP->contextElementResponseVector); /* Pagination stuff */ if (responseP->contextElementResponseVector.size() == 0) { // If the query has an empty response, we have to fill in the status code part in the response. // // However, if the response was empty due to a too high pagination offset, // and if the user has asked for 'details' (as URI parameter, then the response should include information about // the number of hits without pagination. // if ((countP != NULL) && (*countP > 0) && (offset >= *countP)) { char details[256]; snprintf(details, sizeof(details), "Number of matching entities: %lld. Offset is %d", *countP, offset); responseP->errorCode.fill(SccContextElementNotFound, details); } else { responseP->errorCode.fill(SccContextElementNotFound); } } else if (countP != NULL) { // // If all was OK, but the details URI param was set to 'on', then the responses error code details // 'must' contain the total count of hits. // char details[64]; snprintf(details, sizeof(details), "Count: %lld", *countP); responseP->errorCode.fill(SccOk, details); } rawCerV.release(); reqSemGive(__FUNCTION__, "ngsi10 query request", reqSemTaken); return SccOk; }