<commit_before>/*
*
* Copyright 2013 Telefonica Investigacion y Desarrollo, S.A.U
*
* This file is part of Orion Context Broker.
*
* Orion Context Broker is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Orion Context Broker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by this license please contact with
* iot_support at tid dot es
*
* Author: <NAME>
*/
#include <string>
#include <vector>

#include "logMsg/logMsg.h"
#include "logMsg/traceLevels.h"
#include "common/string.h"
#include "common/defaultValues.h"
#include "mongoBackend/mongoUpdateContext.h"
#include "ngsi/ParseData.h"
#include "ngsi10/UpdateContextResponse.h"
#include "orionTypes/UpdateContextRequestVector.h"
#include "rest/ConnectionInfo.h"
#include "rest/clientSocketHttp.h"
#include "serviceRoutines/postUpdateContext.h"
#include "xmlParse/xmlRequest.h"



/* ****************************************************************************
*
* xmlPayloadClean -
*/
char* xmlPayloadClean(const char*  payload, const char* payloadWord)  // FIXME: statis function
{
  return (char*) strstr(payload, payloadWord);
}



/* ****************************************************************************
*
* forwardsPending - 
*/
static bool forwardsPending(UpdateContextResponse* upcrsP)
{
  for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx)
  {
    ContextElementResponse* cerP  = upcrsP->contextElementResponseVector[cerIx];

    for (unsigned int aIx = 0 ; aIx < cerP->contextElement.contextAttributeVector.size(); ++aIx)
    {
      ContextAttribute* aP  = cerP->contextElement.contextAttributeVector[aIx];
      
      if (aP->providingApplication != "")
      {
        return true;
      }      
    }
  }

  return false;
}



/* ****************************************************************************
*
* 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 an XML-string of the request we want to forward
* 3. Send the request to the providing application (and await the response)
* 4. Parse the XML response and fill in a binary UpdateContextResponse
* 5. Render UpdateContextResponse from the providing application
* 6. 'Fixing' StatusCode
* 7. Freeing memory
*/
static void updateForward(ConnectionInfo* ciP, UpdateContextRequest* upcrP, UpdateContextResponse* upcrsP)
{
  std::string     ip;
  std::string     protocol;
  int             port;
  std::string     prefix;
  std::string     answer;


  //
  // 1. Parse the providing application to extract IP, port and URI-path
  //
  if (parseUrl(upcrP->contextProvider, ip, port, prefix, protocol) == false)
  {
    LM_W(("Bad Input (invalid providing application '%s')", upcrP->contextProvider.c_str()));
    //  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;
  }


  LM_M(("KZ: -----------------------------------------------"));
  LM_M(("KZ: Forwarding to %s:%d", ip.c_str(), port));
  upcrP->present("KZ1: ");


  //
  // 2. Render an XML-string of the request we want to forward
  //
  Format outFormat = ciP->outFormat;
  ciP->outFormat = XML;
  std::string  payload = upcrP->render(ciP, UpdateContext, "");
  char*        cleanPayload;
  ciP->outFormat = outFormat;

  LM_M(("KZ: payload: %s", payload.c_str()));

  if ((cleanPayload = xmlPayloadClean(payload.c_str(), "<updateContextRequest>")) == NULL)
  {
    LM_E(("Runtime Error (error rendering forward-request)"));
    upcrsP->errorCode.fill(SccContextElementNotFound, "");
    return;
  }


  //
  // 3. Send the request to the Context Provider (and await the reply)
  // FIXME P7: Should Rush be used?
  //
  std::string     out;
  std::string     verb         = "POST";
  std::string     resource     = prefix + "/updateContext";
  std::string     tenant       = ciP->tenant;
  std::string     servicePath  = (ciP->httpHeaders.servicePathReceived == true)? ciP->httpHeaders.servicePath : "";

  LM_M(("KZ: Forwarding request: %s", cleanPayload));
  out = sendHttpSocket(ip,
                       port,
                       protocol,
                       verb,
                       tenant,
                       servicePath,
                       ciP->httpHeaders.xauthToken,
                       resource,
                       "application/xml",
                       cleanPayload,
                       false,
                       true);

  if ((out == "error") || (out == ""))
  {
    upcrsP->errorCode.fill(SccContextElementNotFound, "");
    LM_E(("Runtime Error (error forwarding 'Update' to providing application)"));
    return;
  }


  //
  // 4. Parse the XML response and fill in a binary UpdateContextResponse
  //
  std::string  s;
  std::string  errorMsg;

  cleanPayload = xmlPayloadClean(out.c_str(), "<updateContextResponse>");

  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;
  }

  //
  // 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";

  s = xmlTreat(cleanPayload, ciP, &parseData, RtUpdateContextResponse, "updateContextResponse", NULL, &errorMsg);
  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;
  }


  //
  // 5. Render UpdateContextResponse from the providing application
  //
  char portV[16];
  snprintf(portV, sizeof(portV), "%d", port);

  // Fill in the response from the redirection into the response
  upcrsP->fill(&parseData.upcrs.res);


  //
  // 6. 'Fixing' 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. Freeing memory
  //
  parseData.upcr.res.release();
  parseData.upcrs.res.release();
}



