/* ****************************************************************************
*
* 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 "";
}
Example #3
0
/* ****************************************************************************
*
* 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;
}
Example #9
0
/* ****************************************************************************
*
* 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;
}
Example #13
0
/* ****************************************************************************
*
* 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
}