/* ****************************************************************************
*
* mapPutIndividualContextEntityAttributes - 
*/
HttpStatusCode mapPutIndividualContextEntityAttributes(const std::string& entityId, UpdateContextElementRequest* ucerP, UpdateContextElementResponse* response, ConnectionInfo* ciP)
{
  HttpStatusCode          ms;
  UpdateContextRequest    ucRequest;
  UpdateContextResponse   ucResponse;
  ContextElement          ce;

  ce.entityId.fill(entityId, "", "false");
  ce.attributeDomainName    = ucerP->attributeDomainName;
  ce.contextAttributeVector = ucerP->contextAttributeVector;
  
  ucRequest.contextElementVector.push_back(&ce);
  ucRequest.updateActionType.set("Update");

  ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV);

  ContextAttributeResponse* carP = new ContextAttributeResponse();
  ContextElement*           ceP  = &ucResponse.contextElementResponseVector.get(0)->contextElement;

  for (unsigned int ix = 0; ix < ceP->contextAttributeVector.size(); ++ix)
  {
    ContextAttribute* caP = new ContextAttribute(ceP->contextAttributeVector.get(ix));
    carP->contextAttributeVector.push_back(caP);
  }

  response->contextAttributeResponseVector.push_back(carP);  
  response->errorCode.fill(&ucResponse.errorCode);
  carP->statusCode.fill(&ucResponse.contextElementResponseVector.get(0)->statusCode);

  return ms;
}
/* ****************************************************************************
*
* mapDeleteIndividualContextEntityAttributes - 
*/
HttpStatusCode mapDeleteIndividualContextEntityAttributes
(
  const std::string&  entityId,
  StatusCode*         response,
  ConnectionInfo*     ciP
)
{
  HttpStatusCode         ms;

  UpdateContextRequest   ucRequest;
  UpdateContextResponse  ucResponse;
  ContextElement*        ceP = new ContextElement();

  ceP->entityId.fill(entityId, "", "false");

  ucRequest.contextElementVector.push_back(ceP);
  ucRequest.updateActionType.set("Delete");

  ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV, ciP->uriParam);

  *response = ucResponse.contextElementResponseVector.get(0)->statusCode;

  ucRequest.release();
  ucResponse.release();

  return ms;
}
/* ****************************************************************************
*
* patternFail -
*
* Discover:  R.* - none
* Result:    none
*
* isPattern=true is not currently supported in updateContext, so this test it disabled: enable it once
* this gets supported (may need some extra modification to work)
*/
TEST(DISABLED_mongoContextProvidersUpdateRequest, patternFail)
{
  HttpStatusCode         ms;
  UpdateContextRequest   req;
  UpdateContextResponse  res;

  /* Prepare database */
  utInit();
  prepareDatabasePatternTrue();

  /* Forge the request (from "inside" to "outside") */
  /* Note that although it is a bit weird having an updateContext without attributes to update,
     * it is legal from the point of view of OMA spec */
  ContextElement ce;
  ce.entityId.fill("R.*", "T", "true");
  req.contextElementVector.push_back(&ce);
  req.updateActionType.set("UPDATE");

  /* Invoke the function in mongoBackend library */
  ms = mongoUpdateContext(&req, &res, "", servicePathVector, uriParams);

  /* Check response is as expected */
  EXPECT_EQ(SccOk, ms);

  EXPECT_EQ(SccContextElementNotFound, res.errorCode.code);
  EXPECT_EQ("No context element found", res.errorCode.reasonPhrase);
  EXPECT_EQ(0, res.errorCode.details.size());
  EXPECT_EQ(0,res.contextElementResponseVector.size());

  /* Release connection */
  mongoDisconnect();

  utExit();

}
/* ****************************************************************************
*
* mapPostIndividualContextEntityAttributes - 
*/
HttpStatusCode mapPostIndividualContextEntityAttributes(const std::string& entityId, AppendContextElementRequest* request, AppendContextElementResponse* response, ConnectionInfo* ciP)
{
  HttpStatusCode         ms;

  UpdateContextRequest   ucRequest;
  UpdateContextResponse  ucResponse;
  ContextElement         ce;

  ce.entityId.fill(entityId, "", "false");
  ce.attributeDomainName    = request->attributeDomainName;
  ce.contextAttributeVector = request->contextAttributeVector;
  
  ucRequest.contextElementVector.push_back(&ce);
  ucRequest.updateActionType.set("Append");

  ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV);

  ContextAttributeResponse* car                      = new ContextAttributeResponse();
  ContextElementResponse*   ucContextElementResponse = ucResponse.contextElementResponseVector.get(0);

  // Copying contextAttributeVector from ucContextElementResponse
  for (unsigned caIx = 0; caIx < ucContextElementResponse->contextElement.contextAttributeVector.size(); ++caIx)
  {
     LM_T(LmtClone, ("Copying ContextAttribute %d", caIx));
     ContextAttribute* caP = new ContextAttribute(ucContextElementResponse->contextElement.contextAttributeVector.get(caIx));
     
     car->contextAttributeVector.push_back(caP);     
  }
  car->statusCode.fill(&ucContextElementResponse->statusCode);

  response->contextResponseVector.push_back(car);
  response->errorCode.fill(&ucResponse.contextElementResponseVector.get(0)->statusCode);

  return ms;
}
/* ****************************************************************************
*
* noPatternMultiEntity -
*
* Discover:  (E1, E2) - no attrs
* Result:    (E1, E2) - (A1, A2, A3) - http://cr1.com
*            E1       - (A1, A4)     - http://cr2.com
*            E2       - (A2, A3)     - http://cr3.com
*/
TEST(mongoContextProvidersUpdateRequest, noPatternMultiEntity)
{
  HttpStatusCode         ms;
  UpdateContextRequest   req;
  UpdateContextResponse  res;

  /* Prepare database */
  utInit();
  prepareDatabase();

  /* Forge the request (from "inside" to "outside") */
  /* Note that although it is a bit weird having an updateContext without attributes to update,
     * it is legal from the point of view of OMA spec */
  ContextElement ce1, ce2;
  ce1.entityId.fill("E1", "T1", "false");
  ce2.entityId.fill("E2", "T2", "false");
  req.contextElementVector.push_back(&ce1);
  req.contextElementVector.push_back(&ce2);
  req.updateActionType.set("UPDATE");

  /* Invoke the function in mongoBackend library */
  ms = mongoUpdateContext(&req, &res, "", servicePathVector, uriParams);

  /* Check response is as expected */
  EXPECT_EQ(SccOk, ms);

  EXPECT_EQ(SccNone, res.errorCode.code);
  EXPECT_EQ(0, res.errorCode.reasonPhrase.size());
  EXPECT_EQ(0, res.errorCode.details.size());

  ASSERT_EQ(2,res.contextElementResponseVector.size());
  /* Context element response #1 */
  EXPECT_EQ("E1", RES_CER(0).entityId.id);
  EXPECT_EQ("T1", RES_CER(0).entityId.type);
  EXPECT_EQ("false", RES_CER(0).entityId.isPattern);
  ASSERT_EQ(0, RES_CER(0).contextAttributeVector.size());
  EXPECT_EQ(SccFound, RES_CER_STATUS(0).code);
  EXPECT_EQ("Found", RES_CER_STATUS(0).reasonPhrase);
  EXPECT_EQ("http://cr1.com", RES_CER_STATUS(0).details);

  /* Context element response #2 */
  EXPECT_EQ("E2", RES_CER(1).entityId.id);
  EXPECT_EQ("T2", RES_CER(1).entityId.type);
  EXPECT_EQ("false", RES_CER(1).entityId.isPattern);
  ASSERT_EQ(0, RES_CER(1).contextAttributeVector.size());
  EXPECT_EQ(SccFound, RES_CER_STATUS(1).code);
  EXPECT_EQ("Found", RES_CER_STATUS(1).reasonPhrase);
  EXPECT_EQ("http://cr1.com", RES_CER_STATUS(1).details);

  /* Release connection */
  mongoDisconnect();

  utExit();

}
/* ****************************************************************************
*
* noPatternMultiAttr -
*
* Discover:  E1 - (A3, A4, A5)
* Result:    E1 - A3 - http://cr1.com
*            E1 - A4 - http://cr2.com
*/
TEST(mongoContextProvidersUpdateRequest, noPatternMultiAttr)
{
  HttpStatusCode         ms;
  UpdateContextRequest   req;
  UpdateContextResponse  res;

  /* Prepare database */
  utInit();
  prepareDatabase();

  /* Forge the request (from "inside" to "outside") */
  ContextElement ce;
  ce.entityId.fill("E1", "T1", "false");
  ContextAttribute ca1("A3", "TA3", "new_val");
  ContextAttribute ca2("A4", "TA4", "new_val");
  ContextAttribute ca3("A5", "TA5", "new_val");
  ce.contextAttributeVector.push_back(&ca1);
  ce.contextAttributeVector.push_back(&ca2);
  ce.contextAttributeVector.push_back(&ca3);
  req.contextElementVector.push_back(&ce);
  req.updateActionType.set("UPDATE");

  /* Invoke the function in mongoBackend library */
  ms = mongoUpdateContext(&req, &res, "", servicePathVector, uriParams);

  /* Check response is as expected */
  EXPECT_EQ(SccOk, ms);

  EXPECT_EQ(SccNone, res.errorCode.code);
  EXPECT_EQ(0, res.errorCode.reasonPhrase.size());
  EXPECT_EQ(0, res.errorCode.details.size());

  ASSERT_EQ(1, res.contextElementResponseVector.size());
  EXPECT_EQ("E1", RES_CER(0).entityId.id);
  EXPECT_EQ("T1", RES_CER(0).entityId.type);
  EXPECT_EQ("false", RES_CER(0).entityId.isPattern);
  ASSERT_EQ(3, RES_CER(0).contextAttributeVector.size());
  EXPECT_EQ("A3", RES_CER_ATTR(0, 0)->name);
  EXPECT_EQ("TA3", RES_CER_ATTR(0, 0)->type);
  EXPECT_EQ("A4", RES_CER_ATTR(0, 1)->name);
  EXPECT_EQ("TA4", RES_CER_ATTR(0, 1)->type);
  EXPECT_EQ("A5", RES_CER_ATTR(0, 2)->name);
  EXPECT_EQ("TA5", RES_CER_ATTR(0, 2)->type);
  EXPECT_EQ(SccFound, RES_CER_STATUS(0).code);
  EXPECT_EQ("Found", RES_CER_STATUS(0).reasonPhrase);
  EXPECT_EQ("http://cr1.com", RES_CER_STATUS(0).details);

  /* Release connection */
  mongoDisconnect();

  utExit();

}
/* ****************************************************************************
*
* mapDeleteIndividualContextEntity - 
*/
HttpStatusCode mapDeleteIndividualContextEntity(const std::string& entityId, StatusCode* response, ConnectionInfo* ciP)
{
  HttpStatusCode         ms;

  UpdateContextRequest   ucRequest;
  UpdateContextResponse  ucResponse;
  ContextElement         ce;

  ce.entityId.fill(entityId, "", "false");
  
  ucRequest.contextElementVector.push_back(&ce);
  ucRequest.updateActionType.set("Delete");

  ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV);

  *response = ucResponse.contextElementResponseVector.get(0)->statusCode;

  return ms;
}
/* ****************************************************************************
*
* mapDeleteIndividualContextEntityAttribute - 
*/
HttpStatusCode mapDeleteIndividualContextEntityAttribute
(
  const std::string&  entityId,
  const std::string&  entityType,
  const std::string&  attributeName,
  StatusCode*         response,
  ConnectionInfo*     ciP
)
{
  HttpStatusCode         ms;
  UpdateContextRequest   ucRequest;
  UpdateContextResponse  ucResponse;
  ContextElement         ce;
  ContextAttribute       contextAttribute(attributeName, "", "");

  ce.entityId.fill(entityId, entityType, "false");
  ce.contextAttributeVector.push_back(&contextAttribute);
  ucRequest.updateActionType.set("Delete");

  ucRequest.contextElementVector.push_back(&ce);

  ucResponse.errorCode.code = SccOk;
  ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV, ciP->uriParam);

  //
  // Only one contextAttribute in request contextAttributeVector, so there will be only one 
  // item in ucResponse.contextElementResponseVector.
  // Completely safe to respond with ucResponse.contextElementResponseVector[0].
  // However, if there is a general error in ucResponse.errorCode, we should pick that
  // response instead.
  //
  if (ucResponse.errorCode.code != SccOk)
  {
    *response = ucResponse.errorCode;
  }
  else
  {
    *response = ucResponse.contextElementResponseVector[0]->statusCode;
  }

  return ms;
}
/* ****************************************************************************
*
* mapPostIndividualContextEntityAttribute - 
*/
HttpStatusCode mapPostIndividualContextEntityAttribute(const std::string& entityId, const std::string& attributeName, UpdateContextAttributeRequest* request, StatusCode* response, ConnectionInfo* ciP)
{
   HttpStatusCode         ms;
   UpdateContextRequest   ucRequest;
   UpdateContextResponse  ucResponse;
   ContextElement         ce;
   ContextAttribute       attribute(attributeName, "", "");

   ce.entityId.fill(entityId, "", "false");
   ce.contextAttributeVector.push_back(&attribute);

   ucRequest.contextElementVector.push_back(&ce);
   ucRequest.updateActionType.set("Append");

   ms = mongoUpdateContext(&ucRequest, &ucResponse, ciP->tenant, ciP->servicePathV);

   response->fill(SccOk);
   
   return ms;
}
/* ****************************************************************************
*
* patternNAttr -
*
* Discover:  E[1-2] - (A1, A2)
* Result:    (E1. E2) - (A1, A2) - http://cr1.com
*            E1      - A1        - http://cr2.com
*            E2      - A2        - http://cr3.com
*
* isPattern=true is not currently supported in updateContext, so this test it disabled: enable it once
* this gets supported (may need some extra modification to work)
*/
TEST(DISABLED_mongoContextProvidersUpdateRequest, patternNAttr)
{
  HttpStatusCode         ms;
  UpdateContextRequest   req;
  UpdateContextResponse  res;

  /* Prepare database */
  utInit();
  prepareDatabasePatternTrue();

  /* Forge the request (from "inside" to "outside") */
  ContextElement ce;
  ce.entityId.fill("E[1-2]", "T", "true");
  ContextAttribute ca1("A1", "TA1", "new_val");
  ContextAttribute ca2("A2", "TA2", "new_val");
  ce.contextAttributeVector.push_back(&ca1);
  ce.contextAttributeVector.push_back(&ca2);
  req.contextElementVector.push_back(&ce);
  req.updateActionType.set("UPDATE");

  /* Invoke the function in mongoBackend library */
  ms = mongoUpdateContext(&req, &res, "", servicePathVector, uriParams);

  /* Check response is as expected */
  EXPECT_EQ(SccOk, ms);

  EXPECT_EQ(SccFound, res.errorCode.code);
  EXPECT_EQ("Found", res.errorCode.reasonPhrase);
  EXPECT_EQ("http://cr1.com", res.errorCode.details);

  ASSERT_EQ(0, res.contextElementResponseVector.size());

  /* Release connection */
  mongoDisconnect();

  utExit();

}
Ejemplo n.º 11
0
/* ****************************************************************************
*
* postUpdateContext -
*
* POST /v1/updateContext
* POST /ngsi10/updateContext
*
* Payload In:  UpdateContextRequest
* Payload Out: UpdateContextResponse
*/
std::string postUpdateContext
(
  ConnectionInfo*            ciP,
  int                        components,
  std::vector<std::string>&  compV,
  ParseData*                 parseDataP
)
{
  UpdateContextResponse*  upcrsP = &parseDataP->upcrs.res;
  UpdateContextRequest*   upcrP  = &parseDataP->upcr.res;
  std::string             answer;

  //
  // 01. Check service-path consistency
  //
  // If more than ONE service-path is input, an error is returned as response.
  // If NO service-path is issued, then the default service-path "/" is used.
  // After these checks, the service-path is checked to be 'correct'.
  //
  if (ciP->servicePathV.size() > 1)
  {
    upcrsP->errorCode.fill(SccBadRequest, "more than one service path in context update request");
    LM_W(("Bad Input (more than one service path for an update request)"));
    answer = upcrsP->render(ciP, UpdateContext, "");
    return answer;
  }
  else if (ciP->servicePathV.size() == 0)
  {
    ciP->servicePathV.push_back(DEFAULT_SERVICE_PATH);
  }

  std::string res = servicePathCheck(ciP->servicePathV[0].c_str());
  if (res != "OK")
  {
    upcrsP->errorCode.fill(SccBadRequest, res);
    answer = upcrsP->render(ciP, UpdateContext, "");
    return answer;
  }


  //
  // 02. Send the request to mongoBackend/mongoUpdateContext
  //
  upcrsP->errorCode.fill(SccOk);
  attributesToNotFound(upcrP);
  ciP->httpStatusCode = mongoUpdateContext(upcrP, upcrsP, ciP->tenant, ciP->servicePathV, ciP->uriParam, ciP->httpHeaders.xauthToken, "postUpdateContext");
  foundAndNotFoundAttributeSeparation(upcrsP, upcrP, ciP);



  //
  // 03. Normal case - no forwards
  //
  // If there is nothing to forward, just return the result
  //
  bool forwarding = forwardsPending(upcrsP);
  if (forwarding == false)
  {
    answer = upcrsP->render(ciP, UpdateContext, "");
    upcrP->release();
    return answer;
  }


  //
  // 04. mongoBackend doesn't give us the values of the attributes.
  //     So, here we have to do a search inside the initial UpdateContextRequest to fill in all the
  //     attribute-values in the output from mongoUpdateContext
  //
  for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx)
  {
    ContextElement* ceP = &upcrsP->contextElementResponseVector[cerIx]->contextElement;

    for (unsigned int aIx = 0; aIx < ceP->contextAttributeVector.size(); ++aIx)
    {
      ContextAttribute* aP = upcrP->attributeLookup(&ceP->entityId, ceP->contextAttributeVector[aIx]->name);

      if (aP == NULL)
      {
        LM_E(("Internal Error (attribute '%s' not found)", ceP->contextAttributeVector[aIx]->name.c_str()));
      }
      else
      {
        ceP->contextAttributeVector[aIx]->value = aP->value;
        ceP->contextAttributeVector[aIx]->type  = aP->type;
      }
    }
  }


  //
  // 05. Forwards necessary - sort parts in outgoing requestV
  //     requestV is a vector of UpdateContextRequests and each Context Provider
  //     will have a slot in the vector. 
  //     When a ContextElementResponse is found in the output from mongoUpdateContext, a
  //     UpdateContextRequest is to be found/created and inside that UpdateContextRequest
  //     a ContextElement for the Entity of the ContextElementResponse.
  //
  //     Non-found parts go directly to 'response'.
  //
  UpdateContextRequestVector  requestV;
  UpdateContextResponse       response;

  response.errorCode.fill(SccOk);
  for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx)
  {
    ContextElementResponse* cerP  = upcrsP->contextElementResponseVector[cerIx];

    if (cerP->contextElement.contextAttributeVector.size() == 0)
    {
      //
      // If we find a contextElement without attributes here, then something is wrong
      //
      LM_E(("Orion Bug (empty contextAttributeVector for ContextElementResponse %d)", cerIx));
    }
    else
    {
      for (unsigned int aIx = 0; aIx < cerP->contextElement.contextAttributeVector.size(); ++aIx)
      {
        ContextAttribute* aP = cerP->contextElement.contextAttributeVector[aIx];
        
        //
        // 0. If the attribute is 'not-found' - just add the attribute to the outgoing response
        //
        if (aP->found == false)
        {
          response.notFoundPush(&cerP->contextElement.entityId, new ContextAttribute(aP), NULL);
          continue;
        }


        //
        // 1. If the attribute is found locally - just add the attribute to the outgoing response
        //
        if (aP->providingApplication.get() == "")
        {
          response.foundPush(&cerP->contextElement.entityId, new ContextAttribute(aP));
          continue;
        }


        //
        // 2. Lookup UpdateContextRequest in requestV according to providingApplication.
        //    If not found, add one.
        UpdateContextRequest*  reqP = requestV.lookup(aP->providingApplication.get());
        if (reqP == NULL)
        {
          reqP = new UpdateContextRequest(aP->providingApplication.get(), &cerP->contextElement.entityId);
          reqP->updateActionType.set("UPDATE");
          requestV.push_back(reqP);
        }

        //
        // 3. Increase the correct format counter
        //
        if (aP->providingApplication.getFormat() == XML)
        {
          reqP->xmls++;
        }
        else
        {
          reqP->jsons++;
        }

        //
        // 3. Lookup ContextElement in UpdateContextRequest according to EntityId.
        //    If not found, add one (to the ContextElementVector of the UpdateContextRequest).
        //
        ContextElement* ceP = reqP->contextElementVector.lookup(&cerP->contextElement.entityId);
        if (ceP == NULL)
        {
          ceP = new ContextElement(&cerP->contextElement.entityId);
          reqP->contextElementVector.push_back(ceP);
        }


        //
        // 4. Add ContextAttribute to the correct ContextElement in the correct UpdateContextRequest
        //
        ceP->contextAttributeVector.push_back(new ContextAttribute(aP));
      }
    }
  }


  //
  // Now we are ready to forward the Updates
  //


  //
  // Calling each of the Context Providers, merging their results into the
  // total response 'response'
  //
  for (unsigned int ix = 0; ix < requestV.size(); ++ix)
  {
    if (requestV[ix]->contextProvider == "")
    {
      LM_E(("Internal Error (empty context provider string)"));
      continue;
    }

    UpdateContextResponse upcrs;
    Format                format = requestV[ix]->format();

    updateForward(ciP, requestV[ix], &upcrs, format);

    //
    // Add the result from the forwarded update to the total response in 'response'
    //
    response.merge(&upcrs);
  }

  answer = response.render(ciP, UpdateContext, "");

  //
  // Cleanup
  //
  upcrP->release();
  requestV.release();
  upcrsP->release();
  upcrsP->fill(&response);
  response.release();

  return answer;
}
/* ****************************************************************************
*
* postAttributeValueInstanceWithTypeAndId - 
*
* POST /ngsi10/contextEntities/type/{type}/id/{id}/attributes/{attributeName}/{valueID}
*
* Payload: UpdateContextAttributeRequest
*/
std::string postAttributeValueInstanceWithTypeAndId
(
  ConnectionInfo*            ciP,
  int                        components,
  std::vector<std::string>&  compV,
  ParseData*                 parseDataP
)
{
  UpdateContextRequest            request;
  UpdateContextResponse           response;
  std::string                     entityType    = compV[3];
  std::string                     entityId      = compV[5];
  std::string                     attributeName = compV[7];
  std::string                     valueId       = compV[8];
  UpdateContextAttributeRequest*  upcarP        = &parseDataP->upcar.res;
  bool                            idFound       = false;

  //
  // Any metadata ID in the payload?
  //
  // If so, the value must be equal to the {valueID} of the URL
  //
  for (unsigned int ix = 0; ix < upcarP->metadataVector.size(); ++ix)
  {
    Metadata* mP = upcarP->metadataVector.get(ix);

    if (mP->name == "ID")
    {
      if (mP->value != valueId)
      {
        std::string out;

        out = restErrorReplyGet(ciP,
                                ciP->outFormat,
                                "", "StatusCode",
                                SccBadRequest,
                                std::string("unmatching metadata ID value URI/payload: '") +
                                  valueId + "' vs '" + mP->value + "'");
        return out;
      }
      else
      {
        idFound = true;
      }
    }
  }


  ContextAttribute*               attributeP    = new ContextAttribute(attributeName, "", upcarP->contextValue);
  ContextElement*                 ceP           = new ContextElement();

  // Copy the metadata vector of the input payload
  attributeP->metadataVector.fill((MetadataVector*) &upcarP->metadataVector);

  // If no "ID" metadata was in the payload, add it
  if (idFound == false)
  {
    Metadata* mP = new Metadata("ID", "", valueId);
    attributeP->metadataVector.push_back(mP);
  }

  // Filling the rest of the structure for mongoUpdateContext
  ceP->entityId.fill(entityId, entityType, "false");
  ceP->attributeDomainName.set("");
  ceP->contextAttributeVector.push_back(attributeP);
  request.contextElementVector.push_back(ceP);
  request.updateActionType.set("APPEND");

  response.errorCode.code = SccNone;
  ciP->httpStatusCode = mongoUpdateContext(&request, &response, ciP->tenant, ciP->servicePathV, ciP->uriParam);

  StatusCode statusCode;
  if (response.contextElementResponseVector.size() == 0)
  {
    statusCode.fill(SccContextElementNotFound,
                    std::string("Entity-Attribute pair: '") + entityId + "-" + attributeName + "'");
  }
  else if (response.contextElementResponseVector.size() == 1)
  {
    ContextElementResponse* cerP = response.contextElementResponseVector.get(0);

    if (response.errorCode.code != SccNone)
    {
      statusCode.fill(&response.errorCode);
    }
    else if (cerP->statusCode.code != SccNone)
    {
      statusCode.fill(&cerP->statusCode);
    }
    else
    {
      statusCode.fill(SccOk);
    }
  }
  else
  {
    statusCode.fill(SccReceiverInternalError,
                    "More than one response from postAttributeValueInstanceWithTypeAndId::mongoUpdateContext");
  }

  request.release();
  return statusCode.render(ciP->outFormat, "", false, false);
}
Ejemplo n.º 13
0
/* ****************************************************************************
*
* postUpdateContext -
*
* POST /v1/updateContext
* POST /ngsi10/updateContext
*
* Payload In:  UpdateContextRequest
* Payload Out: UpdateContextResponse
*/
std::string postUpdateContext
(
  ConnectionInfo*            ciP,
  int                        components,
  std::vector<std::string>&  compV,
  ParseData*                 parseDataP,
  Ngsiv2Flavour              ngsiV2Flavour
)
{
  UpdateContextResponse*  upcrsP = &parseDataP->upcrs.res;
  UpdateContextRequest*   upcrP  = &parseDataP->upcr.res;
  std::string             answer;

  bool asJsonObject = (ciP->uriParam[URI_PARAM_ATTRIBUTE_FORMAT] == "object" && ciP->outMimeType == JSON);
  bool forcedUpdate = ciP->uriParamOptions[OPT_FORCEDUPDATE];
  //
  // 01. Check service-path consistency
  //
  // If more than ONE service-path is input, an error is returned as response.
  // If ONE service-path is issued and that service path is "", then the default service-path is used.
  // Note that by construction servicePath cannot have 0 elements
  // After these checks, the service-path is checked to be 'correct'.
  //
  if (ciP->servicePathV.size() > 1)
  {
    upcrsP->errorCode.fill(SccBadRequest, "more than one service path in context update request");
    alarmMgr.badInput(clientIp, "more than one service path for an update request");

    TIMED_RENDER(answer = upcrsP->toJsonV1(asJsonObject));
    upcrP->release();

    return answer;
  }
  else if (ciP->servicePathV[0] == "")
  {
    ciP->servicePathV[0] = SERVICE_PATH_ROOT;
  }

  std::string res = servicePathCheck(ciP->servicePathV[0].c_str());
  if (res != "OK")
  {
    upcrsP->errorCode.fill(SccBadRequest, res);

    TIMED_RENDER(answer = upcrsP->toJsonV1(asJsonObject));

    upcrP->release();
    return answer;
  }


  //
  // 02. Send the request to mongoBackend/mongoUpdateContext
  //
  upcrsP->errorCode.fill(SccOk);
  attributesToNotFound(upcrP);

  HttpStatusCode httpStatusCode;
  TIMED_MONGO(httpStatusCode = mongoUpdateContext(upcrP,
                                                  upcrsP,
                                                  ciP->tenant,
                                                  ciP->servicePathV,
                                                  ciP->uriParam,
                                                  ciP->httpHeaders.xauthToken,
                                                  ciP->httpHeaders.correlator,
                                                  ciP->httpHeaders.ngsiv2AttrsFormat,
                                                  forcedUpdate,
                                                  ciP->apiVersion,
                                                  ngsiV2Flavour));

  if (ciP->httpStatusCode != SccCreated)
  {
    ciP->httpStatusCode = httpStatusCode;
  }

  foundAndNotFoundAttributeSeparation(upcrsP, upcrP, ciP);



  //
  // 03. Normal case - no forwards
  //
  // If there is nothing to forward, just return the result
  //
  bool forwarding = forwardsPending(upcrsP);
  LM_T(LmtForward, ("forwardsPending returned %s", FT(forwarding)));
  if (forwarding == false)
  {
    TIMED_RENDER(answer = upcrsP->toJsonV1(asJsonObject));

    upcrP->release();
    return answer;
  }



  //
  // 04. mongoBackend doesn't give us the values of the attributes.
  //     So, here we have to do a search inside the initial UpdateContextRequest to fill in all the
  //     attribute-values in the output from mongoUpdateContext
  //
  for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx)
  {
    Entity* eP = &upcrsP->contextElementResponseVector[cerIx]->entity;

    for (unsigned int aIx = 0; aIx < eP->attributeVector.size(); ++aIx)
    {
      ContextAttribute* aP = upcrP->attributeLookup(eP, eP->attributeVector[aIx]->name);

      if (aP == NULL)
      {
        LM_E(("Internal Error (attribute '%s' not found)", eP->attributeVector[aIx]->name.c_str()));
      }
      else
      {
        eP->attributeVector[aIx]->stringValue    = aP->stringValue;
        eP->attributeVector[aIx]->numberValue    = aP->numberValue;
        eP->attributeVector[aIx]->boolValue      = aP->boolValue;
        eP->attributeVector[aIx]->valueType      = aP->valueType;
        eP->attributeVector[aIx]->compoundValueP = aP->compoundValueP == NULL ? NULL : aP->compoundValueP->clone();
      }
    }
  }


  //
  // 05. Forwards necessary - sort parts in outgoing requestV
  //     requestV is a vector of UpdateContextRequests and each Context Provider
  //     will have a slot in the vector.
  //     When a ContextElementResponse is found in the output from mongoUpdateContext, a
  //     UpdateContextRequest is to be found/created and inside that UpdateContextRequest
  //     a ContextElement for the Entity of the ContextElementResponse.
  //
  //     Non-found parts go directly to 'response'.
  //
  UpdateContextRequestVector  requestV;
  UpdateContextResponse       response;

  response.errorCode.fill(SccOk);
  for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx)
  {
    ContextElementResponse* cerP  = upcrsP->contextElementResponseVector[cerIx];

    if (cerP->entity.attributeVector.size() == 0)
    {
      //
      // If we find a contextElement without attributes here, then something is wrong
      //
      LM_E(("Orion Bug (empty contextAttributeVector for ContextElementResponse %d)", cerIx));
    }
    else
    {
      for (unsigned int aIx = 0; aIx < cerP->entity.attributeVector.size(); ++aIx)
      {
        ContextAttribute* aP = cerP->entity.attributeVector[aIx];

        //
        // 0. If the attribute is 'not-found' - just add the attribute to the outgoing response
        //
        if (aP->found == false)
        {
          ContextAttribute ca(aP);
          response.notFoundPush(&cerP->entity, &ca, NULL);
          continue;
        }


        //
        // 1. If the attribute is found locally - just add the attribute to the outgoing response
        //
        if (aP->providingApplication.get() == "")
        {
          ContextAttribute ca(aP);
          response.foundPush(&cerP->entity, &ca);
          continue;
        }


        //
        // 2. Lookup UpdateContextRequest in requestV according to providingApplication.
        //    If not found, add one.
        UpdateContextRequest*  reqP = requestV.lookup(aP->providingApplication.get());
        if (reqP == NULL)
        {
          reqP = new UpdateContextRequest(aP->providingApplication.get(), aP->providingApplication.providerFormat, &cerP->entity);
          reqP->updateActionType = ActionTypeUpdate;
          requestV.push_back(reqP);
        }

        //
        // 3. Lookup ContextElement in UpdateContextRequest according to EntityId.
        //    If not found, add one (to the EntityVector of the UpdateContextRequest).
        //
        Entity* eP = reqP->entityVector.lookup(cerP->entity.id, cerP->entity.type);
        if (eP == NULL)
        {
          eP = new Entity();
          eP->fill(cerP->entity.id, cerP->entity.type, cerP->entity.isPattern);
          reqP->entityVector.push_back(eP);
        }


        //
        // 4. Add ContextAttribute to the correct ContextElement in the correct UpdateContextRequest
        //
        eP->attributeVector.push_back(new ContextAttribute(aP));
      }
    }
  }


  //
  // Now we are ready to forward the Updates
  //


  //
  // Calling each of the Context Providers, merging their results into the
  // total response 'response'
  //
  bool forwardOk = true;

  for (unsigned int ix = 0; ix < requestV.size() && ix < cprForwardLimit; ++ix)
  {
    if (requestV[ix]->contextProvider == "")
    {
      LM_E(("Internal Error (empty context provider string)"));
      continue;
    }

    UpdateContextResponse upcrs;
    bool                  b;

    b = updateForward(ciP, requestV[ix], &upcrs);

    if (b == false)
    {
      forwardOk = false;
    }

    //
    // Add the result from the forwarded update to the total response in 'response'
    //
    response.merge(&upcrs);
  }

  //
  // Note this is a slight break in the separation of concerns among the different layers (i.e.
  // serviceRoutine/ logic should work in a "NGSIv1 isolated context"). However, it seems to be
  // a smart way of dealing with partial update situations
  //
  if (ciP->apiVersion == V2)
  {
    LM_T(LmtForward, ("ciP->apiVersion == V2"));
    //
    // Adjust OrionError response in the case of partial updates. This may happen in CPr forwarding
    // scenarios. Note that mongoBackend logic "splits" successfull updates and failing updates in
    // two different CER (maybe using the same entity)
    //
    std::string failing = "";
    unsigned int failures  = 0;

    LM_T(LmtForward, ("Going over a contextElementResponseVector of %d items", response.contextElementResponseVector.size()));
    for (unsigned int ix = 0; ix < response.contextElementResponseVector.size(); ++ix)
    {
      ContextElementResponse* cerP = response.contextElementResponseVector[ix];

      if (cerP->statusCode.code != SccOk)
      {
        failures++;

        std::string failingPerCer = "";
        for (unsigned int jx = 0; jx < cerP->entity.attributeVector.size(); ++jx)
        {
          failingPerCer += cerP->entity.attributeVector[jx]->name;
          if (jx != cerP->entity.attributeVector.size() - 1)
          {
            failingPerCer +=", ";
          }
        }

        failing += cerP->entity.id + "-" + cerP->entity.type + " : [" + failingPerCer + "], ";
      }
    }

    //
    // Note that we modify parseDataP->upcrs.res.oe and not response.oe, as the former is the
    // one used by the calling postBatchUpdate() function at serviceRoutineV2 library
    //
    if ((forwardOk == true) && (failures == 0))
    {
      parseDataP->upcrs.res.oe.fill(SccNone, "");
    }
    else if (failures == response.contextElementResponseVector.size())
    {
      parseDataP->upcrs.res.oe.fill(SccContextElementNotFound, ERROR_DESC_NOT_FOUND_ENTITY, ERROR_NOT_FOUND);
    }
    else if (failures > 0)
    {
      // Removing trailing ", "
      failing = failing.substr(0, failing.size() - 2);

      // If some CER (but not all) fail, then it is a partial update
      parseDataP->upcrs.res.oe.fill(SccContextElementNotFound, "Attributes that were not updated: { " + failing + " }", "PartialUpdate");
    }
    else  // failures == 0
    {
      // No failure, so invalidate any possible OrionError filled by mongoBackend on the mongoUpdateContext step
      parseDataP->upcrs.res.oe.fill(SccNone, "");
    }
  }
  else  // v1
  {
    LM_T(LmtForward, ("ciP->apiVersion != V2"));
    // Note that v2 case doesn't use an actual response (so no need to waste time rendering it).
    // We render in the v1 case only
    TIMED_RENDER(answer = response.toJsonV1(asJsonObject));
  }

  //
  // Cleanup
  //
  upcrP->release();
  requestV.release();
  upcrsP->release();
  upcrsP->fill(&response);
  response.release();

  return answer;
}
/* ****************************************************************************
*
* deleteAttributeValueInstanceWithTypeAndId - 
*
* DELETE /ngsi10/contextEntities/type/{type}/id/{id}/attributes/{attributeName}/{valueID}
*/
std::string deleteAttributeValueInstanceWithTypeAndId
(
  ConnectionInfo*            ciP,
  int                        components,
  std::vector<std::string>&  compV,
  ParseData*                 parseDataP
)
{
  UpdateContextRequest            request;
  UpdateContextResponse           response;
  std::string                     entityType    = compV[3];
  std::string                     entityId      = compV[5];
  std::string                     attributeName = compV[7];
  std::string                     valueId       = compV[8];
  ContextAttribute*               attributeP    = new ContextAttribute(attributeName, "", "false");
  Metadata*                       mP            = new Metadata("ID", "", valueId);
  ContextElement*                 ceP           = new ContextElement();

  attributeP->metadataVector.push_back(mP);

  ceP->entityId.fill(entityId, entityType, "false");
  ceP->attributeDomainName.set("");
  ceP->contextAttributeVector.push_back(attributeP);

  request.contextElementVector.push_back(ceP);
  request.updateActionType.set("DELETE");

  response.errorCode.code = SccNone;
  ciP->httpStatusCode = mongoUpdateContext(&request, &response, ciP->tenant, ciP->servicePathV, ciP->uriParam);

  StatusCode statusCode;
  if (response.contextElementResponseVector.size() == 0)
  {
    statusCode.fill(SccContextElementNotFound,
                    std::string("Entity-Attribute pair: '") + entityId + "-" + attributeName + "'");
  }
  else if (response.contextElementResponseVector.size() == 1)
  {
     ContextElementResponse* cerP = response.contextElementResponseVector.get(0);

    if (response.errorCode.code != SccNone)
    {
      statusCode.fill(&response.errorCode);
    }
    else if (cerP->statusCode.code != SccNone)
    {
      statusCode.fill(&cerP->statusCode);
    }
    else
    {
      statusCode.fill(SccOk);
    }
  }
  else
  {
    statusCode.fill(SccReceiverInternalError,
                    "More than one response from deleteAttributeValueInstanceWithTypeAndId::mongoUpdateContext");
  }

  request.release();

  return statusCode.render(ciP->outFormat, "", false, false);
}