/* ****************************************************************************
*
* foundAndNotFound - 
*
* Examine the response from mongo to find out what has really happened ...
*
*/
static void foundAndNotFound(UpdateContextResponse* upcrsP, UpdateContextRequest* upcrP)
{
  ContextElementResponseVector  notFoundV;

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

    //
    // Empty attribute-vector?
    //
    if (cerP->contextElement.contextAttributeVector.size() == 0)
    {
      cerP->statusCode.fill(SccContextElementNotFound, cerP->contextElement.entityId.id);
    }

    //
    // All attributes with found == false?
    //
    int noOfFounds    = 0;
    int noOfNotFounds = 0;
    for (unsigned int aIx = 0; aIx < cerP->contextElement.contextAttributeVector.size(); ++aIx)
    {
      if (cerP->contextElement.contextAttributeVector[aIx]->found == true)
      {
        ++noOfFounds;
      }
      else
      {
        ++noOfNotFounds;
      }
    }

    //
    // Now, if we have ONLY FOUNDS, then things stay the way they are, one response with '200 OK'
    // If we have ONLY NOT-FOUNDS, the we have one response with '404 Not Found'
    // If we have a mix, then we need to add a response for the not founds, with '404 Not Found'
    //
    if (noOfFounds == 0)
    {
      cerP->statusCode.fill(SccContextElementNotFound, cerP->contextElement.entityId.id);
    }
    else if ((noOfFounds > 0) && (noOfNotFounds > 0))
    {
      // Adding a ContextElementResponse for the 'Not-Founds'

      ContextElementResponse* notFoundCerP = new ContextElementResponse(&cerP->contextElement.entityId, NULL);
      notFoundCerP->statusCode.fill(SccContextElementNotFound, cerP->contextElement.entityId.id);
      notFoundV.push_back(notFoundCerP);

      // Now moving the not-founds to notFoundCerP
      LM_M(("KZ: moving the not-founds to notFoundCerP (vector of %d)", cerP->contextElement.contextAttributeVector.size()));

      std::vector<ContextAttribute*>::iterator iter;
      for (iter = cerP->contextElement.contextAttributeVector.vec.begin(); iter < cerP->contextElement.contextAttributeVector.vec.end();)
      {
        if ((*iter)->found == false)
        {
          // 1. Push to notFoundCerP
          LM_M(("KZ: pushing attribute '%s' to notFoundCerP (having %d attributes)", (*iter)->name.c_str(), notFoundCerP->contextElement.contextAttributeVector.size()));
          notFoundCerP->contextElement.contextAttributeVector.push_back(*iter);
          LM_M(("KZ: notFoundCerP now has %d attributes", notFoundCerP->contextElement.contextAttributeVector.size()));

          // 2. remove from cerP
          iter = cerP->contextElement.contextAttributeVector.vec.erase(iter);
        }
        else
        {
          ++iter;
        }
      }
    }
  }

  //
  // Now add the contextElementResponses for 404 Not Found
  //
  if (notFoundV.size() != 0)
  {
    for (unsigned int ix = 0; ix < notFoundV.size(); ++ix)
    {
      LM_M(("Adding notFoundCerP (of %d attributes) to upcrsP", notFoundV[ix]->contextElement.contextAttributeVector.size()));
      upcrsP->contextElementResponseVector.push_back(notFoundV[ix]);
    }
  }


  //
  // If nothing at all in response vector, mark as not found
  //
  if (upcrsP->contextElementResponseVector.size() == 0)
  {
    if (upcrsP->errorCode.code == SccOk)
    {
      upcrsP->errorCode.fill(SccContextElementNotFound, upcrP->contextElementVector[0]->entityId.id);
    }
  }


  //
  // Add entityId::id to details if Not Found and only one element in response
  //
  if (upcrsP->errorCode.code == SccContextElementNotFound)
  {
    if (upcrsP->contextElementResponseVector.size() == 1)
    {
      upcrsP->errorCode.details = upcrsP->contextElementResponseVector[0]->contextElement.entityId.id;
    }
  }
}
  


