/* **************************************************************************** * * mapPutIndividualContextEntityAttributes - */ HttpStatusCode mapPutIndividualContextEntityAttributes(const std::string& entityId, UpdateContextElementRequest* ucerP, UpdateContextElementResponse* response, ConnectionInfo* ciP) { HttpStatusCode ms; UpdateContextRequest ucRequest; UpdateContextResponse ucResponse; ContextElement ce; ce.entityId.fill(entityId, "", "false"); ce.attributeDomainName = ucerP->attributeDomainName; ce.contextAttributeVector = ucerP->contextAttributeVector; ucRequest.contextElementVector.push_back(&ce); ucRequest.updateActionType.set("Update"); ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV); ContextAttributeResponse* carP = new ContextAttributeResponse(); ContextElement* ceP = &ucResponse.contextElementResponseVector.get(0)->contextElement; for (unsigned int ix = 0; ix < ceP->contextAttributeVector.size(); ++ix) { ContextAttribute* caP = new ContextAttribute(ceP->contextAttributeVector.get(ix)); carP->contextAttributeVector.push_back(caP); } response->contextAttributeResponseVector.push_back(carP); response->errorCode.fill(&ucResponse.errorCode); carP->statusCode.fill(&ucResponse.contextElementResponseVector.get(0)->statusCode); return ms; }
/* **************************************************************************** * * mapDeleteIndividualContextEntityAttributes - */ HttpStatusCode mapDeleteIndividualContextEntityAttributes ( const std::string& entityId, StatusCode* response, ConnectionInfo* ciP ) { HttpStatusCode ms; UpdateContextRequest ucRequest; UpdateContextResponse ucResponse; ContextElement* ceP = new ContextElement(); ceP->entityId.fill(entityId, "", "false"); ucRequest.contextElementVector.push_back(ceP); ucRequest.updateActionType.set("Delete"); ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV, ciP->uriParam); *response = ucResponse.contextElementResponseVector.get(0)->statusCode; ucRequest.release(); ucResponse.release(); return ms; }
/* **************************************************************************** * * patternFail - * * Discover: R.* - none * Result: none * * isPattern=true is not currently supported in updateContext, so this test it disabled: enable it once * this gets supported (may need some extra modification to work) */ TEST(DISABLED_mongoContextProvidersUpdateRequest, patternFail) { HttpStatusCode ms; UpdateContextRequest req; UpdateContextResponse res; /* Prepare database */ utInit(); prepareDatabasePatternTrue(); /* Forge the request (from "inside" to "outside") */ /* Note that although it is a bit weird having an updateContext without attributes to update, * it is legal from the point of view of OMA spec */ ContextElement ce; ce.entityId.fill("R.*", "T", "true"); req.contextElementVector.push_back(&ce); req.updateActionType.set("UPDATE"); /* Invoke the function in mongoBackend library */ ms = mongoUpdateContext(&req, &res, "", servicePathVector, uriParams); /* Check response is as expected */ EXPECT_EQ(SccOk, ms); EXPECT_EQ(SccContextElementNotFound, res.errorCode.code); EXPECT_EQ("No context element found", res.errorCode.reasonPhrase); EXPECT_EQ(0, res.errorCode.details.size()); EXPECT_EQ(0,res.contextElementResponseVector.size()); /* Release connection */ mongoDisconnect(); utExit(); }
/* **************************************************************************** * * mapPostIndividualContextEntityAttributes - */ HttpStatusCode mapPostIndividualContextEntityAttributes(const std::string& entityId, AppendContextElementRequest* request, AppendContextElementResponse* response, ConnectionInfo* ciP) { HttpStatusCode ms; UpdateContextRequest ucRequest; UpdateContextResponse ucResponse; ContextElement ce; ce.entityId.fill(entityId, "", "false"); ce.attributeDomainName = request->attributeDomainName; ce.contextAttributeVector = request->contextAttributeVector; ucRequest.contextElementVector.push_back(&ce); ucRequest.updateActionType.set("Append"); ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV); ContextAttributeResponse* car = new ContextAttributeResponse(); ContextElementResponse* ucContextElementResponse = ucResponse.contextElementResponseVector.get(0); // Copying contextAttributeVector from ucContextElementResponse for (unsigned caIx = 0; caIx < ucContextElementResponse->contextElement.contextAttributeVector.size(); ++caIx) { LM_T(LmtClone, ("Copying ContextAttribute %d", caIx)); ContextAttribute* caP = new ContextAttribute(ucContextElementResponse->contextElement.contextAttributeVector.get(caIx)); car->contextAttributeVector.push_back(caP); } car->statusCode.fill(&ucContextElementResponse->statusCode); response->contextResponseVector.push_back(car); response->errorCode.fill(&ucResponse.contextElementResponseVector.get(0)->statusCode); return ms; }
/* **************************************************************************** * * noPatternMultiEntity - * * Discover: (E1, E2) - no attrs * Result: (E1, E2) - (A1, A2, A3) - http://cr1.com * E1 - (A1, A4) - http://cr2.com * E2 - (A2, A3) - http://cr3.com */ TEST(mongoContextProvidersUpdateRequest, noPatternMultiEntity) { HttpStatusCode ms; UpdateContextRequest req; UpdateContextResponse res; /* Prepare database */ utInit(); prepareDatabase(); /* Forge the request (from "inside" to "outside") */ /* Note that although it is a bit weird having an updateContext without attributes to update, * it is legal from the point of view of OMA spec */ ContextElement ce1, ce2; ce1.entityId.fill("E1", "T1", "false"); ce2.entityId.fill("E2", "T2", "false"); req.contextElementVector.push_back(&ce1); req.contextElementVector.push_back(&ce2); req.updateActionType.set("UPDATE"); /* Invoke the function in mongoBackend library */ ms = mongoUpdateContext(&req, &res, "", servicePathVector, uriParams); /* Check response is as expected */ EXPECT_EQ(SccOk, ms); EXPECT_EQ(SccNone, res.errorCode.code); EXPECT_EQ(0, res.errorCode.reasonPhrase.size()); EXPECT_EQ(0, res.errorCode.details.size()); ASSERT_EQ(2,res.contextElementResponseVector.size()); /* Context element response #1 */ EXPECT_EQ("E1", RES_CER(0).entityId.id); EXPECT_EQ("T1", RES_CER(0).entityId.type); EXPECT_EQ("false", RES_CER(0).entityId.isPattern); ASSERT_EQ(0, RES_CER(0).contextAttributeVector.size()); EXPECT_EQ(SccFound, RES_CER_STATUS(0).code); EXPECT_EQ("Found", RES_CER_STATUS(0).reasonPhrase); EXPECT_EQ("http://cr1.com", RES_CER_STATUS(0).details); /* Context element response #2 */ EXPECT_EQ("E2", RES_CER(1).entityId.id); EXPECT_EQ("T2", RES_CER(1).entityId.type); EXPECT_EQ("false", RES_CER(1).entityId.isPattern); ASSERT_EQ(0, RES_CER(1).contextAttributeVector.size()); EXPECT_EQ(SccFound, RES_CER_STATUS(1).code); EXPECT_EQ("Found", RES_CER_STATUS(1).reasonPhrase); EXPECT_EQ("http://cr1.com", RES_CER_STATUS(1).details); /* Release connection */ mongoDisconnect(); utExit(); }
/* **************************************************************************** * * noPatternMultiAttr - * * Discover: E1 - (A3, A4, A5) * Result: E1 - A3 - http://cr1.com * E1 - A4 - http://cr2.com */ TEST(mongoContextProvidersUpdateRequest, noPatternMultiAttr) { HttpStatusCode ms; UpdateContextRequest req; UpdateContextResponse res; /* Prepare database */ utInit(); prepareDatabase(); /* Forge the request (from "inside" to "outside") */ ContextElement ce; ce.entityId.fill("E1", "T1", "false"); ContextAttribute ca1("A3", "TA3", "new_val"); ContextAttribute ca2("A4", "TA4", "new_val"); ContextAttribute ca3("A5", "TA5", "new_val"); ce.contextAttributeVector.push_back(&ca1); ce.contextAttributeVector.push_back(&ca2); ce.contextAttributeVector.push_back(&ca3); req.contextElementVector.push_back(&ce); req.updateActionType.set("UPDATE"); /* Invoke the function in mongoBackend library */ ms = mongoUpdateContext(&req, &res, "", servicePathVector, uriParams); /* Check response is as expected */ EXPECT_EQ(SccOk, ms); EXPECT_EQ(SccNone, res.errorCode.code); EXPECT_EQ(0, res.errorCode.reasonPhrase.size()); EXPECT_EQ(0, res.errorCode.details.size()); ASSERT_EQ(1, res.contextElementResponseVector.size()); EXPECT_EQ("E1", RES_CER(0).entityId.id); EXPECT_EQ("T1", RES_CER(0).entityId.type); EXPECT_EQ("false", RES_CER(0).entityId.isPattern); ASSERT_EQ(3, RES_CER(0).contextAttributeVector.size()); EXPECT_EQ("A3", RES_CER_ATTR(0, 0)->name); EXPECT_EQ("TA3", RES_CER_ATTR(0, 0)->type); EXPECT_EQ("A4", RES_CER_ATTR(0, 1)->name); EXPECT_EQ("TA4", RES_CER_ATTR(0, 1)->type); EXPECT_EQ("A5", RES_CER_ATTR(0, 2)->name); EXPECT_EQ("TA5", RES_CER_ATTR(0, 2)->type); EXPECT_EQ(SccFound, RES_CER_STATUS(0).code); EXPECT_EQ("Found", RES_CER_STATUS(0).reasonPhrase); EXPECT_EQ("http://cr1.com", RES_CER_STATUS(0).details); /* Release connection */ mongoDisconnect(); utExit(); }
/* **************************************************************************** * * mapDeleteIndividualContextEntity - */ HttpStatusCode mapDeleteIndividualContextEntity(const std::string& entityId, StatusCode* response, ConnectionInfo* ciP) { HttpStatusCode ms; UpdateContextRequest ucRequest; UpdateContextResponse ucResponse; ContextElement ce; ce.entityId.fill(entityId, "", "false"); ucRequest.contextElementVector.push_back(&ce); ucRequest.updateActionType.set("Delete"); ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV); *response = ucResponse.contextElementResponseVector.get(0)->statusCode; return ms; }
/* **************************************************************************** * * mapDeleteIndividualContextEntityAttribute - */ HttpStatusCode mapDeleteIndividualContextEntityAttribute ( const std::string& entityId, const std::string& entityType, const std::string& attributeName, StatusCode* response, ConnectionInfo* ciP ) { HttpStatusCode ms; UpdateContextRequest ucRequest; UpdateContextResponse ucResponse; ContextElement ce; ContextAttribute contextAttribute(attributeName, "", ""); ce.entityId.fill(entityId, entityType, "false"); ce.contextAttributeVector.push_back(&contextAttribute); ucRequest.updateActionType.set("Delete"); ucRequest.contextElementVector.push_back(&ce); ucResponse.errorCode.code = SccOk; ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV, ciP->uriParam); // // Only one contextAttribute in request contextAttributeVector, so there will be only one // item in ucResponse.contextElementResponseVector. // Completely safe to respond with ucResponse.contextElementResponseVector[0]. // However, if there is a general error in ucResponse.errorCode, we should pick that // response instead. // if (ucResponse.errorCode.code != SccOk) { *response = ucResponse.errorCode; } else { *response = ucResponse.contextElementResponseVector[0]->statusCode; } return ms; }
/* **************************************************************************** * * mapPostIndividualContextEntityAttribute - */ HttpStatusCode mapPostIndividualContextEntityAttribute(const std::string& entityId, const std::string& attributeName, UpdateContextAttributeRequest* request, StatusCode* response, ConnectionInfo* ciP) { HttpStatusCode ms; UpdateContextRequest ucRequest; UpdateContextResponse ucResponse; ContextElement ce; ContextAttribute attribute(attributeName, "", ""); ce.entityId.fill(entityId, "", "false"); ce.contextAttributeVector.push_back(&attribute); ucRequest.contextElementVector.push_back(&ce); ucRequest.updateActionType.set("Append"); ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV); response->fill(SccOk); return ms; }
/* **************************************************************************** * * patternNAttr - * * Discover: E[1-2] - (A1, A2) * Result: (E1. E2) - (A1, A2) - http://cr1.com * E1 - A1 - http://cr2.com * E2 - A2 - http://cr3.com * * isPattern=true is not currently supported in updateContext, so this test it disabled: enable it once * this gets supported (may need some extra modification to work) */ TEST(DISABLED_mongoContextProvidersUpdateRequest, patternNAttr) { HttpStatusCode ms; UpdateContextRequest req; UpdateContextResponse res; /* Prepare database */ utInit(); prepareDatabasePatternTrue(); /* Forge the request (from "inside" to "outside") */ ContextElement ce; ce.entityId.fill("E[1-2]", "T", "true"); ContextAttribute ca1("A1", "TA1", "new_val"); ContextAttribute ca2("A2", "TA2", "new_val"); ce.contextAttributeVector.push_back(&ca1); ce.contextAttributeVector.push_back(&ca2); req.contextElementVector.push_back(&ce); req.updateActionType.set("UPDATE"); /* Invoke the function in mongoBackend library */ ms = mongoUpdateContext(&req, &res, "", servicePathVector, uriParams); /* Check response is as expected */ EXPECT_EQ(SccOk, ms); EXPECT_EQ(SccFound, res.errorCode.code); EXPECT_EQ("Found", res.errorCode.reasonPhrase); EXPECT_EQ("http://cr1.com", res.errorCode.details); ASSERT_EQ(0, res.contextElementResponseVector.size()); /* Release connection */ mongoDisconnect(); utExit(); }
/* **************************************************************************** * * postUpdateContext - * * POST /v1/updateContext * POST /ngsi10/updateContext * * Payload In: UpdateContextRequest * Payload Out: UpdateContextResponse */ std::string postUpdateContext ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { UpdateContextResponse* upcrsP = &parseDataP->upcrs.res; UpdateContextRequest* upcrP = &parseDataP->upcr.res; std::string answer; // // 01. Check service-path consistency // // If more than ONE service-path is input, an error is returned as response. // If NO service-path is issued, then the default service-path "/" is used. // After these checks, the service-path is checked to be 'correct'. // if (ciP->servicePathV.size() > 1) { upcrsP->errorCode.fill(SccBadRequest, "more than one service path in context update request"); LM_W(("Bad Input (more than one service path for an update request)")); answer = upcrsP->render(ciP, UpdateContext, ""); return answer; } else if (ciP->servicePathV.size() == 0) { ciP->servicePathV.push_back(DEFAULT_SERVICE_PATH); } std::string res = servicePathCheck(ciP->servicePathV[0].c_str()); if (res != "OK") { upcrsP->errorCode.fill(SccBadRequest, res); answer = upcrsP->render(ciP, UpdateContext, ""); return answer; } // // 02. Send the request to mongoBackend/mongoUpdateContext // upcrsP->errorCode.fill(SccOk); attributesToNotFound(upcrP); ciP->httpStatusCode = mongoUpdateContext(upcrP, upcrsP, ciP->tenant, ciP->servicePathV, ciP->uriParam, ciP->httpHeaders.xauthToken, "postUpdateContext"); foundAndNotFoundAttributeSeparation(upcrsP, upcrP, ciP); // // 03. Normal case - no forwards // // If there is nothing to forward, just return the result // bool forwarding = forwardsPending(upcrsP); if (forwarding == false) { answer = upcrsP->render(ciP, UpdateContext, ""); upcrP->release(); return answer; } // // 04. mongoBackend doesn't give us the values of the attributes. // So, here we have to do a search inside the initial UpdateContextRequest to fill in all the // attribute-values in the output from mongoUpdateContext // for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx) { ContextElement* ceP = &upcrsP->contextElementResponseVector[cerIx]->contextElement; for (unsigned int aIx = 0; aIx < ceP->contextAttributeVector.size(); ++aIx) { ContextAttribute* aP = upcrP->attributeLookup(&ceP->entityId, ceP->contextAttributeVector[aIx]->name); if (aP == NULL) { LM_E(("Internal Error (attribute '%s' not found)", ceP->contextAttributeVector[aIx]->name.c_str())); } else { ceP->contextAttributeVector[aIx]->value = aP->value; ceP->contextAttributeVector[aIx]->type = aP->type; } } } // // 05. Forwards necessary - sort parts in outgoing requestV // requestV is a vector of UpdateContextRequests and each Context Provider // will have a slot in the vector. // When a ContextElementResponse is found in the output from mongoUpdateContext, a // UpdateContextRequest is to be found/created and inside that UpdateContextRequest // a ContextElement for the Entity of the ContextElementResponse. // // Non-found parts go directly to 'response'. // UpdateContextRequestVector requestV; UpdateContextResponse response; response.errorCode.fill(SccOk); for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx) { ContextElementResponse* cerP = upcrsP->contextElementResponseVector[cerIx]; if (cerP->contextElement.contextAttributeVector.size() == 0) { // // If we find a contextElement without attributes here, then something is wrong // LM_E(("Orion Bug (empty contextAttributeVector for ContextElementResponse %d)", cerIx)); } else { for (unsigned int aIx = 0; aIx < cerP->contextElement.contextAttributeVector.size(); ++aIx) { ContextAttribute* aP = cerP->contextElement.contextAttributeVector[aIx]; // // 0. If the attribute is 'not-found' - just add the attribute to the outgoing response // if (aP->found == false) { response.notFoundPush(&cerP->contextElement.entityId, new ContextAttribute(aP), NULL); continue; } // // 1. If the attribute is found locally - just add the attribute to the outgoing response // if (aP->providingApplication.get() == "") { response.foundPush(&cerP->contextElement.entityId, new ContextAttribute(aP)); continue; } // // 2. Lookup UpdateContextRequest in requestV according to providingApplication. // If not found, add one. UpdateContextRequest* reqP = requestV.lookup(aP->providingApplication.get()); if (reqP == NULL) { reqP = new UpdateContextRequest(aP->providingApplication.get(), &cerP->contextElement.entityId); reqP->updateActionType.set("UPDATE"); requestV.push_back(reqP); } // // 3. Increase the correct format counter // if (aP->providingApplication.getFormat() == XML) { reqP->xmls++; } else { reqP->jsons++; } // // 3. Lookup ContextElement in UpdateContextRequest according to EntityId. // If not found, add one (to the ContextElementVector of the UpdateContextRequest). // ContextElement* ceP = reqP->contextElementVector.lookup(&cerP->contextElement.entityId); if (ceP == NULL) { ceP = new ContextElement(&cerP->contextElement.entityId); reqP->contextElementVector.push_back(ceP); } // // 4. Add ContextAttribute to the correct ContextElement in the correct UpdateContextRequest // ceP->contextAttributeVector.push_back(new ContextAttribute(aP)); } } } // // Now we are ready to forward the Updates // // // Calling each of the Context Providers, merging their results into the // total response 'response' // for (unsigned int ix = 0; ix < requestV.size(); ++ix) { if (requestV[ix]->contextProvider == "") { LM_E(("Internal Error (empty context provider string)")); continue; } UpdateContextResponse upcrs; Format format = requestV[ix]->format(); updateForward(ciP, requestV[ix], &upcrs, format); // // Add the result from the forwarded update to the total response in 'response' // response.merge(&upcrs); } answer = response.render(ciP, UpdateContext, ""); // // Cleanup // upcrP->release(); requestV.release(); upcrsP->release(); upcrsP->fill(&response); response.release(); return answer; }
/* **************************************************************************** * * postAttributeValueInstanceWithTypeAndId - * * POST /ngsi10/contextEntities/type/{type}/id/{id}/attributes/{attributeName}/{valueID} * * Payload: UpdateContextAttributeRequest */ std::string postAttributeValueInstanceWithTypeAndId ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { UpdateContextRequest request; UpdateContextResponse response; std::string entityType = compV[3]; std::string entityId = compV[5]; std::string attributeName = compV[7]; std::string valueId = compV[8]; UpdateContextAttributeRequest* upcarP = &parseDataP->upcar.res; bool idFound = false; // // Any metadata ID in the payload? // // If so, the value must be equal to the {valueID} of the URL // for (unsigned int ix = 0; ix < upcarP->metadataVector.size(); ++ix) { Metadata* mP = upcarP->metadataVector.get(ix); if (mP->name == "ID") { if (mP->value != valueId) { std::string out; out = restErrorReplyGet(ciP, ciP->outFormat, "", "StatusCode", SccBadRequest, std::string("unmatching metadata ID value URI/payload: '") + valueId + "' vs '" + mP->value + "'"); return out; } else { idFound = true; } } } ContextAttribute* attributeP = new ContextAttribute(attributeName, "", upcarP->contextValue); ContextElement* ceP = new ContextElement(); // Copy the metadata vector of the input payload attributeP->metadataVector.fill((MetadataVector*) &upcarP->metadataVector); // If no "ID" metadata was in the payload, add it if (idFound == false) { Metadata* mP = new Metadata("ID", "", valueId); attributeP->metadataVector.push_back(mP); } // Filling the rest of the structure for mongoUpdateContext ceP->entityId.fill(entityId, entityType, "false"); ceP->attributeDomainName.set(""); ceP->contextAttributeVector.push_back(attributeP); request.contextElementVector.push_back(ceP); request.updateActionType.set("APPEND"); response.errorCode.code = SccNone; ciP->httpStatusCode = mongoUpdateContext(&request, &response, ciP->tenant, ciP->servicePathV, ciP->uriParam); StatusCode statusCode; if (response.contextElementResponseVector.size() == 0) { statusCode.fill(SccContextElementNotFound, std::string("Entity-Attribute pair: '") + entityId + "-" + attributeName + "'"); } else if (response.contextElementResponseVector.size() == 1) { ContextElementResponse* cerP = response.contextElementResponseVector.get(0); if (response.errorCode.code != SccNone) { statusCode.fill(&response.errorCode); } else if (cerP->statusCode.code != SccNone) { statusCode.fill(&cerP->statusCode); } else { statusCode.fill(SccOk); } } else { statusCode.fill(SccReceiverInternalError, "More than one response from postAttributeValueInstanceWithTypeAndId::mongoUpdateContext"); } request.release(); return statusCode.render(ciP->outFormat, "", false, false); }
/* **************************************************************************** * * postUpdateContext - * * POST /v1/updateContext * POST /ngsi10/updateContext * * Payload In: UpdateContextRequest * Payload Out: UpdateContextResponse */ std::string postUpdateContext ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP, Ngsiv2Flavour ngsiV2Flavour ) { UpdateContextResponse* upcrsP = &parseDataP->upcrs.res; UpdateContextRequest* upcrP = &parseDataP->upcr.res; std::string answer; bool asJsonObject = (ciP->uriParam[URI_PARAM_ATTRIBUTE_FORMAT] == "object" && ciP->outMimeType == JSON); bool forcedUpdate = ciP->uriParamOptions[OPT_FORCEDUPDATE]; // // 01. Check service-path consistency // // If more than ONE service-path is input, an error is returned as response. // If ONE service-path is issued and that service path is "", then the default service-path is used. // Note that by construction servicePath cannot have 0 elements // After these checks, the service-path is checked to be 'correct'. // if (ciP->servicePathV.size() > 1) { upcrsP->errorCode.fill(SccBadRequest, "more than one service path in context update request"); alarmMgr.badInput(clientIp, "more than one service path for an update request"); TIMED_RENDER(answer = upcrsP->toJsonV1(asJsonObject)); upcrP->release(); return answer; } else if (ciP->servicePathV[0] == "") { ciP->servicePathV[0] = SERVICE_PATH_ROOT; } std::string res = servicePathCheck(ciP->servicePathV[0].c_str()); if (res != "OK") { upcrsP->errorCode.fill(SccBadRequest, res); TIMED_RENDER(answer = upcrsP->toJsonV1(asJsonObject)); upcrP->release(); return answer; } // // 02. Send the request to mongoBackend/mongoUpdateContext // upcrsP->errorCode.fill(SccOk); attributesToNotFound(upcrP); HttpStatusCode httpStatusCode; TIMED_MONGO(httpStatusCode = mongoUpdateContext(upcrP, upcrsP, ciP->tenant, ciP->servicePathV, ciP->uriParam, ciP->httpHeaders.xauthToken, ciP->httpHeaders.correlator, ciP->httpHeaders.ngsiv2AttrsFormat, forcedUpdate, ciP->apiVersion, ngsiV2Flavour)); if (ciP->httpStatusCode != SccCreated) { ciP->httpStatusCode = httpStatusCode; } foundAndNotFoundAttributeSeparation(upcrsP, upcrP, ciP); // // 03. Normal case - no forwards // // If there is nothing to forward, just return the result // bool forwarding = forwardsPending(upcrsP); LM_T(LmtForward, ("forwardsPending returned %s", FT(forwarding))); if (forwarding == false) { TIMED_RENDER(answer = upcrsP->toJsonV1(asJsonObject)); upcrP->release(); return answer; } // // 04. mongoBackend doesn't give us the values of the attributes. // So, here we have to do a search inside the initial UpdateContextRequest to fill in all the // attribute-values in the output from mongoUpdateContext // for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx) { Entity* eP = &upcrsP->contextElementResponseVector[cerIx]->entity; for (unsigned int aIx = 0; aIx < eP->attributeVector.size(); ++aIx) { ContextAttribute* aP = upcrP->attributeLookup(eP, eP->attributeVector[aIx]->name); if (aP == NULL) { LM_E(("Internal Error (attribute '%s' not found)", eP->attributeVector[aIx]->name.c_str())); } else { eP->attributeVector[aIx]->stringValue = aP->stringValue; eP->attributeVector[aIx]->numberValue = aP->numberValue; eP->attributeVector[aIx]->boolValue = aP->boolValue; eP->attributeVector[aIx]->valueType = aP->valueType; eP->attributeVector[aIx]->compoundValueP = aP->compoundValueP == NULL ? NULL : aP->compoundValueP->clone(); } } } // // 05. Forwards necessary - sort parts in outgoing requestV // requestV is a vector of UpdateContextRequests and each Context Provider // will have a slot in the vector. // When a ContextElementResponse is found in the output from mongoUpdateContext, a // UpdateContextRequest is to be found/created and inside that UpdateContextRequest // a ContextElement for the Entity of the ContextElementResponse. // // Non-found parts go directly to 'response'. // UpdateContextRequestVector requestV; UpdateContextResponse response; response.errorCode.fill(SccOk); for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx) { ContextElementResponse* cerP = upcrsP->contextElementResponseVector[cerIx]; if (cerP->entity.attributeVector.size() == 0) { // // If we find a contextElement without attributes here, then something is wrong // LM_E(("Orion Bug (empty contextAttributeVector for ContextElementResponse %d)", cerIx)); } else { for (unsigned int aIx = 0; aIx < cerP->entity.attributeVector.size(); ++aIx) { ContextAttribute* aP = cerP->entity.attributeVector[aIx]; // // 0. If the attribute is 'not-found' - just add the attribute to the outgoing response // if (aP->found == false) { ContextAttribute ca(aP); response.notFoundPush(&cerP->entity, &ca, NULL); continue; } // // 1. If the attribute is found locally - just add the attribute to the outgoing response // if (aP->providingApplication.get() == "") { ContextAttribute ca(aP); response.foundPush(&cerP->entity, &ca); continue; } // // 2. Lookup UpdateContextRequest in requestV according to providingApplication. // If not found, add one. UpdateContextRequest* reqP = requestV.lookup(aP->providingApplication.get()); if (reqP == NULL) { reqP = new UpdateContextRequest(aP->providingApplication.get(), aP->providingApplication.providerFormat, &cerP->entity); reqP->updateActionType = ActionTypeUpdate; requestV.push_back(reqP); } // // 3. Lookup ContextElement in UpdateContextRequest according to EntityId. // If not found, add one (to the EntityVector of the UpdateContextRequest). // Entity* eP = reqP->entityVector.lookup(cerP->entity.id, cerP->entity.type); if (eP == NULL) { eP = new Entity(); eP->fill(cerP->entity.id, cerP->entity.type, cerP->entity.isPattern); reqP->entityVector.push_back(eP); } // // 4. Add ContextAttribute to the correct ContextElement in the correct UpdateContextRequest // eP->attributeVector.push_back(new ContextAttribute(aP)); } } } // // Now we are ready to forward the Updates // // // Calling each of the Context Providers, merging their results into the // total response 'response' // bool forwardOk = true; for (unsigned int ix = 0; ix < requestV.size() && ix < cprForwardLimit; ++ix) { if (requestV[ix]->contextProvider == "") { LM_E(("Internal Error (empty context provider string)")); continue; } UpdateContextResponse upcrs; bool b; b = updateForward(ciP, requestV[ix], &upcrs); if (b == false) { forwardOk = false; } // // Add the result from the forwarded update to the total response in 'response' // response.merge(&upcrs); } // // Note this is a slight break in the separation of concerns among the different layers (i.e. // serviceRoutine/ logic should work in a "NGSIv1 isolated context"). However, it seems to be // a smart way of dealing with partial update situations // if (ciP->apiVersion == V2) { LM_T(LmtForward, ("ciP->apiVersion == V2")); // // Adjust OrionError response in the case of partial updates. This may happen in CPr forwarding // scenarios. Note that mongoBackend logic "splits" successfull updates and failing updates in // two different CER (maybe using the same entity) // std::string failing = ""; unsigned int failures = 0; LM_T(LmtForward, ("Going over a contextElementResponseVector of %d items", response.contextElementResponseVector.size())); for (unsigned int ix = 0; ix < response.contextElementResponseVector.size(); ++ix) { ContextElementResponse* cerP = response.contextElementResponseVector[ix]; if (cerP->statusCode.code != SccOk) { failures++; std::string failingPerCer = ""; for (unsigned int jx = 0; jx < cerP->entity.attributeVector.size(); ++jx) { failingPerCer += cerP->entity.attributeVector[jx]->name; if (jx != cerP->entity.attributeVector.size() - 1) { failingPerCer +=", "; } } failing += cerP->entity.id + "-" + cerP->entity.type + " : [" + failingPerCer + "], "; } } // // Note that we modify parseDataP->upcrs.res.oe and not response.oe, as the former is the // one used by the calling postBatchUpdate() function at serviceRoutineV2 library // if ((forwardOk == true) && (failures == 0)) { parseDataP->upcrs.res.oe.fill(SccNone, ""); } else if (failures == response.contextElementResponseVector.size()) { parseDataP->upcrs.res.oe.fill(SccContextElementNotFound, ERROR_DESC_NOT_FOUND_ENTITY, ERROR_NOT_FOUND); } else if (failures > 0) { // Removing trailing ", " failing = failing.substr(0, failing.size() - 2); // If some CER (but not all) fail, then it is a partial update parseDataP->upcrs.res.oe.fill(SccContextElementNotFound, "Attributes that were not updated: { " + failing + " }", "PartialUpdate"); } else // failures == 0 { // No failure, so invalidate any possible OrionError filled by mongoBackend on the mongoUpdateContext step parseDataP->upcrs.res.oe.fill(SccNone, ""); } } else // v1 { LM_T(LmtForward, ("ciP->apiVersion != V2")); // Note that v2 case doesn't use an actual response (so no need to waste time rendering it). // We render in the v1 case only TIMED_RENDER(answer = response.toJsonV1(asJsonObject)); } // // Cleanup // upcrP->release(); requestV.release(); upcrsP->release(); upcrsP->fill(&response); response.release(); return answer; }
/* **************************************************************************** * * deleteAttributeValueInstanceWithTypeAndId - * * DELETE /ngsi10/contextEntities/type/{type}/id/{id}/attributes/{attributeName}/{valueID} */ std::string deleteAttributeValueInstanceWithTypeAndId ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { UpdateContextRequest request; UpdateContextResponse response; std::string entityType = compV[3]; std::string entityId = compV[5]; std::string attributeName = compV[7]; std::string valueId = compV[8]; ContextAttribute* attributeP = new ContextAttribute(attributeName, "", "false"); Metadata* mP = new Metadata("ID", "", valueId); ContextElement* ceP = new ContextElement(); attributeP->metadataVector.push_back(mP); ceP->entityId.fill(entityId, entityType, "false"); ceP->attributeDomainName.set(""); ceP->contextAttributeVector.push_back(attributeP); request.contextElementVector.push_back(ceP); request.updateActionType.set("DELETE"); response.errorCode.code = SccNone; ciP->httpStatusCode = mongoUpdateContext(&request, &response, ciP->tenant, ciP->servicePathV, ciP->uriParam); StatusCode statusCode; if (response.contextElementResponseVector.size() == 0) { statusCode.fill(SccContextElementNotFound, std::string("Entity-Attribute pair: '") + entityId + "-" + attributeName + "'"); } else if (response.contextElementResponseVector.size() == 1) { ContextElementResponse* cerP = response.contextElementResponseVector.get(0); if (response.errorCode.code != SccNone) { statusCode.fill(&response.errorCode); } else if (cerP->statusCode.code != SccNone) { statusCode.fill(&cerP->statusCode); } else { statusCode.fill(SccOk); } } else { statusCode.fill(SccReceiverInternalError, "More than one response from deleteAttributeValueInstanceWithTypeAndId::mongoUpdateContext"); } request.release(); return statusCode.render(ciP->outFormat, "", false, false); }