/* **************************************************************************** * * getSubscription - * * GET /v2/subscription/<id> * */ std::string getSubscription ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { ngsiv2::Subscription sub; std::string idSub = compV[2]; OrionError oe; std::string out; std::string err; if ((err = idCheck(idSub)) != "OK") { OrionError oe(SccBadRequest, err); return oe.render(ciP, "Invalid subscription ID"); } TIMED_MONGO(mongoGetSubscription(&sub, &oe, idSub, ciP->uriParam, ciP->tenant)); if (oe.code != SccOk) { TIMED_RENDER(out = oe.render(ciP, "Invalid subscription ID")); return out; } TIMED_RENDER(out = sub.toJson()); return out; }
/* **************************************************************************** * * deleteSubscription - * * DELETE /v2/subscriptions/{entityId} * * Payload In: None * Payload Out: None * * URI parameters: * - */ std::string deleteSubscription ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string subscriptionId = compV[2]; UnsubscribeContextResponse uncr; // 'Fill In' UnsubscribeContextRequest parseDataP->uncr.res.subscriptionId.set(subscriptionId); TIMED_MONGO(mongoUnsubscribeContext(&parseDataP->uncr.res, &uncr, ciP->tenant)); // Check for potential error std::string answer = ""; if (uncr.oe.code != SccNone ) { TIMED_RENDER(answer = uncr.oe.toJson()); ciP->httpStatusCode = uncr.oe.code; } else { ciP->httpStatusCode = SccNoContent; } return answer; }
/* **************************************************************************** * * patchSubscription - * * PATCH /v2/subscriptions/{entityId} * * Payload In: None * Payload Out: None * * URI parameters: * - */ std::string patchSubscription ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string answer; std::string subscriptionId = compV[2]; UpdateContextSubscriptionResponse ucsr; // 'Fill In' UpdateContextSubscriptionRequest parseDataP->ucsr.res.subscriptionId.set(subscriptionId); TIMED_MONGO(ciP->httpStatusCode = mongoUpdateContextSubscription(&parseDataP->ucsr.res, &ucsr, ciP->outFormat, ciP->tenant, ciP->httpHeaders.xauthToken, ciP->servicePathV, ciP->apiVersion)); if (ciP->httpStatusCode != SccOk) { OrionError oe(ciP->httpStatusCode); TIMED_RENDER(answer = oe.render(ciP, "")); return answer; } else if (ucsr.subscribeError.errorCode.code != SccNone) { OrionError oe(ucsr.subscribeError.errorCode.code); ciP->httpStatusCode = ucsr.subscribeError.errorCode.code; oe.reasonPhrase = ucsr.subscribeError.errorCode.reasonPhrase; if (ucsr.subscribeError.errorCode.code == SccContextElementNotFound) { oe.details = "The requested subscription has not been found. Check id"; } TIMED_RENDER(answer = oe.render(ciP, "")); return answer; } ciP->httpStatusCode = SccNoContent; return ""; }
/* **************************************************************************** * * postNotifyContext - */ std::string postNotifyContext ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { NotifyContextResponse ncr; std::string answer; TIMED_MONGO(ciP->httpStatusCode = mongoNotifyContext(&parseDataP->ncr.res, &ncr, ciP->tenant, ciP->httpHeaders.xauthToken, ciP->servicePathV)); TIMED_RENDER(answer = ncr.render(NotifyContext, ciP->outFormat, "")); return answer; }
/* **************************************************************************** * * postUnsubscribeContextAvailability - */ std::string postUnsubscribeContextAvailability ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { UnsubscribeContextAvailabilityResponse ucar; std::string answer; TIMED_MONGO(ciP->httpStatusCode = mongoUnsubscribeContextAvailability(&parseDataP->ucar.res, &ucar, ciP->tenant)); TIMED_RENDER(answer = ucar.toJsonV1()); return answer; }
/* **************************************************************************** * * postUpdateContextAvailabilitySubscription - */ std::string postUpdateContextAvailabilitySubscription ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { UpdateContextAvailabilitySubscriptionResponse ucas; std::string answer; ucas.subscriptionId = parseDataP->ucas.res.subscriptionId; TIMED_MONGO(ciP->httpStatusCode = mongoUpdateContextAvailabilitySubscription(&parseDataP->ucas.res, &ucas, ciP->httpHeaders.correlator, ciP->tenant)); TIMED_RENDER(answer = ucas.render(UpdateContextAvailabilitySubscription, "", 0)); return answer; }
/* **************************************************************************** * * postUpdateContextSubscription - */ std::string postUpdateContextSubscription ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { UpdateContextSubscriptionResponse ucsr; std::string answer; ucsr.subscribeError.subscriptionId = parseDataP->ucsr.res.subscriptionId; TIMED_MONGO(ciP->httpStatusCode = mongoUpdateContextSubscription(&parseDataP->ucsr.res, &ucsr, ciP->tenant, ciP->httpHeaders.xauthToken, ciP->servicePathV)); TIMED_RENDER(answer = ucsr.render(UpdateContextSubscription, "")); return answer; }
/* **************************************************************************** * * getEntityType - * * GET /v2/types/<entityType> * * Payload In: None * Payload Out: EntityTypeResponse * * URI parameters: * - options=noAttrDetail * */ std::string getEntityType ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { EntityTypeResponse response; std::string entityTypeName = compV[2]; std::string answer; bool noAttrDetail = ciP->uriParamOptions[OPT_NO_ATTR_DETAIL]; if (entityTypeName == "") { OrionError oe(SccBadRequest, EMPTY_ENTITY_TYPE, "BadRequest"); ciP->httpStatusCode = oe.code; return oe.toJson(); } TIMED_MONGO(mongoAttributesForEntityType(entityTypeName, &response, ciP->tenant, ciP->servicePathV, ciP->uriParam, noAttrDetail, ciP->apiVersion)); if (response.entityType.count == 0) { OrionError oe(SccContextElementNotFound, "Entity type not found", "NotFound"); TIMED_RENDER(answer = oe.toJson()); ciP->httpStatusCode = oe.code; } else { TIMED_RENDER(answer = response.toJson(ciP)); } response.release(); return answer; }
/* **************************************************************************** * * postSubscribeContext - * * POST /v1/subscribeContext * POST /ngsi10/subscribeContext * * Payload In: SubscribeContextRequest * Payload Out: SubscribeContextResponse * * URI parameters * - notifyFormat=XXX (used by mongoBackend) */ std::string postSubscribeContext ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { SubscribeContextResponse scr; std::string answer; // // FIXME P0: Only *one* service path is allowed for subscriptions. // Personally (kz) I kind of like that. If you want additional service-paths, just add another subscription! // However, we need to at least state that HERE is where we limit the number of service paths to *one*. // if (ciP->servicePathV.size() > 1) { char noOfV[STRING_SIZE_FOR_INT]; snprintf(noOfV, sizeof(noOfV), "%lu", ciP->servicePathV.size()); std::string details = std::string("max *one* service-path allowed for subscriptions (") + noOfV + " given"; alarmMgr.badInput(clientIp, details); scr.subscribeError.errorCode.fill(SccBadRequest, "max one service-path allowed for subscriptions"); TIMED_RENDER(answer = scr.render(SubscribeContext, "")); return answer; } TIMED_MONGO(ciP->httpStatusCode = mongoSubscribeContext(&parseDataP->scr.res, &scr, ciP->tenant, ciP->uriParam, ciP->httpHeaders.xauthToken, ciP->servicePathV)); TIMED_RENDER(answer = scr.render(SubscribeContext, "")); parseDataP->scr.res.release(); return answer; }
/* **************************************************************************** * * 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; }