/* ****************************************************************************
*
* 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(SccContextElementNotFound);
  LM_M(("KZ: Calling mongoUpdateContext"));
  ciP->httpStatusCode = mongoUpdateContext(upcrP, upcrsP, ciP->tenant, ciP->servicePathV, ciP->uriParam, ciP->httpHeaders.xauthToken, "postUpdateContext");
  LM_M(("KZ: mongoUpdateContext responded with a UpdateContextResponse of %d items", upcrsP->contextElementResponseVector.size()));
  upcrsP->present("From mongoUpdateContext: ");
  foundAndNotFound(upcrsP, upcrP);
  upcrsP->present("After foundAndNotFound: ");



  //
  // 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;
      }
    }
  }


  upcrsP->present("KZ2: ");




  //
  // 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;

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

    LM_M(("KZ: Treating ContextElementResponse %d", cerIx));
    if (cerP->contextElement.contextAttributeVector.size() == 0)
    {
      //
      // What to do when finding a contextElement without attributes?
      //
      // ContextElementResponse* errorResponse = new ContextElementResponse(cerP);
      // response.contextElementResponseVector.push_back(errorResponse);
      LM_M(("KZ: Empty contextAttributeVector for ContextElementResponse %d", cerIx));
    }
    else
    {
      for (unsigned int aIx = 0; aIx < cerP->contextElement.contextAttributeVector.size(); ++aIx)
      {
        ContextAttribute* aP = cerP->contextElement.contextAttributeVector[aIx];
        
        LM_M(("KZ: Treating Attribute %d (%s) of ContextElementResponse %d", aIx, aP->name.c_str(), cerIx));

        //<commit_msg>PRE-PR fixes<commit_after>/*
*
* Copyright 2013 Telefonica Investigacion y Desarrollo, S.A.U
*
* This file is part of Orion Context Broker.
*
* Orion Context Broker is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Orion Context Broker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by this license please contact with
* iot_support at tid dot es
*
* Author: <NAME>
*/
#include <string>
#include <vector>

#include "logMsg/logMsg.h"
#include "logMsg/traceLevels.h"
#include "common/string.h"
#include "common/defaultValues.h"
#include "mongoBackend/mongoUpdateContext.h"
#include "ngsi/ParseData.h"
#include "ngsi10/UpdateContextResponse.h"
#include "orionTypes/UpdateContextRequestVector.h"
#include "rest/ConnectionInfo.h"
#include "rest/clientSocketHttp.h"
#include "serviceRoutines/postUpdateContext.h"
#include "xmlParse/xmlRequest.h"



/* ****************************************************************************
*
* xmlPayloadClean -
*/
static char* xmlPayloadClean(const char*  payload, const char* payloadWord)
{
  return (char*) strstr(payload, payloadWord);
}



/* ****************************************************************************
*
* forwardsPending - 
*/
static bool forwardsPending(UpdateContextResponse* upcrsP)
{
  for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx)
  {
    ContextElementResponse* cerP  = upcrsP->contextElementResponseVector[cerIx];

    for (unsigned int aIx = 0 ; aIx < cerP->contextElement.contextAttributeVector.size(); ++aIx)
    {
      ContextAttribute* aP  = cerP->contextElement.contextAttributeVector[aIx];
      
      if (aP->providingApplication != "")
      {
        return true;
      }      
    }
  }

  return false;
}



