/* **************************************************************************** * * 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; }
/* **************************************************************************** * * 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 ""; }
/* **************************************************************************** * * leakTreat - */ std::string leakTreat ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string password = "******"; std::string out; if (harakiri == false) { OrionError orionError(SccBadRequest, "no such service"); ciP->httpStatusCode = SccOk; TIMED_RENDER(out = orionError.render()); return out; } if (components > 1) { password = compV[1]; } if (components == 1) { OrionError orionError(SccBadRequest, "Password requested"); ciP->httpStatusCode = SccOk; TIMED_RENDER(out = orionError.render()); } else if (password != "harakiri") { OrionError orionError(SccBadRequest, "Request denied - password erroneous"); ciP->httpStatusCode = SccOk; TIMED_RENDER(out = orionError.render()); } else { // No Cleanup for valgrind, and just in case another malloc std::string pwd = strdup("Leak test done"); OrionError orionError(SccOk, "Leak test: " + pwd); TIMED_RENDER(out = orionError.render()); } 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; }
/* **************************************************************************** * * deleteIndividualContextEntityAttribute - * * DELETE /v1/contextEntities/{entityId::id}/attributes/{attributeName} * DELETE /ngsi10/contextEntities/{entityId::id}/attributes/{attributeName} * * Payload In: None * Payload Out: StatusCode * * URI parameters: * - entity::type=TYPE * - note that '!exist=entity::type' and 'exist=entity::type' are not supported by convenience operations * that use the standard operation UpdateContext as there is no restriction within UpdateContext. * [ attributesFormat=object: makes no sense for this operation as StatusCode is returned ] * * 0. Take care of URI params * 1. Fill in UpdateContextRequest from URL-path components * 2. Call postUpdateContext standard service routine * 3. Translate UpdateContextResponse to StatusCode * 4. Cleanup and return result */ std::string deleteIndividualContextEntityAttribute ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string answer; std::string entityId = compV[2]; std::string entityType = ciP->uriParam[URI_PARAM_ENTITY_TYPE]; std::string attributeName = compV[4]; StatusCode response; // 1. Fill in UpdateContextRequest from URL-path components parseDataP->upcr.res.fill(entityId, entityType, "false", attributeName, "", "DELETE"); // 2. Call postUpdateContext standard service routine postUpdateContext(ciP, components, compV, parseDataP); // 3. Translate UpdateContextResponse to StatusCode response.fill(parseDataP->upcrs.res); // 4. Cleanup and return result TIMED_RENDER(answer = response.render("", false, false)); response.release(); parseDataP->upcr.res.release(); return answer; }
/* **************************************************************************** * * putEntityAttributeValue - * * PUT /v2/entities/<id>/attrs/<attrName>/value * * Payload In: AttributeValue * Payload Out: None * * * 01. Fill in UpdateContextRequest with data from URI and payload * 02. Call standard op postUpdateContext * 03. Check output from mongoBackend - any errors? * 04. Prepare HTTP headers * 05. Cleanup and return result */ std::string putEntityAttributeValue ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string entityId = compV[2]; std::string attributeName = compV[4]; std::string type = ciP->uriParam["type"]; if (forbiddenIdChars(ciP->apiVersion, entityId.c_str(), NULL) || forbiddenIdChars(ciP->apiVersion, attributeName.c_str(), NULL)) { OrionError oe(SccBadRequest, ERROR_DESC_BAD_REQUEST_INVALID_CHAR_URI, ERROR_BAD_REQUEST); ciP->httpStatusCode = oe.code; return oe.toJson(); } // 01. Fill in UpdateContextRequest with data from URI and payload parseDataP->av.attribute.name = attributeName; parseDataP->av.attribute.type = ""; // Overwrite 'none', as no type can be given in 'value' payload parseDataP->av.attribute.onlyValue = true; std::string err = parseDataP->av.attribute.check(ciP->apiVersion, ciP->requestType); if (err != "OK") { OrionError oe(SccBadRequest, err, "BadRequest"); ciP->httpStatusCode = oe.code; return oe.toJson(); } parseDataP->upcr.res.fill(entityId, &parseDataP->av.attribute, ActionTypeUpdate, type); // 02. Call standard op postUpdateContext postUpdateContext(ciP, components, compV, parseDataP); // 03. Check output from mongoBackend std::string answer = ""; if (parseDataP->upcrs.res.oe.code != SccNone) { TIMED_RENDER(answer = parseDataP->upcrs.res.oe.toJson()); ciP->httpStatusCode = parseDataP->upcrs.res.oe.code; } else { ciP->httpStatusCode = SccNoContent; } // 05. Cleanup and return result parseDataP->upcr.res.release(); return answer; }
/* **************************************************************************** * * putEntityAttribute - * * PUT /v2/entities/<id>/attrs/<attrName> * * Payload In: None * Payload Out: Entity Attribute * * * 01. Fill in UpdateContextRequest * 02. Call standard op postQueryContext * 03. Check output from mongoBackend - any errors? * 04. Prepare HTTP headers * 05. Cleanup and return result */ std::string putEntityAttribute ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string answer; std::string entityId = compV[2]; std::string attributeName = compV[4]; // 01. Fill in UpdateContextRequest from URL and payload parseDataP->attr.attribute.name = attributeName; parseDataP->upcr.res.fill(entityId, &parseDataP->attr.attribute, "UPDATE"); // 02. Call standard op postUpdateContext postUpdateContext(ciP, components, compV, parseDataP); // 03. Check output from mongoBackend - any errors? if (parseDataP->upcrs.res.contextElementResponseVector.size() == 1) { if (parseDataP->upcrs.res.contextElementResponseVector[0]->statusCode.code != SccOk) { ciP->httpStatusCode = parseDataP->upcrs.res.contextElementResponseVector[0]->statusCode.code; } } // 04. Prepare HTTP headers if ((ciP->httpStatusCode == SccOk) || (ciP->httpStatusCode == SccNone)) { ciP->httpStatusCode = SccNoContent; } // 05. Cleanup and return result parseDataP->upcr.res.release(); parseDataP->upcrs.res.release(); if (ciP->httpStatusCode == SccInvalidModification) { std::string details = "Request payload is missing some piece of information. Please, check Orion documentation."; OrionError orionError(SccInvalidModification, details); TIMED_RENDER(answer = orionError.render(ciP, "")); } return answer; }
/* **************************************************************************** * * getEntityAttribute - * * GET /v2/entities/:id:/attrs/:attrName: * * Payload In: None * Payload Out: Entity Attribute * * * 01. Fill in QueryContextRequest * 02. Call standard op postQueryContext * 03. Render Entity Attribute response * 04. Cleanup and return result */ std::string getEntityAttribute ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string type = ciP->uriParam["type"]; std::string answer; Attribute attribute; if (forbiddenIdChars(ciP->apiVersion, compV[2].c_str() , NULL) || (forbiddenIdChars(ciP->apiVersion, compV[4].c_str() , NULL))) { OrionError oe(SccBadRequest, INVAL_CHAR_URI, "BadRequest"); ciP->httpStatusCode = oe.code; return oe.toJson(); } // 01. Fill in QueryContextRequest parseDataP->qcr.res.fill(compV[2], type, "false", EntityTypeEmptyOrNotEmpty, ""); parseDataP->qcr.res.metadataList.fill(ciP->uriParam[URI_PARAM_METADATA]); // 02. Call standard op postQueryContext postQueryContext(ciP, components, compV, parseDataP); // 03. Render entity attribute response attribute.fill(&parseDataP->qcrs.res, compV[4]); TIMED_RENDER(answer = attribute.render(ciP, EntityAttributeResponse)); if (attribute.oe.reasonPhrase == "TooManyResults") { ciP->httpStatusCode = SccConflict; } else if (attribute.oe.reasonPhrase == "NotFound") { ciP->httpStatusCode = SccContextElementNotFound; // Attribute to be precise! } else { // the same of the wrapped operation ciP->httpStatusCode = parseDataP->qcrs.res.errorCode.code; } // 04. Cleanup and return result parseDataP->qcr.res.release(); 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; }
/* **************************************************************************** * * 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; }
/* **************************************************************************** * * putEntity - * * PUT /v2/entities * * Payload In: Entity * Payload Out: None * * URI parameters: * - * * 01. Fill in UpdateContextRequest * 02. Call standard op putUpdateContext * 03. Check output from mongoBackend - any errors? * 04. Prepare HTTP headers * 05. Cleanup and return result */ std::string putEntity ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { Entity* eP = &parseDataP->ent.res; eP->id = compV[2]; eP->type = ciP->uriParam["type"]; if (forbiddenIdChars(ciP->apiVersion, compV[2].c_str() , NULL)) { OrionError oe(SccBadRequest, ERROR_DESC_BAD_REQUEST_INVALID_CHAR_URI, ERROR_BAD_REQUEST); ciP->httpStatusCode = oe.code; return oe.toJson(); } // 01. Fill in UpdateContextRequest parseDataP->upcr.res.fill(eP, ActionTypeReplace); // 02. Call standard op postUpdateContext postUpdateContext(ciP, components, compV, parseDataP); // 03. Check error std::string answer = ""; if (parseDataP->upcrs.res.oe.code != SccNone ) { TIMED_RENDER(answer = parseDataP->upcrs.res.oe.toJson()); ciP->httpStatusCode = parseDataP->upcrs.res.oe.code; } else { ciP->httpStatusCode = SccNoContent; } // 04. Cleanup and return result eP->release(); return answer; }
/* **************************************************************************** * * postBatchQuery - * * POST /v2/op/query * * Payload In: BatchQueryRequest * Payload Out: Entities * * URI parameters: * - limit=NUMBER * - offset=NUMBER * - options=count,keyValues */ std::string postBatchQuery ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { BatchQuery* bqP = &parseDataP->bq.res; QueryContextRequest* qcrP = &parseDataP->qcr.res; Entities entities; std::string answer; qcrP->fill(bqP); bqP->release(); // qcrP just 'took over' the data from bqP, bqP no longer needed answer = postQueryContext(ciP, components, compV, parseDataP); if (ciP->httpStatusCode != SccOk) { parseDataP->qcr.res.release(); return answer; } // 03. Render Entities response if (parseDataP->qcrs.res.contextElementResponseVector.size() == 0) { ciP->httpStatusCode = SccOk; answer = "[]"; } else { entities.fill(&parseDataP->qcrs.res); TIMED_RENDER(answer = entities.render(ciP, EntitiesResponse)); } // 04. Cleanup and return result entities.release(); parseDataP->qcr.res.release(); return answer; }
/* **************************************************************************** * * putIndividualContextEntity - * * Corresponding Standard Operation: UpdateContext/UPDATE * * PUT /v1/contextEntities/{entityId::id} * PUT /ngsi10/contextEntities/{entityId::id} * * Payload In: UpdateContextElementRequest * Payload Out: UpdateContextElementResponse * * URI parameters: * - attributesFormat=object * - entity::type=TYPE * - note that '!exist=entity::type' and 'exist=entity::type' are not supported by convenience operations * that use the standard operation UpdateContext as there is no restriction within UpdateContext. * * 01. Take care of URI params * 02. Fill in UpdateContextRequest from UpdateContextElementRequest * 03. Call postUpdateContext standard service routine * 04. Translate UpdateContextResponse to UpdateContextElementResponse * 05. Cleanup and return result */ std::string putIndividualContextEntity ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string answer; std::string entityId = compV[2]; UpdateContextElementResponse response; std::string entityType; bool asJsonObject = (ciP->uriParam[URI_PARAM_ATTRIBUTE_FORMAT] == "object" && ciP->outMimeType == JSON); // 01. Take care of URI params entityType = ciP->uriParam[URI_PARAM_ENTITY_TYPE]; // 02. Fill in UpdateContextRequest from UpdateContextElementRequest and entityId parseDataP->upcr.res.fill(&parseDataP->ucer.res, entityId, entityType); // And, set the UpdateActionType to UPDATE parseDataP->upcr.res.updateActionType = ActionTypeUpdate; // 03. Call postUpdateContext standard service routine postUpdateContext(ciP, components, compV, parseDataP); // 04. Translate UpdateContextResponse to UpdateContextElementResponse response.fill(&parseDataP->upcrs.res); // 05. Cleanup and return result TIMED_RENDER(answer = response.toJsonV1(asJsonObject, IndividualContextEntity)); response.release(); parseDataP->upcr.res.release(); 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; }
/* **************************************************************************** * * deleteAttributeValueInstance - * * DELETE /v1/contextEntities/{entity::id}/attributes/{attribute::name}/{metaID} * DELETE /ngsi10/contextEntities/{entity::id}/attributes/{attribute::name}/{metaID} * * Payload In: None * Payload Out: StatusCode * * Mapped Standard Operation: UpdateContextRequest/DELETE * * URI params: * - entity::type=TYPE * - note that '!exist=entity::type' and 'exist=entity::type' are not supported by convenience operations * that use the standard operation UpdateContext as there is no restriction within UpdateContext. * * 01. URI parameters * 02. Fill in UpdateContextRequest * 03. Call postUpdateContext standard service routine * 04. Translate UpdateContextResponse to StatusCode * 05. Cleanup and return result * */ std::string deleteAttributeValueInstance ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { StatusCode response; std::string answer; std::string entityId = compV[2]; std::string attributeName = compV[4]; std::string metaId = compV[5]; std::string entityType; // 01. URI parameters entityType = ciP->uriParam[URI_PARAM_ENTITY_TYPE]; // 02. Fill in UpdateContextRequest parseDataP->upcr.res.fill(entityId, entityType, "false", attributeName, metaId, "DELETE"); // 03. Call postUpdateContext standard service routine postUpdateContext(ciP, components, compV, parseDataP); // 04. Translate UpdateContextResponse to StatusCode response.fill(parseDataP->upcrs.res); // 05. Cleanup and return result TIMED_RENDER(answer = response.render("", false, false)); response.release(); parseDataP->upcr.res.release(); return answer; }
/* **************************************************************************** * * deleteIndividualContextEntity - * * Corresponding Standard Operation: UpdateContext/DELETE * * DELETE /v1/contextEntities/{entityId::id} * * Payload In: None * Payload Out: StatusCode * * URI parameters: * - entity::type=TYPE * - note that '!exist=entity::type' and 'exist=entity::type' are not supported by convenience operations * that use the standard operation UpdateContext as there is no restriction within UpdateContext. * * 00. URI params * 01. Fill in UpdateContextRequest from URL-data + URI params * 02. Call postUpdateContext standard service routine * 03. Translate UpdateContextResponse to StatusCode * 04. If not found, put entity info in details * 05. Cleanup and return result */ std::string deleteIndividualContextEntity ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string answer; std::string entityId = compV[2]; std::string entityType = ciP->uriParam[URI_PARAM_ENTITY_TYPE]; StatusCode response; // 01. Fill in UpdateContextRequest fromURL-data + URI params parseDataP->upcr.res.fill(entityId, entityType, "false", "", "", "DELETE"); // 02. Call postUpdateContext standard service routine answer = postUpdateContext(ciP, components, compV, parseDataP); // 03. Translate UpdateContextResponse to StatusCode response.fill(parseDataP->upcrs.res); // 04. If not found, put entity info in details if ((response.code == SccContextElementNotFound) && (response.details == "")) { response.details = entityId; } // 05. Cleanup and return result TIMED_RENDER(answer = response.render(ciP->outFormat, "", false, false)); response.release(); parseDataP->upcr.res.release(); return answer; }
/* **************************************************************************** * * postEntityByIdAttributeByNameWithTypeAndId - * * POST /v1/registry/contextEntities/type/{entity::type}/id/{entity::id}/attributes/{attribute::name} * * Payload In: RegisterProviderRequest * Payload Out: RegisterContextResponse * * URI parameters: * - entity::type=XXX (must coincide with entity::type in URL) * - !exist=entity::type (if set - error -- entity::type cannot be empty) * - exist=entity::type (not supported - ok if present, ok if not present ...) * * 01. Get values from URL (entityId::type, exist, !exist) * 02. Check validity of URI params * 03. Fill in RegisterContextRequest * 04. Call standard operation postRegisterContext * 05. Cleanup and return result */ std::string postEntityByIdAttributeByNameWithTypeAndId ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string entityType = compV[4]; std::string entityId = compV[6]; std::string attributeName = compV[8]; std::string entityTypeFromUriParam = ciP->uriParam[URI_PARAM_ENTITY_TYPE]; EntityTypeInfo typeInfo = EntityTypeEmptyOrNotEmpty; std::string answer; // 01. Get values from URL (entityId::type, exist, !exist) if (ciP->uriParam[URI_PARAM_NOT_EXIST] == URI_PARAM_ENTITY_TYPE) { typeInfo = EntityTypeEmpty; } else if (ciP->uriParam[URI_PARAM_EXIST] == URI_PARAM_ENTITY_TYPE) { typeInfo = EntityTypeNotEmpty; } // // 02. Check validity of URI params ... // and if OK: // 03. Fill in RegisterContextRequest // 04. Call standard operation postRegisterContext // if (typeInfo == EntityTypeEmpty) { parseDataP->rcrs.res.errorCode.fill(SccBadRequest, "entity::type cannot be empty for this request"); alarmMgr.badInput(clientIp, "entity::type cannot be empty for this request"); TIMED_RENDER(answer = parseDataP->rcrs.res.render(IndividualContextEntityAttributeWithTypeAndId, ciP->outFormat, "")); } else if ((entityTypeFromUriParam != entityType) && (entityTypeFromUriParam != "")) { parseDataP->rcrs.res.errorCode.fill(SccBadRequest, "non-matching entity::types in URL"); alarmMgr.badInput(clientIp, "non-matching entity::types in URL"); TIMED_RENDER(answer = parseDataP->rcrs.res.render(IndividualContextEntityAttributeWithTypeAndId, ciP->outFormat, "")); } else { // 03. Fill in RegisterContextRequest parseDataP->rcr.res.fill(parseDataP->rpr.res, entityId, entityType, attributeName); // 04. Call standard operation postRegisterContext answer = postRegisterContext(ciP, components, compV, parseDataP); } // 05. Cleanup and return result parseDataP->rpr.res.release(); parseDataP->rcr.res.release(); return answer; }
/* **************************************************************************** * * putAttributeValueInstanceWithTypeAndId - * * PUT /v1/contextEntities/type/{entity::type}/id/{entity::id}/attributes/{attribute::name}/{metaID} * PUT /ngsi10/contextEntities/type/{entity::type}/id/{entity::id}/attributes/{attribute::name}/{metaID} * * Payload In: UpdateContextAttributeRequest * Payload Out: StatusCode * * Mapped Standard Operation: UpdateContextRequest/UPDATE * * URI parameters * - entity::type=TYPE * - note that '!exist=entity::type' and 'exist=entity::type' are not supported by convenience operations * that don't use the standard operation QueryContext as there is no Restriction otherwise. */ std::string putAttributeValueInstanceWithTypeAndId ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string entityType = compV[3]; std::string entityId = compV[5]; std::string attributeName = compV[7]; std::string metaID = compV[8]; std::string entityTypeFromUriParam; StatusCode response; std::string answer; // 01. Get values from URI parameters entityTypeFromUriParam = ciP->uriParam[URI_PARAM_ENTITY_TYPE]; // 02. Check validity of URI params VS URI path components if ((entityTypeFromUriParam != "") && (entityTypeFromUriParam != entityType)) { alarmMgr.badInput(clientIp, "non-matching entity::types in URL"); response.fill(SccBadRequest, "non-matching entity::types in URL"); TIMED_RENDER(answer = response.render(ciP->outFormat, "", false, false)); parseDataP->upcar.res.release(); return answer; } // 03. Check validity of path components VS payload Metadata* mP = parseDataP->upcar.res.metadataVector.lookupByName("ID"); if ((mP != NULL) && (mP->stringValue != metaID)) { std::string details = "unmatching metadata ID value URI/payload: /" + metaID + "/ vs /" + mP->stringValue + "/"; response.fill(SccBadRequest, details); TIMED_RENDER(answer = response.render(ciP->outFormat, "", false, false)); parseDataP->upcar.res.release(); return answer; } // 04. Fill in UpdateContextRequest parseDataP->upcr.res.fill(&parseDataP->upcar.res, entityId, entityType, attributeName, metaID, "UPDATE"); // 05. Call postUpdateContext postUpdateContext(ciP, components, compV, parseDataP); // 06. Fill in StatusCode from UpdateContextResponse response.fill(parseDataP->upcrs.res); // 07. Render result TIMED_RENDER(answer = response.render(ciP->outFormat, "", false, false)); // 08. Cleanup and return result response.release(); parseDataP->upcar.res.release(); parseDataP->upcr.res.release(); parseDataP->upcrs.res.release(); return answer; }
/* **************************************************************************** * * putAllEntitiesWithTypeAndId - * * PUT /v1/contextEntities/type/{entity::type}/id/{entity::id} * * Payload In: UpdateContextElementRequest * Payload Out: UpdateContextElementResponse * * URI parameters: * - attributesFormat=object * - entity::type=TYPE (must coincide with type in URL-path) * - !exist=entity::type (if set - error -- entity::type cannot be empty) * - exist=entity::type (not supported - ok if present, ok if not present ...) * * 01. Get values from URL (entityId::type, exist, !exist) * 02. Check validity of URI params * 03. Fill in UpdateContextRequest * 04. Call Standard Operation * 05. Fill in response from UpdateContextResponse * 06. Cleanup and return result */ extern std::string putAllEntitiesWithTypeAndId ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string entityType = compV[3]; std::string entityId = compV[5]; EntityTypeInfo typeInfo = EntityTypeEmptyOrNotEmpty; std::string typeNameFromUriParam = ciP->uriParam[URI_PARAM_ENTITY_TYPE]; std::string answer; UpdateContextElementResponse response; // FIXME P1: AttributeDomainName skipped // FIXME P1: domainMetadataVector skipped // 01. Get values from URL (entityId::type, esist, !exist) if (ciP->uriParam[URI_PARAM_NOT_EXIST] == URI_PARAM_ENTITY_TYPE) { typeInfo = EntityTypeEmpty; } else if (ciP->uriParam[URI_PARAM_EXIST] == URI_PARAM_ENTITY_TYPE) { typeInfo = EntityTypeNotEmpty; } // 02. Check validity of URI params if (typeInfo == EntityTypeEmpty) { alarmMgr.badInput(clientIp, "entity::type cannot be empty for this request"); response.errorCode.fill(SccBadRequest, "entity::type cannot be empty for this request"); TIMED_RENDER(answer = response.render(ciP, AllEntitiesWithTypeAndId, "")); return answer; } else if ((typeNameFromUriParam != entityType) && (typeNameFromUriParam != "")) { alarmMgr.badInput(clientIp, "non-matching entity::types in URL"); response.errorCode.fill(SccBadRequest, "non-matching entity::types in URL"); TIMED_RENDER(answer = response.render(ciP, AllEntitiesWithTypeAndId, "")); return answer; } // 03. Fill in UpdateContextRequest parseDataP->upcr.res.fill(&parseDataP->ucer.res, entityId, entityType); // 04. Call Standard Operation postUpdateContext(ciP, components, compV, parseDataP); // 05. Fill in response from UpdateContextResponse response.fill(&parseDataP->upcrs.res); // 06. Cleanup and return result TIMED_RENDER(answer = response.render(ciP, IndividualContextEntity, "")); parseDataP->upcr.res.release(); response.release(); return answer; }
/* **************************************************************************** * * getEntityAttributeValue - * * GET /v2/entities/:id:/attrs/:attrName: * * Payload In: None * Payload Out: Entity Attribute * */ std::string getEntityAttributeValue ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string answer; Attribute attribute; bool text = (ciP->uriParam["options"] == "text" || ciP->outFormat == TEXT); // Fill in QueryContextRequest parseDataP->qcr.res.fill(compV[2], "", "false", EntityTypeEmptyOrNotEmpty, ""); // Call standard op postQueryContext postQueryContext(ciP, components, compV, parseDataP); attribute.fill(&parseDataP->qcrs.res, compV[4]); // Render entity attribute response if (attribute.errorCode.error == "TooManyResults") { ciP->httpStatusCode = SccConflict; TIMED_RENDER(answer = attribute.render(ciP, EntityAttributeResponse)); } else if (attribute.errorCode.error == "NotFound") { ciP->httpStatusCode = SccContextElementNotFound; TIMED_RENDER(answer = attribute.render(ciP, EntityAttributeResponse)); } else { // the same of the wrapped operation ciP->httpStatusCode = parseDataP->qcrs.res.errorCode.code; // Remove unwanted fields from attribute before rendering attribute.pcontextAttribute->type = ""; attribute.pcontextAttribute->metadataVector.release(); if (!text) { // Do not use attribute name, change to 'value' attribute.pcontextAttribute->name = "value"; TIMED_RENDER(answer = attribute.render(ciP, EntityAttributeResponse)); } else { if (attribute.pcontextAttribute->compoundValueP != NULL) { TIMED_RENDER(answer = attribute.pcontextAttribute->compoundValueP->render(ciP, JSON, "")); if (attribute.pcontextAttribute->compoundValueP->isObject()) { answer = "{" + answer + "}"; } else if (attribute.pcontextAttribute->compoundValueP->isVector()) { answer = "[" + answer + "]"; } } else { TIMED_RENDER(answer = attribute.pcontextAttribute->toStringValue()); } ciP->outFormat = TEXT; } } // Cleanup and return result parseDataP->qcr.res.release(); return answer; }
/* **************************************************************************** * * getIndividualContextEntityAttributeWithTypeAndId - * * GET /v1/contextEntities/type/{entity::type}/id/{entity::id}/attributes/{attribute::name} * * Payload In: None * Payload Out: ContextAttributeResponse * * URI parameters: * - attributesFormat=object * - entity::type=XXX (must coincide with entity::type in URL) * - !exist=entity::type (if set - error -- entity::type cannot be empty) * - exist=entity::type (not supported - ok if present, ok if not present ...) * * 01. Get values from URL (entityId::type, esist, !exist) * 02. Check validity of URI params * 03. Fill in QueryContextRequest * 04. Call standard operation postQueryContext * 05. If 404 Not Found - enter request info into response context element * 06. Translate QueryContextResponse to ContextElementResponse * 07. Cleanup and return result */ std::string getIndividualContextEntityAttributeWithTypeAndId ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string answer; std::string entityType = compV[3]; std::string entityId = compV[5]; std::string attributeName = compV[7]; std::string entityTypeFromUriParam = ciP->uriParam[URI_PARAM_ENTITY_TYPE]; EntityTypeInfo typeInfo = EntityTypeEmptyOrNotEmpty; ContextAttributeResponse response; // 01. Get values from URL (entityId::type, esist, !exist) if (ciP->uriParam[URI_PARAM_NOT_EXIST] == URI_PARAM_ENTITY_TYPE) { typeInfo = EntityTypeEmpty; } else if (ciP->uriParam[URI_PARAM_EXIST] == URI_PARAM_ENTITY_TYPE) { typeInfo = EntityTypeNotEmpty; } // // 02. Check validity of URI params ... // and if OK; // 03. Fill in QueryContextRequest // 04. Call standard operation postQueryContext // 05. If 404 Not Found - enter request info into response context element // if (typeInfo == EntityTypeEmpty) { parseDataP->qcrs.res.errorCode.fill(SccBadRequest, "entity::type cannot be empty for this request"); LM_W(("Bad Input (entity::type cannot be empty for this request)")); } else if ((entityTypeFromUriParam != entityType) && (entityTypeFromUriParam != "")) { parseDataP->qcrs.res.errorCode.fill(SccBadRequest, "non-matching entity::types in URL"); LM_W(("Bad Input non-matching entity::types in URL")); } else { // 03. Fill in QueryContextRequest parseDataP->qcr.res.fill(entityId, entityType, attributeName); // 04. Call standard operation postQueryContext parseDataP->qcrs.res.errorCode.fill(SccOk); postQueryContext(ciP, components, compV, parseDataP); // 05. If 404 Not Found - enter request info into response context element if (parseDataP->qcrs.res.errorCode.code == SccContextElementNotFound) { response.statusCode.details = "entityId::type/attribute::name pair not found"; } } // 06. Translate QueryContextResponse to ContextAttributeResponse response.fill(&parseDataP->qcrs.res, entityId, entityType, attributeName, ""); // 07. Cleanup and return result TIMED_RENDER(answer = response.render(ciP, RtContextAttributeResponse, "")); parseDataP->qcr.res.release(); parseDataP->qcrs.res.release(); response.release(); return answer; }
/* **************************************************************************** * * logTraceTreat - */ std::string logTraceTreat ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string out = "OK"; std::string path = ""; for (int ix = 0; ix < components; ++ix) { path += compV[ix]; if (ix != components - 1) path += "/"; } if ((components == 2) && (ciP->method == "DELETE")) { lmTraceSet(NULL); out = orionLogReply(ciP, "tracelevels", "all trace levels off"); } else if ((components == 3) && (ciP->method == "DELETE")) { if (strspn(compV[2].c_str(), "0123456789-,'") != strlen(compV[2].c_str())) { out = orionLogReply(ciP, "tracelevels", "poorly formatted trace level string"); return out; } lmTraceSub(compV[2].c_str()); out = orionLogReply(ciP, "tracelevels_removed", compV[2]); } else if ((components == 2) && (ciP->method == "GET")) { char tLevels[256]; lmTraceGet(tLevels, sizeof(tLevels)); out = orionLogReply(ciP, "tracelevels", tLevels); } else if ((components == 3) && (ciP->method == "PUT")) { if (strspn(compV[2].c_str(), "0123456789-,'") != strlen(compV[2].c_str())) { out = orionLogReply(ciP, "tracelevels", "poorly formatted trace level string"); return out; } lmTraceSet(NULL); lmTraceSet(compV[2].c_str()); out = orionLogReply(ciP, "tracelevels", compV[2]); } else { OrionError error(SccBadRequest, std::string("bad URL/Verb: ") + ciP->method + " " + path); TIMED_RENDER(out = error.toJsonV1()); } return out; }
/* **************************************************************************** * * 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; }
/* **************************************************************************** * * getContextEntitiesByEntityIdAndType - * * GET /v1/registry/contextEntities/type/{ETYPE}/id/{EID} * * URI parameters: * - entity::type=XXX (must coincide with entity::type in URL) * - !exist=entity::type (if set - error -- entity::type cannot be empty) * - exist=entity::type (not supported - ok if present, ok if not present ...) * * 01. Get values from URL (entityId::type, esist, !exist) * 02. Check validity of URI params * 03. Fill in DiscoverContextAvailabilityRequest * 04. Call standard operation postDiscoverContextAvailability * 05. Cleanup and return result */ std::string getContextEntitiesByEntityIdAndType ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string entityType = compV[4]; std::string entityId = compV[6]; std::string entityTypeFromUriParam = ciP->uriParam[URI_PARAM_ENTITY_TYPE]; EntityTypeInfo typeInfo = EntityTypeEmptyOrNotEmpty; std::string answer; DiscoverContextAvailabilityResponse response; // 01. Get values from URL (entityId::type, esist, !exist) if (ciP->uriParam[URI_PARAM_NOT_EXIST] == URI_PARAM_ENTITY_TYPE) { typeInfo = EntityTypeEmpty; } else if (ciP->uriParam[URI_PARAM_EXIST] == URI_PARAM_ENTITY_TYPE) { typeInfo = EntityTypeNotEmpty; } // // 02. Check validity of URI params ... // and if OK: // 03. Fill in DiscoverContextAvailabilityRequest // 04. Call standard operation postDiscoverContextAvailability // if (typeInfo == EntityTypeEmpty) { parseDataP->dcars.res.errorCode.fill(SccBadRequest, "entity::type cannot be empty for this request"); alarmMgr.badInput(clientIp, "entity::type cannot be empty for this request"); TIMED_RENDER(answer = parseDataP->dcars.res.render(ContextEntitiesByEntityIdAndType, "")); } else if ((entityTypeFromUriParam != entityType) && (entityTypeFromUriParam != "")) { parseDataP->dcars.res.errorCode.fill(SccBadRequest, "non-matching entity::types in URL"); alarmMgr.badInput(clientIp, "non-matching entity::types in URL"); TIMED_RENDER(answer = parseDataP->dcars.res.render(ContextEntitiesByEntityIdAndType, "")); } else { // 03. Fill in DiscoverContextAvailabilityRequest parseDataP->dcar.res.fill(entityId, entityType); // 04. Call standard operation postDiscoverContextAvailability answer = postDiscoverContextAvailability(ciP, components, compV, parseDataP); } // 05. Cleanup and return result parseDataP->dcar.res.release(); parseDataP->dcars.res.release(); return answer; }
/* **************************************************************************** * * postEntities - * * POST /v2/entities * * Payload In: Entity * Payload Out: None * * URI parameters: * options=keyValues * options=upsert * * 01. Fill in UpdateContextRequest * 02. Call standard op postUpdateContext * 03. Prepare HTTP headers * 04. Cleanup and return result */ std::string postEntities ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { Entity* eP = &parseDataP->ent.res; bool upsert = ciP->uriParamOptions[OPT_UPSERT]; if (!legalEntityLength(eP, ciP->httpHeaders.servicePath)) { OrionError oe(SccBadRequest, "Too long entity id/type/servicePath combination", "BadRequest"); eP->release(); std::string out; TIMED_RENDER(out = oe.toJson()); ciP->httpStatusCode = oe.code; return out; } // Set some aspects depending on upsert or not upsert ActionType actionType; Ngsiv2Flavour ngsiv2flavour; HttpStatusCode sccCodeOnSuccess; if (upsert) { actionType = ActionTypeAppend; ngsiv2flavour = NGSIV2_NO_FLAVOUR; sccCodeOnSuccess = SccNoContent; } else { actionType = ActionTypeAppendStrict; ngsiv2flavour = NGSIV2_FLAVOUR_ONCREATE; sccCodeOnSuccess = SccCreated; } // 01. Fill in UpdateContextRequest parseDataP->upcr.res.fill(eP, actionType); // 02. Call standard op postUpdateContext postUpdateContext(ciP, components, compV, parseDataP, ngsiv2flavour); // // 03. Check error - 3 different ways to get an error from postUpdateContext ... :-( // FIXME P4: make postUpdateContext have ONE way to return errors. See github issue #2763 // std::string answer = ""; if (parseDataP->upcrs.res.oe.code != SccNone) { TIMED_RENDER(answer = parseDataP->upcrs.res.oe.toJson()); ciP->httpStatusCode = parseDataP->upcrs.res.oe.code; } else if (parseDataP->upcrs.res.errorCode.code != SccOk) { ciP->httpStatusCode = parseDataP->upcrs.res.errorCode.code; TIMED_RENDER(answer = parseDataP->upcrs.res.errorCode.toJson()); ciP->answer = answer; } else { // Prepare HTTP headers std::string location = "/v2/entities/" + eP->id; if (eP->type != "" ) { location += "?type=" + eP->type; } else { location += "?type=none"; } ciP->httpHeader.push_back(HTTP_RESOURCE_LOCATION); ciP->httpHeaderValue.push_back(location); ciP->httpStatusCode = sccCodeOnSuccess; } // 04. Cleanup and return result eP->release(); return answer; }
/* **************************************************************************** * * deleteAllEntitiesWithTypeAndId - * * DELETE /v1/contextEntities/type/{entity::type}/id/{entity::id} * * Payload In: None * Payload Out: StatusCode * * URI parameters: * - entity::type=TYPE (must coincide with type in URL-path) * - !exist=entity::type (if set - error -- entity::type cannot be empty) * - exist=entity::type (not supported - ok if present, ok if not present ...) * * 01. Get values from URL (+ entityId::type, exist, !exist) * 02. Check validity of URI params * 03. Fill in UpdateContextRequest * 04. Call Standard Operation * 05. Fill in response from UpdateContextResponse * 06. Cleanup and return result */ std::string deleteAllEntitiesWithTypeAndId ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string entityType = compV[3]; std::string entityId = compV[5]; EntityTypeInfo typeInfo = EntityTypeEmptyOrNotEmpty; std::string typeNameFromUriParam = ciP->uriParam[URI_PARAM_ENTITY_TYPE]; std::string answer; StatusCode response; // 01. Get values from URL (+ entityId::type, exist, !exist) if (ciP->uriParam[URI_PARAM_NOT_EXIST] == URI_PARAM_ENTITY_TYPE) { typeInfo = EntityTypeEmpty; } else if (ciP->uriParam[URI_PARAM_EXIST] == URI_PARAM_ENTITY_TYPE) { typeInfo = EntityTypeNotEmpty; } // 02. Check validity of URI params if (typeInfo == EntityTypeEmpty) { alarmMgr.badInput(clientIp, "entity::type cannot be empty for this request"); response.fill(SccBadRequest, "entity::type cannot be empty for this request"); TIMED_RENDER(answer = response.render("", false, false)); return answer; } else if ((typeNameFromUriParam != entityType) && (typeNameFromUriParam != "")) { alarmMgr.badInput(clientIp, "non-matching entity::types in URL"); response.fill(SccBadRequest, "non-matching entity::types in URL"); TIMED_RENDER(answer = response.render("", false, false)); return answer; } // 03. Fill in UpdateContextRequest parseDataP->upcr.res.fill(entityId, entityType, "", "", "", "DELETE"); // 04. Call Standard Operation postUpdateContext(ciP, components, compV, parseDataP); // 05. Fill in response from UpdateContextResponse response.fill(parseDataP->upcrs.res); // 06. Cleanup and return result TIMED_RENDER(answer = response.render("", false, false)); parseDataP->upcr.res.release(); response.release(); return answer; }
/* **************************************************************************** * * updateForward - * * An entity/attribute has been found on some context provider. * We need to forward the update request to the context provider, indicated in upcrsP->contextProvider * * 1. Parse the providing application to extract IP, port and URI-path * 2. Render the string of the request we want to forward * 3. Send the request to the providing application (and await the response) * 4. Parse the response and fill in a binary UpdateContextResponse * 5. Fill in the response from the redirection into the response of this function * 6. 'Fix' StatusCode * 7. Freeing memory * */ static bool updateForward(ConnectionInfo* ciP, UpdateContextRequest* upcrP, UpdateContextResponse* upcrsP) { std::string ip; std::string protocol; int port; std::string prefix; bool asJsonObject = (ciP->uriParam[URI_PARAM_ATTRIBUTE_FORMAT] == "object" && ciP->outMimeType == JSON); // // 1. Parse the providing application to extract IP, port and URI-path // if (parseUrl(upcrP->contextProvider, ip, port, prefix, protocol) == false) { std::string details = std::string("invalid providing application '") + upcrP->contextProvider + "'"; alarmMgr.badInput(clientIp, details); // // Somehow, if we accepted this providing application, it is the brokers fault ... // SccBadRequest should have been returned before, when it was registered! // upcrsP->errorCode.fill(SccContextElementNotFound, ""); return false; } LM_T(LmtForward, ("*** Provider Format: %d", upcrP->providerFormat)); // // 2. Render the string of the request we want to forward // MimeType outMimeType = ciP->outMimeType; std::string payload; char* cleanPayload; ciP->outMimeType = JSON; std::string verb; std::string resource; std::string tenant = ciP->tenant; std::string servicePath = (ciP->httpHeaders.servicePathReceived == true)? ciP->httpHeaders.servicePath : ""; std::string mimeType = "application/json"; std::string out; int r; if (upcrP->providerFormat == PfJson) { TIMED_RENDER(payload = upcrP->toJsonV1(asJsonObject)); verb = "POST"; resource = prefix + "/updateContext"; } else { TIMED_RENDER(payload = upcrP->toJson()); verb = "POST"; resource = prefix + "/op/update"; #if 0 // FIXME #3485: this part is not removed by the moment, in the case it may be useful in the // context of issue #3485 std::vector<std::string> nullFilter; Entity* eP = upcrP->entityVector[0]; eP->renderId = false; TIMED_RENDER(payload = eP->toJson(NGSI_V2_NORMALIZED, nullFilter, false, nullFilter)); resource = prefix + "/entities/" + eP->id + "/attrs"; verb = "PATCH"; if (eP->type != "") { // Add ?type=<TYPE> to 'resource' resource += "?type=" + eP->type; } #endif } ciP->outMimeType = outMimeType; cleanPayload = (char*) payload.c_str(); // // 3. Send the request to the Context Provider (and await the reply) // FIXME P7: Should Rush be used? // LM_T(LmtCPrForwardRequestPayload, ("forward updateContext request payload: %s", payload.c_str())); std::map<std::string, std::string> noHeaders; long long statusCode; // not used by the moment r = httpRequestSend(fromIp, // thread variable ip, port, protocol, verb, tenant, servicePath, ciP->httpHeaders.xauthToken, resource, mimeType, cleanPayload, ciP->httpHeaders.correlator, "", false, &out, &statusCode, noHeaders, mimeType); if (r != 0) { upcrsP->errorCode.fill(SccContextElementNotFound, "error forwarding update"); LM_E(("Runtime Error (error '%s' forwarding 'Update' to providing application)", out.c_str())); return false; } LM_T(LmtCPrForwardResponsePayload, ("forward updateContext response payload: %s", out.c_str())); // // If NGSIv1 (providerFormat == PfJson): // 4. Parse the response and fill in a binary UpdateContextResponse // 5. Fill in the response from the redirection into the response of this function // 6. 'Fix' StatusCode // 7. Free up memory // // If NGSIv2: // 4. Look for "204 No Content" in the response of the forwarded request // 5. If found: OK, else, error // if (upcrP->providerFormat == PfJson) { LM_T(LmtForward, ("upcrP->providerFormat == PfJson")); // // 4. Parse the response and fill in a binary UpdateContextResponse // std::string s; std::string errorMsg; cleanPayload = jsonPayloadClean(out.c_str()); if ((cleanPayload == NULL) || (cleanPayload[0] == 0)) { // // This is really an internal error in the Context Provider // It is not in the orion broker though, so 404 is returned // LM_W(("Other Error (context provider response to UpdateContext is empty)")); upcrsP->errorCode.fill(SccContextElementNotFound, "invalid context provider response"); return false; } // // NOTE // When coming from a convenience operation, such as GET /v1/contextEntities/EID/attributes/attrName, // the verb/method in ciP is GET. However, the parsing function expects a POST, as if it came from a // POST /v1/updateContext. // So, here we change the verb/method for POST. // ParseData parseData; ciP->verb = POST; ciP->method = "POST"; parseData.upcrs.res.errorCode.fill(SccOk); LM_T(LmtForward, ("Parsing Response of Forwarded Request: '%s'", cleanPayload)); s = jsonTreat(cleanPayload, ciP, &parseData, RtUpdateContextResponse, NULL); LM_T(LmtForward, ("Parse Result: %s", s.c_str())); if (s != "OK") { LM_W(("Internal Error (error parsing reply from prov app: %s)", errorMsg.c_str())); upcrsP->errorCode.fill(SccContextElementNotFound, ""); parseData.upcr.res.release(); parseData.upcrs.res.release(); return false; } // // 5. Fill in the response from the redirection into the response of this function // upcrsP->fill(&parseData.upcrs.res); // // 6. 'Fix' StatusCode // if (upcrsP->errorCode.code == SccNone) { upcrsP->errorCode.fill(SccOk); } if ((upcrsP->contextElementResponseVector.size() == 1) && (upcrsP->contextElementResponseVector[0]->statusCode.code == SccContextElementNotFound)) { upcrsP->errorCode.fill(SccContextElementNotFound); } // // 7. Free up memory // parseData.upcr.res.release(); parseData.upcrs.res.release(); return true; } else // NGSIv2 { LM_T(LmtForward, ("upcrP->providerFormat == V2. out: '%s'", out.c_str())); // NGSIv2 forward - no payload to be received, just a 204 No Content HTTP Header if (strstr(out.c_str(), "204 No Content") != NULL) { LM_T(LmtForward, ("Found '204 No Content'")); upcrsP->errorCode.fill(SccNone); return true; } LM_W(("Other Error (unexpected response from context provider: %s)", out.c_str())); upcrsP->errorCode.fill(SccReceiverInternalError); return false; } // Can't reach this point - no return-statement needed }