/* **************************************************************************** * * mongoGetContextElementResponses_fail - */ TEST(mongoOntimeintervalOperations, mongoGetContextElementResponses_fail) { HttpStatusCode ms; /* Forge the parameters */ EntityIdVector enV; EntityId en("E5", "T", "false"); enV.push_back(&en); AttributeList attrL; attrL.push_back("A1"); attrL.push_back("A2"); attrL.push_back("A3"); attrL.push_back("A4"); ContextElementResponseVector cerV; std::string err; /* Prepare database */ prepareDatabase(); /* Do operation */ ms = mongoGetContextElementResponses(enV, attrL, &cerV, &err); /* Check results */ EXPECT_EQ(SccOk, ms); ASSERT_EQ(0, cerV.size()); }
/* **************************************************************************** * * mongoGetContextElementResponses - * * This function is basically a wrapper of mongoBackend internal entitiesQuery() function */ HttpStatusCode mongoGetContextElementResponses(const EntityIdVector& enV, const AttributeList& attrL, ContextElementResponseVector* cerV, std::string* err, const std::string& tenant) { bool reqSemTaken; reqSemTake(__FUNCTION__, "get context-element responses", SemReadOp, &reqSemTaken); LM_T(LmtMongo, ("Get Notify Context Request operation")); // FIXME P10: we are using dummy scope by the moment, until subscription scopes get implemented // FIXME P10: we are using an empty service path vector until service paths get implemented for subscriptions ContextElementResponseVector rawCerV; std::vector<std::string> servicePath; Restriction res; if (!entitiesQuery(enV, attrL, res, &rawCerV, err, true, tenant, servicePath)) { reqSemGive(__FUNCTION__, "get context-element responses (no entities found)", reqSemTaken); rawCerV.release(); return SccOk; } /* Prune "not found" CERs */ pruneContextElements(rawCerV, cerV); rawCerV.release(); reqSemGive(__FUNCTION__, "get context-element responses", reqSemTaken); return SccOk; }
/* **************************************************************************** * * fillContextProviders - * * Looks in the elements of the CER vector passed as argument, searching for a suitable CPr in the CRR * vector passed as argument. If a suitable CPr is found, it is added to the CER (and the 'found' field * is changed to true) * */ void fillContextProviders(ContextElementResponseVector& cerV, ContextRegistrationResponseVector& crrV) { for (unsigned int ix = 0; ix < cerV.size(); ++ix) { fillContextProviders(cerV.get(ix), crrV); } }
/* **************************************************************************** * * someContextElementNotFound - * * Returns true if some attribute with 'found' set to 'false' is found in the CER vector passed * as argument * */ bool someContextElementNotFound(ContextElementResponseVector& cerV) { for (unsigned int ix = 0; ix < cerV.size(); ++ix) { if (someContextElementNotFound(*cerV.get(ix))) { return true; } } return false; }
/* **************************************************************************** * * present - * * Just to exercise the code, nothing to be expected here ... */ TEST(ContextElementResponseVector, present) { ContextElementResponseVector cerv; ContextElementResponse cer; utInit(); cer.contextElement.entityId.id = "ID"; cer.contextElement.entityId.type = "Type"; cer.contextElement.entityId.isPattern = "false"; cer.statusCode.fill(SccOk, "details"); cerv.push_back(&cer); cerv.present(""); utExit(); }
/* **************************************************************************** * * ContextElementResponseVector::fill - */ void ContextElementResponseVector::fill(ContextElementResponseVector& cerV) { for (unsigned int ix = 0; ix < cerV.size(); ++ix) { ContextElementResponse* cerP = new ContextElementResponse(cerV[ix]); push_back(cerP); } }
/* **************************************************************************** * * render - * */ TEST(ContextElementResponseVector, render) { ContextElementResponseVector cerv; ContextElementResponse cer; std::string out; ConnectionInfo ci(JSON); utInit(); out = cerv.render(&ci, UpdateContextElement, ""); EXPECT_STREQ("", out.c_str()); cer.contextElement.entityId.id = "ID"; cer.contextElement.entityId.type = "Type"; cer.contextElement.entityId.isPattern = "false"; cer.statusCode.fill(SccOk, "details"); utExit(); }
/* **************************************************************************** * * addContextProviderEntity - * */ void addContextProviderEntity(ContextElementResponseVector& cerV, EntityId* enP, ProvidingApplication pa) { for (unsigned int ix = 0; ix < cerV.size(); ++ix) { if (cerV.get(ix)->contextElement.entityId.id == enP->id && cerV.get(ix)->contextElement.entityId.type == enP->type) { cerV.get(ix)->contextElement.providingApplicationList.push_back(pa); return; /* by construction, no more than one CER with the same entity information should exist in the CERV) */ } } /* Reached this point, it means that the cerV doesn't contain a proper CER, so we create it */ ContextElementResponse* cerP = new ContextElementResponse(); cerP->contextElement.entityId.id = enP->id; cerP->contextElement.entityId.type = enP->type; cerP->contextElement.entityId.isPattern = "false"; cerP->contextElement.providingApplicationList.push_back(pa); cerP->statusCode.fill(SccOk); cerV.push_back(cerP); }
/* **************************************************************************** * * equalContextElementResponseVector - */ static bool equalContextElementResponseVector ( ContextElementResponseVector cerExpectedV, ContextElementResponseVector cerArgV ) { /* Check vector size */ if (cerExpectedV.size() != cerArgV.size()) { LM_M(("different sizes: expected %d, actual %d", cerExpectedV.size(), cerArgV.size())); return false; } /* Check that every entity in 'cerArgV' is in 'cerExpectedV'. Order doesn't matter */ for (unsigned int ix = 0; ix < cerArgV.size(); ++ix) { bool entityMatch = false; for (unsigned int jx = 0; jx < cerExpectedV.size(); ++jx) { ContextElementResponse* cerArg = cerArgV[ix]; ContextElementResponse* cerExpected = cerExpectedV[jx]; EntityId enExpected(cerExpected->entity.id, cerExpected->entity.type); EntityId enArg(cerArg->entity.id, cerArg->entity.type); if (!equalEntity(enExpected, enArg)) { LM_M(("entity doesn't match in ContextElementResponseVector comparison, continue ...")); continue; /* loop in jx */ } /* If there aren't attributes to check, then early exits the loop */ if (cerExpected->entity.attributeVector.size() == 0) { LM_M(("entity (without attributes) matches in ContextElementResponseVector comparison, check next one...")); entityMatch = true; break; /* loop in jx */ } if (equalContextAttributeVector(cerExpected->entity.attributeVector, cerArg->entity.attributeVector)) { LM_M(("entity (with attributes) matches in ContextElementResponseVector comparison, check next one...")); entityMatch = true; break; /* loop in jx */ } } if (!entityMatch) { LM_M(("after checking everything, entity doesn't match in ContextElementResponseVector comparison")); return false; } } LM_M(("ContextElementResponseVector comparison ok")); return true; }
/* **************************************************************************** * * addContextProviderAttribute - * * 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. * */ void addContextProviderAttribute ( ContextElementResponseVector& cerV, EntityId* enP, ContextRegistrationAttribute* craP, const ProvidingApplication& pa, bool limitReached ) { for (unsigned int ix = 0; ix < cerV.size(); ++ix) { if ((cerV.get(ix)->contextElement.entityId.id != enP->id) || (cerV.get(ix)->contextElement.entityId.type != enP->type)) { continue; } for (unsigned int jx = 0; jx < cerV.get(ix)->contextElement.contextAttributeVector.size(); ++jx) { std::string attrName = cerV.get(ix)->contextElement.contextAttributeVector.get(jx)->name; if (attrName == craP->name) { /* In this case, the attribute has been already found in local database. CPr is unnecessary */ return; } } /* Reached this point, no attribute was found, so adding it with corresponding CPr info */ ContextAttribute* caP = new ContextAttribute(craP->name, "", ""); caP->providingApplication = pa; cerV.get(ix)->contextElement.contextAttributeVector.push_back(caP); return; } if (!limitReached) { /* Reached this point, it means that the cerV doesn't contain a proper CER, so we create it */ ContextElementResponse* cerP = new ContextElementResponse(); cerP->contextElement.entityId.id = enP->id; cerP->contextElement.entityId.type = enP->type; cerP->contextElement.entityId.isPattern = "false"; cerP->statusCode.fill(SccOk); ContextAttribute* caP = new ContextAttribute(craP->name, "", ""); caP->providingApplication = pa; cerP->contextElement.contextAttributeVector.push_back(caP); cerV.push_back(cerP); } }
/* **************************************************************************** * * foundAndNotFoundAttributeSeparation - * * Examine the response from mongo to find out what has really happened ... * */ static void foundAndNotFoundAttributeSeparation(UpdateContextResponse* upcrsP, UpdateContextRequest* upcrP, ConnectionInfo* ciP) { ContextElementResponseVector notFoundV; for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx) { ContextElementResponse* cerP = upcrsP->contextElementResponseVector[cerIx]; // // All attributes with found == false? // int noOfFounds = 0; int noOfNotFounds = 0; for (unsigned int aIx = 0; aIx < cerP->contextElement.contextAttributeVector.size(); ++aIx) { if (cerP->contextElement.contextAttributeVector[aIx]->found == true) { ++noOfFounds; } else { ++noOfNotFounds; } } // // Now, if we have ONLY FOUNDS, then things stay the way they are, one response with '200 OK' // If we have ONLY NOT-FOUNDS, the we have one response with '404 Not Found' // If we have a mix, then we need to add a response for the not founds, with '404 Not Found' // if ((noOfFounds == 0) && (noOfNotFounds > 0)) { if ((cerP->statusCode.code == SccOk) || (cerP->statusCode.code == SccNone)) { cerP->statusCode.fill(SccContextElementNotFound, cerP->contextElement.entityId.id); } } else if ((noOfFounds > 0) && (noOfNotFounds > 0)) { // Adding a ContextElementResponse for the 'Not-Founds' ContextElementResponse* notFoundCerP = new ContextElementResponse(&cerP->contextElement.entityId, NULL); // // Filling in StatusCode (SccContextElementNotFound) for NotFound // notFoundCerP->statusCode.fill(SccContextElementNotFound, cerP->contextElement.entityId.id); // // Setting StatusCode to OK for Found // cerP->statusCode.fill(SccOk); // // And, pushing to NotFound-vector // notFoundV.push_back(notFoundCerP); // Now moving the not-founds to notFoundCerP std::vector<ContextAttribute*>::iterator iter; for (iter = cerP->contextElement.contextAttributeVector.vec.begin(); iter < cerP->contextElement.contextAttributeVector.vec.end();) { if ((*iter)->found == false) { // 1. Push to notFoundCerP notFoundCerP->contextElement.contextAttributeVector.push_back(*iter); // 2. remove from cerP iter = cerP->contextElement.contextAttributeVector.vec.erase(iter); } else { ++iter; } } } // // Add EntityId::id to StatusCode::details if 404, but only if StatusCode::details is empty // if ((cerP->statusCode.code == SccContextElementNotFound) && (cerP->statusCode.details == "")) { cerP->statusCode.details = cerP->contextElement.entityId.id; } } // // Now add the contextElementResponses for 404 Not Found // if (notFoundV.size() != 0) { for (unsigned int ix = 0; ix < notFoundV.size(); ++ix) { upcrsP->contextElementResponseVector.push_back(notFoundV[ix]); } } // // If nothing at all in response vector, mark as not found (but not if DELETE request) // if (ciP->method != "DELETE") { if (upcrsP->contextElementResponseVector.size() == 0) { if (upcrsP->errorCode.code == SccOk) { upcrsP->errorCode.fill(SccContextElementNotFound, upcrP->contextElementVector[0]->entityId.id); } } } // // Add entityId::id to details if Not Found and only one element in response. // And, if 0 elements in response, take entityId::id from the request. // if (upcrsP->errorCode.code == SccContextElementNotFound) { if (upcrsP->contextElementResponseVector.size() == 1) { upcrsP->errorCode.details = upcrsP->contextElementResponseVector[0]->contextElement.entityId.id; } else if (upcrsP->contextElementResponseVector.size() == 0) { upcrsP->errorCode.details = upcrP->contextElementVector[0]->entityId.id; } } }
/* **************************************************************************** * * mongoGetContextElementResponses_pattern - */ TEST(mongoOntimeintervalOperations, mongoGetContextElementResponses_pattern) { HttpStatusCode ms; /* Forge the parameters */ EntityIdVector enV; EntityId en("E[1-2]", "T", "true"); enV.push_back(&en); AttributeList attrL; attrL.push_back("A1"); attrL.push_back("A2"); attrL.push_back("A3"); attrL.push_back("A4"); ContextElementResponseVector cerV; std::string err; /* Prepare database */ prepareDatabase(); /* Do operation */ ms = mongoGetContextElementResponses(enV, attrL, &cerV, &err); /* Check results */ EXPECT_EQ(SccOk, ms); ASSERT_EQ(2, cerV.size()); ContextElementResponse cer0 = *cerV[0]; ContextElementResponse cer1 = *cerV[1]; ContextAttribute ca0, ca1, ca2, ca3; /* Context Element Response #1 */ EXPECT_EQ(SccOk, cer0.statusCode.code); EXPECT_EQ("OK", cer0.statusCode.reasonPhrase); EXPECT_EQ(0, cer0.statusCode.details.length()); EXPECT_EQ("E1", cer0.contextElement.entityId.id); EXPECT_EQ("T", cer0.contextElement.entityId.type); EXPECT_EQ("false", cer0.contextElement.entityId.isPattern); ASSERT_EQ(3, cer0.contextElement.contextAttributeVector.size()); ca0 = *cer0.contextElement.contextAttributeVector[0]; ca1 = *cer0.contextElement.contextAttributeVector[1]; ca2 = *cer0.contextElement.contextAttributeVector[2]; EXPECT_EQ("A1", ca0.name); EXPECT_EQ("TA1", ca0.type); EXPECT_EQ("X", ca0.stringValue); EXPECT_EQ("A2", ca1.name); EXPECT_EQ("TA2", ca1.type); EXPECT_EQ("Z", ca1.stringValue); EXPECT_EQ("A3", ca2.name); EXPECT_EQ("TA3", ca2.type); EXPECT_EQ("W", ca2.stringValue); /* Context Element Response #2 */ EXPECT_EQ(SccOk, cer1.statusCode.code); EXPECT_EQ("OK", cer1.statusCode.reasonPhrase); EXPECT_EQ(0, cer1.statusCode.details.length()); EXPECT_EQ("E2", cer1.contextElement.entityId.id); EXPECT_EQ("T", cer1.contextElement.entityId.type); EXPECT_EQ("false", cer1.contextElement.entityId.isPattern); ASSERT_EQ(2, cer1.contextElement.contextAttributeVector.size()); ca0 = *cer1.contextElement.contextAttributeVector[0]; ca1 = *cer1.contextElement.contextAttributeVector[1]; EXPECT_EQ("A1", ca0.name); EXPECT_EQ("TA1", ca0.type); EXPECT_EQ("S", ca0.stringValue); EXPECT_EQ("A4", ca1.name); EXPECT_EQ("TA4", ca1.type); EXPECT_EQ("T", ca1.stringValue); EXPECT_EQ(0, err.length()); }
/* **************************************************************************** * * 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; }