/* ****************************************************************************
*
* 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 an XML-string of the request we want to forward
* 3. Send the request to the providing application (and await the response)
* 4. Parse the XML response and fill in a binary UpdateContextResponse
* 5. Render UpdateContextResponse from the providing application
* 6. 'Fixing' StatusCode
* 7. Freeing memory
*/
static void updateForward(ConnectionInfo* ciP, UpdateContextRequest* upcrP, UpdateContextResponse* upcrsP)
{
  std::string     ip;
  std::string     protocol;
  int             port;
  std::string     prefix;
  std::string     answer;


  //
  // 1. Parse the providing application to extract IP, port and URI-path
  //
  if (parseUrl(upcrP->contextProvider, ip, port, prefix, protocol) == false)
  {
    LM_W(("Bad Input (invalid providing application '%s')", upcrP->contextProvider.c_str()));
    //  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;
  }


  LM_M(("KZ: -----------------------------------------------"));
  LM_M(("KZ: Forwarding to %s:%d", ip.c_str(), port));
  upcrP->present("KZ1: ");


  //
  // 2. Render an XML-string of the request we want to forward
  //
  Format outFormat = ciP->outFormat;
  ciP->outFormat = XML;
  std::string  payload = upcrP->render(ciP, UpdateContext, "");
  char*        cleanPayload;
  ciP->outFormat = outFormat;

  LM_M(("KZ: payload: %s", payload.c_str()));

  if ((cleanPayload = xmlPayloadClean(payload.c_str(), "<updateContextRequest>")) == NULL)
  {
    LM_E(("Runtime Error (error rendering forward-request)"));
    upcrsP->errorCode.fill(SccContextElementNotFound, "");
    return;
  }


  //
  // 3. Send the request to the Context Provider (and await the reply)
  // FIXME P7: Should Rush be used?
  //
  std::string     out;
  std::string     verb         = "POST";
  std::string     resource     = prefix + "/updateContext";
  std::string     tenant       = ciP->tenant;
  std::string     servicePath  = (ciP->httpHeaders.servicePathReceived == true)? ciP->httpHeaders.servicePath : "";

  LM_M(("KZ: Forwarding request: %s", cleanPayload));
  out = sendHttpSocket(ip,
                       port,
                       protocol,
                       verb,
                       tenant,
                       servicePath,
                       ciP->httpHeaders.xauthToken,
                       resource,
                       "application/xml",
                       cleanPayload,
                       false,
                       true);

  if ((out == "error") || (out == ""))
  {
    upcrsP->errorCode.fill(SccContextElementNotFound, "");
    LM_E(("Runtime Error (error forwarding 'Update' to providing application)"));
    return;
  }


  //
  // 4. Parse the XML response and fill in a binary UpdateContextResponse
  //
  std::string  s;
  std::string  errorMsg;

  cleanPayload = xmlPayloadClean(out.c_str(), "<updateContextResponse>");

  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;
  }

  //
  // 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";

  s = xmlTreat(cleanPayload, ciP, &parseData, RtUpdateContextResponse, "updateContextResponse", NULL, &errorMsg);
  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;
  }


  //
  // 5. Render UpdateContextResponse from the providing application
  //
  char portV[16];
  snprintf(portV, sizeof(portV), "%d", port);

  // Fill in the response from the redirection into the response
  upcrsP->fill(&parseData.upcrs.res);


  //
  // 6. 'Fixing' 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. Freeing memory
  //
  parseData.upcr.res.release();
  parseData.upcrs.res.release();
}



