/* ****************************************************************************
*
* addContextProviders -
*
* This function takes a CRR vector and adds the Context Providers in the CER vector
* (except the ones corresponding to some locally found attribute, i.e. info already in the
* CER vector)
*
* The limitReached parameter is to prevent the addition of new entities, which is needed in the case of the pagination
* limit has been reached with local entities.
*
* The enP parameter is optional. If not NULL, then before adding a CPr the function checks that the
* containting CRR matches the entity (this is used for funcionality related to  "generic queries", see
* processGenericEntities() function)
*
*/
void addContextProviders(ContextElementResponseVector& cerV, ContextRegistrationResponseVector& crrV, bool limitReached, EntityId* enP = NULL)
{
  for (unsigned int ix = 0; ix < crrV.size(); ++ix)
  {
    ContextRegistration cr = crrV.get(ix)->contextRegistration;

    /* In the case a "filtering" entity was provided, check that the current CRR matches or skip to next CRR */
    if (enP != NULL && !matchEntityInCrr(cr, enP)) {
      continue;
    }

    if (cr.contextRegistrationAttributeVector.size() == 0)
    {
      if (!limitReached)
      {
        /* Registration without attributes */
        for (unsigned int eIx = 0; eIx < cr.entityIdVector.size(); ++eIx)
        {
          addContextProviderEntity(cerV, cr.entityIdVector.get(eIx), cr.providingApplication);
        }
      }
    }
    else
    {
      /* Registration with attributes */
      for (unsigned int eIx = 0; eIx < cr.entityIdVector.size(); ++eIx)
      {
        for (unsigned int aIx = 0; aIx < cr.contextRegistrationAttributeVector.size(); ++aIx)
        {
          addContextProviderAttribute(cerV, cr.entityIdVector.get(eIx), cr.contextRegistrationAttributeVector.get(aIx), cr.providingApplication, limitReached);
        }
      }
    }
  }
}
/* ****************************************************************************
*
* all - 
*/
TEST(ContextRegistrationResponseVector, all)
{
  ContextRegistrationResponse        crr;
  ContextRegistrationResponseVector  crrV;
  std::string                        rendered;

  crr.contextRegistration.providingApplication.set("10.1.1.1://nada");

  // Empty vector gives empty rendered result
  rendered = crrV.render(XML, "");
  EXPECT_EQ("", rendered);

  crrV.push_back(&crr);

  // presenting - just to exercise the code
  crrV.present("");

  // check OK
  rendered = crrV.check(RegisterContext, XML, "", "", 0);
  EXPECT_EQ("OK", rendered);

  // Now telling the crr that we've found an instance of '<entityIdList></entityIdList>
  // but without any entities inside the vector
  crr.contextRegistration.entityIdVectorPresent = true;
  rendered = crrV.check(RegisterContext, XML, "", "", 0);
  EXPECT_EQ("Empty entityIdVector", rendered);

  EntityId             eId;   // Empty ID

  crr.contextRegistration.entityIdVector.push_back(&eId);
  rendered = crrV.check(RegisterContext, XML, "", "", 0);
  EXPECT_EQ("empty entityId:id", rendered);
}
Esempio n. 3
0
/* ****************************************************************************
*
* equalContextRegistrationResponseVector -
*/
static bool equalContextRegistrationResponseVector
(
  ContextRegistrationResponseVector crrExpectedV,
  ContextRegistrationResponseVector crrArgV
)
{
  /* Check vector size */
  if (crrExpectedV.size() != crrArgV.size())
  {
    LM_M(("different sizes: expected %d, actual %d", crrExpectedV.size(), crrArgV.size()));
    return false;
  }

  /* Check that every context registration in 'crrArgV' is in 'crrExpectedV'. Order doesn't matter */
  for (unsigned int ix = 0; ix < crrArgV.size(); ++ix)
  {
    bool contextRegistrationMatch = false;

    for (unsigned int jx = 0; jx < crrExpectedV.size(); ++jx)
    {
      ContextRegistration crArg       = crrArgV[ix]->contextRegistration;
      ContextRegistration crExpected  = crrExpectedV[jx]->contextRegistration;

      LM_M(("%d == %d?", ix, jx));
      if (!equalEntityIdVector(crExpected.entityIdVector, crArg.entityIdVector))
      {
        LM_M(("entity vector doesn't match in ContextRegistrationResponseVector comparison, continue..."));
        continue; /* loop in jx */
      }

      if (!equalContextRegistrationAttributeVector(crExpected.contextRegistrationAttributeVector,
                                                   crArg.contextRegistrationAttributeVector))
      {
        LM_M(("context registration attribute vector doesn't match in ContextRegistrationResponseVector comparison"));
        continue; /* loop in jx */
      }

      if (crExpected.providingApplication.get() == crArg.providingApplication.get())
      {
        contextRegistrationMatch = true;
        LM_M(("context registration match in ContextRegistrationResponseVector comparison, check next one..."));
        break; /* loop in jx */
      }
    }

    if (!contextRegistrationMatch)
    {
      LM_M(("after looking everyone, context registration doesn't matches in ContextElementResponseVector comparison"));
      return false;
    }
  }

  LM_M(("ContextElementResponseVector comparison ok"));
  return true;
}
/* ****************************************************************************
*
* associationsDiscoverContextAvailability -
*/
static HttpStatusCode associationsDiscoverContextAvailability
(
  DiscoverContextAvailabilityRequest*   requestP,
  DiscoverContextAvailabilityResponse*  responseP,
  const std::string&                    scope,
  const std::string&                    tenant,
  int                                   offset,
  int                                   limit,
  bool                                  details,
  const std::vector<std::string>&       servicePathV
)
{
    if (scope == SCOPE_VALUE_ASSOC_ALL)
    {
        LM_W(("Bad Input (%s scope not supported)", SCOPE_VALUE_ASSOC_ALL));
        responseP->errorCode.fill(SccNotImplemented, std::string("Not supported scope: '") + SCOPE_VALUE_ASSOC_ALL + "'");
        return SccOk;
    }

    MetadataVector mdV;
    std::string err;
    if (!associationsQuery(&requestP->entityIdVector, &requestP->attributeList, scope, &mdV, &err, tenant, offset, limit, details, servicePathV))
    {
        mdV.release();
        responseP->errorCode.fill(SccReceiverInternalError, std::string("Database error: ") + err);
        return SccOk;
    }

    LM_T(LmtPagination, ("Offset: %d, Limit: %d, Details: %s", offset, limit, (details == true)? "true" : "false"));

    /* Query for associated entities */
    for (unsigned int ix = 0; ix < mdV.size(); ++ix) {
        /* Each association involves a registrationsQuery() operation, accumulating the answer in
         * responseP->responseVector */
        Metadata* md = mdV.get(ix);
        EntityIdVector enV;
        AttributeList attrL;

        EntityId en;
        if (scope == SCOPE_VALUE_ASSOC_SOURCE) {
            en = EntityId(md->association.entityAssociation.source.id, md->association.entityAssociation.source.type);
        }
        else {  // SCOPE_VALUE_ASSOC_TARGET
            en = EntityId(md->association.entityAssociation.target.id, md->association.entityAssociation.target.type);
        }
        enV.push_back(&en);

        for (unsigned int jx = 0; jx < md->association.attributeAssociationList.size(); ++jx) {
            if (scope == SCOPE_VALUE_ASSOC_SOURCE) {
                attrL.push_back(md->association.attributeAssociationList.get(jx)->source);
            }
            else {
                attrL.push_back(md->association.attributeAssociationList.get(jx)->target);
            }
        }

        ContextRegistrationResponseVector crrV;
        if (!registrationsQuery(enV, attrL, &crrV, &err, tenant, servicePathV))
        {
            responseP->errorCode.fill(SccReceiverInternalError, err);
            mdV.release();
            return SccOk;
        }

        /* Accumulate in responseP */
        for (unsigned int jx = 0; jx < crrV.size(); ++jx) {
            responseP->responseVector.push_back(crrV.get(jx));
        }
    }

    if (responseP->responseVector.size() == 0)
    {
      mdV.release();
      responseP->errorCode.fill(SccContextElementNotFound, "Could not query association with combination of entity/attribute");
      LM_RE(SccOk, (responseP->errorCode.details.c_str()));
    }

    /* Set association metadata as final ContextRegistrationResponse */
    ContextRegistrationResponse* crrMd = new ContextRegistrationResponse();
    crrMd->contextRegistration.providingApplication.set("http://www.fi-ware.eu/NGSI/association");
    crrMd->contextRegistration.registrationMetadataVector = mdV;
    responseP->responseVector.push_back(crrMd);

    return SccOk;
}
/* ****************************************************************************
*
* mongoQueryContext - 
*
* NOTE
*   If the in/out-parameter countP is non-NULL then the number of matching entities
*   must be returned in *countP.
*
*   This replaces the 'uriParams[URI_PARAM_PAGINATION_DETAILS]' way of passing this information.
*   The old method was one-way, using the new method 
*/
HttpStatusCode mongoQueryContext
(
  QueryContextRequest*                 requestP,
  QueryContextResponse*                responseP,
  const std::string&                   tenant,
  const std::vector<std::string>&      servicePathV,
  std::map<std::string, std::string>&  uriParams,
  long long*                           countP
)
{
    int         offset         = atoi(uriParams[URI_PARAM_PAGINATION_OFFSET].c_str());
    int         limit          = atoi(uriParams[URI_PARAM_PAGINATION_LIMIT].c_str());
    std::string detailsString  = uriParams[URI_PARAM_PAGINATION_DETAILS];
    bool        details        = (strcasecmp("on", detailsString.c_str()) == 0)? true : false;

    LM_T(LmtMongo, ("QueryContext Request"));    
    LM_T(LmtPagination, ("Offset: %d, Limit: %d, Details: %s", offset, limit, (details == true)? "true" : "false"));

    /* FIXME: restriction not supported for the moment */
    if (!requestP->restriction.attributeExpression.isEmpty())
    {
      LM_W(("Bad Input (restriction found, but restrictions are not supported by mongo backend)"));
    }

    std::string err;
    bool        ok;
    bool        limitReached = false;
    bool        reqSemTaken;

    ContextElementResponseVector rawCerV;    
    reqSemTake(__FUNCTION__, "ngsi10 query request", SemReadOp, &reqSemTaken);
    ok = entitiesQuery(requestP->entityIdVector,
                       requestP->attributeList,
                       requestP->restriction,
                       &rawCerV,
                       &err,
                       true,
                       tenant,
                       servicePathV,
                       offset,
                       limit,
                       &limitReached,
                       countP);

    if (!ok)
    {
        responseP->errorCode.fill(SccReceiverInternalError, err);
        rawCerV.release();
        reqSemGive(__FUNCTION__, "ngsi10 query request", reqSemTaken);
        return SccOk;
    }

    ContextRegistrationResponseVector crrV;

    /* In the case of empty response, if only generic processing is needed */
    if (rawCerV.size() == 0)
    {
      if (registrationsQuery(requestP->entityIdVector, requestP->attributeList, &crrV, &err, tenant, servicePathV, 0, 0, false))
      {
        if (crrV.size() > 0)
        {
          processGenericEntities(requestP->entityIdVector, rawCerV, crrV, limitReached);
        }
      }
      else
      {
        /* Different from errors in DB at entitiesQuery(), DB fails at registrationsQuery() are not considered "critical" */
        LM_E(("Database Error (%s)", err.c_str()));
      }
      crrV.release();
    }

    /* First CPr lookup (in the case some CER is not found): looking in E-A registrations */
    if (someContextElementNotFound(rawCerV))
    {
      if (registrationsQuery(requestP->entityIdVector, requestP->attributeList, &crrV, &err, tenant, servicePathV, 0, 0, false))
      {
        if (crrV.size() > 0)
        {
          fillContextProviders(rawCerV, crrV);
          processGenericEntities(requestP->entityIdVector, rawCerV, crrV, limitReached);
        }
      }
      else
      {
        /* Different from errors in DB at entitiesQuery(), DB fails at registrationsQuery() are not considered "critical" */
        LM_E(("Database Error (%s)", err.c_str()));
      }
      crrV.release();
    }

    /* Second CPr lookup (in the case some element stills not being found): looking in E-<null> registrations */
    AttributeList attrNullList;
    if (someContextElementNotFound(rawCerV))
    {
      if (registrationsQuery(requestP->entityIdVector, attrNullList, &crrV, &err, tenant, servicePathV, 0, 0, false))
      {
        if (crrV.size() > 0)
        {
          fillContextProviders(rawCerV, crrV);
        }
      }
      else
      {
        /* Different from errors in DB at entitiesQuery(), DB fails at registrationsQuery() are not considered "critical" */
        LM_E(("Database Error (%s)", err.c_str()));
      }
      crrV.release();
    }

    /* Special case: request with <null> attributes. In that case, entitiesQuery() may have captured some local attribute, but
     * the list need to be completed. Note that in the case of having this request someContextElementNotFound() is always false
     * so we efficient not invoking registrationQuery() too much times */
    if (requestP->attributeList.size() == 0)
    {
      if (registrationsQuery(requestP->entityIdVector, requestP->attributeList, &crrV, &err, tenant, servicePathV, 0, 0, false))
      {
        if (crrV.size() > 0)
        {
          addContextProviders(rawCerV, crrV, limitReached);
        }
      }
      else
      {
        /* Different from fails in DB at entitiesQuery(), DB fails at registrationsQuery() are not considered "critical" */
        LM_E(("Database Error (%s)", err.c_str()));
      }
      crrV.release();
    }

    /* Prune "not found" CERs */
    pruneContextElements(rawCerV, &responseP->contextElementResponseVector);

    /* Pagination stuff */
    if (responseP->contextElementResponseVector.size() == 0)
    {

      // If the query has an empty response, we have to fill in the status code part in the response.
      //
      // However, if the response was empty due to a too high pagination offset,
      // and if the user has asked for 'details' (as URI parameter, then the response should include information about
      // the number of hits without pagination.
      //

      if ((countP != NULL) && (*countP > 0) && (offset >= *countP))
      {
        char details[256];

        snprintf(details, sizeof(details), "Number of matching entities: %lld. Offset is %d", *countP, offset);
        responseP->errorCode.fill(SccContextElementNotFound, details);
      }
      else
      {
        responseP->errorCode.fill(SccContextElementNotFound);
      }
    }
    else if (countP != NULL)
    {
      //
      // If all was OK, but the details URI param was set to 'on', then the responses error code details
      // 'must' contain the total count of hits.
      //

      char details[64];

      snprintf(details, sizeof(details), "Count: %lld", *countP);
      responseP->errorCode.fill(SccOk, details);
    }

    rawCerV.release();
    
    reqSemGive(__FUNCTION__, "ngsi10 query request", reqSemTaken);
    return SccOk;
}