/* ****************************************************************************
*
* foundAndNotFound - 
*
* Examine the response from mongo to find out what has really happened ...
*
*/
static void foundAndNotFound(UpdateContextResponse* upcrsP, UpdateContextRequest* upcrP)
{
  ContextElementResponseVector  notFoundV;

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

    //
    // Empty attribute-vector?
    //
    if (cerP->contextElement.contextAttributeVector.size() == 0)
    {
      cerP->statusCode.fill(SccContextElementNotFound, cerP->contextElement.entityId.id);
    }

    //
    // All attributes with found == false?
    //
    int noOfFounds    = 0;
    int noOfNotFounds = 0;
    for (unsigned int aIx = 0; aIx < cerP->contextElement.contextAttributeVector.size(); ++aIx)
    {
      if (cerP->contextElement.contextAttributeVector[aIx]->found == true)
      {
        ++noOfFounds;
      }
      else
      {
        ++noOfNotFounds;
      }
    }

    //
    // Now, if we have ONLY FOUNDS, then things stay the way they are, one response with '200 OK'
    // If we have ONLY NOT-FOUNDS, the we have one response with '404 Not Found'
    // If we have a mix, then we need to add a response for the not founds, with '404 Not Found'
    //
    if (noOfFounds == 0)
    {
      cerP->statusCode.fill(SccContextElementNotFound, cerP->contextElement.entityId.id);
    }
    else if ((noOfFounds > 0) && (noOfNotFounds > 0))
    {
      // Adding a ContextElementResponse for the 'Not-Founds'

      ContextElementResponse* notFoundCerP = new ContextElementResponse(&cerP->contextElement.entityId, NULL);
      notFoundCerP->statusCode.fill(SccContextElementNotFound, cerP->contextElement.entityId.id);
      notFoundV.push_back(notFoundCerP);

      // Now moving the not-founds to notFoundCerP
      LM_M(("KZ: moving the not-founds to notFoundCerP (vector of %d)", cerP->contextElement.contextAttributeVector.size()));

      std::vector<ContextAttribute*>::iterator iter;
      for (iter = cerP->contextElement.contextAttributeVector.vec.begin(); iter < cerP->contextElement.contextAttributeVector.vec.end();)
      {
        if ((*iter)->found == false)
        {
          // 1. Push to notFoundCerP
          LM_M(("KZ: pushing attribute '%s' to notFoundCerP (having %d attributes)", (*iter)->name.c_str(), notFoundCerP->contextElement.contextAttributeVector.size()));
          notFoundCerP->contextElement.contextAttributeVector.push_back(*iter);
          LM_M(("KZ: notFoundCerP now has %d attributes", notFoundCerP->contextElement.contextAttributeVector.size()));

          // 2. remove from cerP
          iter = cerP->contextElement.contextAttributeVector.vec.erase(iter);
        }
        else
        {
          ++iter;
        }
      }
    }
  }

  //
  // Now add the contextElementResponses for 404 Not Found
  //
  if (notFoundV.size() != 0)
  {
    for (unsigned int ix = 0; ix < notFoundV.size(); ++ix)
    {
      LM_M(("Adding notFoundCerP (of %d attributes) to upcrsP", notFoundV[ix]->contextElement.contextAttributeVector.size()));
      upcrsP->contextElementResponseVector.push_back(notFoundV[ix]);
    }
  }


  //
  // If nothing at all in response vector, mark as not found
  //
  if (upcrsP->contextElementResponseVector.size() == 0)
  {
    if (upcrsP->errorCode.code == SccOk)
    {
      upcrsP->errorCode.fill(SccContextElementNotFound, upcrP->contextElementVector[0]->entityId.id);
    }
  }


  //
  // Add entityId::id to details if Not Found and only one element in response
  //
  if (upcrsP->errorCode.code == SccContextElementNotFound)
  {
    if (upcrsP->contextElementResponseVector.size() == 1)
    {
      upcrsP->errorCode.details = upcrsP->contextElementResponseVector[0]->contextElement.entityId.id;
    }
  }
}
  


/* ****************************************************************************
*
* 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(SccContextElementNotFound);
  LM_M(("KZ: Calling mongoUpdateContext"));
  ciP->httpStatusCode = mongoUpdateContext(upcrP, upcrsP, ciP->tenant, ciP->servicePathV, ciP->uriParam, ciP->httpHeaders.xauthToken, "postUpdateContext");
  LM_M(("KZ: mongoUpdateContext responded with a UpdateContextResponse of %d items", upcrsP->contextElementResponseVector.size()));
  upcrsP->present("From mongoUpdateContext: ");
  foundAndNotFound(upcrsP, upcrP);
  upcrsP->present("After foundAndNotFound: ");



  //
  // 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;
      }
    }
  }


  upcrsP->present("KZ2: ");




  //
  // 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;

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

    LM_M(("KZ: Treating ContextElementResponse %d", 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];
        
        LM_M(("KZ: Treating Attribute %d (%s) of ContextElementResponse %d", aIx, aP->name.c_str(), cerIx));

        //
{'fraction_non_alphanumeric': 0.14420439485930903, 'fraction_numerical': 0.00530095232224859, 'mean_word_length': 3.124570992754544, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 241, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 16}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3391825', 'n_tokens_mistral': 10213, 'n_tokens_neox': 9783, 'n_words': 2580}