diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 0b87f1d5fd..d195f7a5d3 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -6,16 +6,16 @@ * #1573: pagination not working when entities have the exact same creation date (sort also by entity id) * #1583: If a 'q' contains an ampersand, that ampersand needs to be forwarded as a semicolon * #1593: Modify the type of an entity in a Replace operation (only working in legacy mode) + * Fixed a possible crash for TRoE and attributes of type Vocab/Json/Language + * Forbidden to DELETE the Core Context !!! (it can be reloaded) + * Bug fix: JSON NULL literal is no longer forwarded! ## New Features: * Support for attributes of type VocabularyProperty * Support for attributes of type JsonProperty * Support for the new URL parameter "format" for output formats (normalized, concise, simplified) - * Support for JsonProperty attribute type - * Fixed a possible crash for TRoE and attributes of type Vocab/Json/Language - * Forbidden to DELETE the Core Context !!! (it can be reloaded) - * Bug fix: JSON NULL literal is no longer forwarded! - + * New service: DELETE /ngsi-ld/v1/entities (URL param 'type' only - the rest are missing still) + ## Notes * TRoE is still not prepared for attributes of type Vocab/Json/Language, so, attributes of those types are not stored in the historical database * Modified the @context hosting feature to be according to API spec diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b25eee43f..e86c8607f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,7 @@ SET (ORION_LIBS orionld_serviceRoutines orionld_distOp orionld_regMatch + orionld_distOp orionld_troe orionld_kjTree orionld_mqtt diff --git a/src/lib/orionld/distOp/distOpSend.cpp b/src/lib/orionld/distOp/distOpSend.cpp index 9b25c84d20..d7bb2280d1 100644 --- a/src/lib/orionld/distOp/distOpSend.cpp +++ b/src/lib/orionld/distOp/distOpSend.cpp @@ -40,11 +40,13 @@ extern "C" #include "orionld/types/DistOp.h" // DistOp #include "orionld/types/ApiVersion.h" // API_VERSION_NGSILD_V1 +#include "orionld/types/OrionLdRestService.h" // OrionLdRestService #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/tenantList.h" // tenant0 #include "orionld/context/orionldCoreContext.h" // orionldCoreContextP #include "orionld/context/orionldContextItemAliasLookup.h" // orionldContextItemAliasLookup #include "orionld/q/qRender.h" // qRender +#include "orionld/serviceRoutines/orionldDeleteAttribute.h" // orionldDeleteAttribute #include "orionld/distOp/distOpSend.h" // Own interface @@ -469,9 +471,9 @@ bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedF // Add URI Params // LM_T(LmtDistOpRequestParams, ("%s: ---- URL Parameters for %s ------------------------", distOpP->regP->regId, distOpP->id)); - if (orionldState.verb == HTTP_GET) + if ((orionldState.verb == HTTP_GET) || (orionldState.verb == HTTP_DELETE)) { - if (distOpP->attrsParam != NULL) + if ((distOpP->attrsParam != NULL) && (orionldState.serviceP->serviceRoutine != orionldDeleteAttribute)) uriParamAdd(&urlParts, distOpP->attrsParam, NULL, distOpP->attrsParamLen); // @@ -480,10 +482,13 @@ bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedF // // There is one exception though - when only the entity ids are wanted as output // - if (distOpP->onlyIds == true) - uriParamAdd(&urlParts, "onlyIds=true", NULL, 12); - else - uriParamAdd(&urlParts, "options=sysAttrs", NULL, 16); + if (orionldState.verb == HTTP_GET) + { + if (distOpP->onlyIds == true) + uriParamAdd(&urlParts, "onlyIds=true", NULL, 12); + else + uriParamAdd(&urlParts, "options=sysAttrs", NULL, 16); + } if (distOpP->operation == DoQueryEntity) { diff --git a/src/lib/orionld/payloadCheck/CMakeLists.txt b/src/lib/orionld/payloadCheck/CMakeLists.txt index 0c9f3017a7..eb5ad8d02d 100644 --- a/src/lib/orionld/payloadCheck/CMakeLists.txt +++ b/src/lib/orionld/payloadCheck/CMakeLists.txt @@ -70,6 +70,7 @@ SET (SOURCES pCheckRegistrationManagement.cpp pCheckTenant.cpp pCheckScope.cpp + pCheckQueryParams.cpp ) # Include directories diff --git a/src/lib/orionld/payloadCheck/pCheckQueryParams.cpp b/src/lib/orionld/payloadCheck/pCheckQueryParams.cpp new file mode 100644 index 0000000000..03cc991a64 --- /dev/null +++ b/src/lib/orionld/payloadCheck/pCheckQueryParams.cpp @@ -0,0 +1,126 @@ +/* +* +* Copyright 2024 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD 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-LD 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-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/types/QNode.h" // QNode +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/q/qLex.h" // qLex +#include "orionld/q/qParse.h" // qParse +#include "orionld/payloadCheck/pCheckGeo.h" // pCheckGeo +#include "orionld/payloadCheck/pCheckQueryParams.h" // Own interface + + + +// ---------------------------------------------------------------------------- +// +// qCheck - +// +static QNode* qCheck(char* qString) +{ + QNode* qList; + char* title; + char* detail; + + qList = qLex(qString, true, &title, &detail); + if (qList == NULL) + { + orionldError(OrionldBadRequestData, "Invalid Q-Filter", detail, 400); + LM_RE(NULL, ("Error (qLex: %s: %s)", title, detail)); + } + + QNode* qNode = qParse(qList, NULL, true, true, &title, &detail); // 3rd parameter: forDb=true + if (qNode == NULL) + { + orionldError(OrionldBadRequestData, title, detail, 400); + LM_E(("Error (qParse: %s: %s) - but, the subscription will be inserted in the sub-cache without 'q'", title, detail)); + } + + return qNode; +} + + + +// ----------------------------------------------------------------------------- +// +// pCheckQueryParams - +// +bool pCheckQueryParams +( + char* id, + char* type, + char* idPattern, + char* q, + char* geometry, + char* attrs, + bool local, + EntityMap* entityMap, + QNode** qNodeP, + OrionldGeoInfo* geoInfoP +) +{ + // + // URI param validity check + // + + if ((id == NULL) && + (idPattern == NULL) && + (type == NULL) && + (geometry == NULL) && + (attrs == NULL) && + (q == NULL) && + (local == false) && + (orionldState.in.entityMap == NULL)) + { + orionldError(OrionldBadRequestData, + "Too broad query", + "Need at least one of: entity-id, entity-type, geo-location, attribute-list, Q-filter, local=true, or an entity map", + 400); + + return false; + } + + + // + // If ONE or ZERO types in URI param 'type', the prepared array isn't used, just a simple char-pointer (named "type") + // + if (orionldState.in.typeList.items == 0) type = (char*) ".*"; + else if (orionldState.in.typeList.items == 1) type = orionldState.in.typeList.array[0]; + + if (pCheckGeo(geoInfoP, orionldState.uriParams.geometry, orionldState.uriParams.georel, orionldState.uriParams.coordinates, orionldState.uriParams.geoproperty) == false) + return false; + + QNode* qNode = NULL; + if (orionldState.uriParams.q != NULL) + { + qNode = qCheck(orionldState.uriParams.q); + if (qNode == NULL) + return false; + } + + *qNodeP = qNode; + + return true; +} diff --git a/src/lib/orionld/payloadCheck/pCheckQueryParams.h b/src/lib/orionld/payloadCheck/pCheckQueryParams.h new file mode 100644 index 0000000000..893b851a21 --- /dev/null +++ b/src/lib/orionld/payloadCheck/pCheckQueryParams.h @@ -0,0 +1,55 @@ +#ifndef SRC_LIB_ORIONLD_PAYLOADCHECK_PCHECKQUERYPARAMS_H_ +#define SRC_LIB_ORIONLD_PAYLOADCHECK_PCHECKQUERYPARAMS_H_ + +/* +* +* Copyright 2024 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD 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-LD 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-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +} + +#include "orionld/types/TreeNode.h" // TreeNode + + + +// ----------------------------------------------------------------------------- +// +// pCheckQueryParams - +// +extern bool pCheckQueryParams +( + char* id, + char* type, + char* idPattern, + char* q, + char* geometry, + char* attrs, + bool local, + EntityMap* entityMap, + QNode** qNodeP, + OrionldGeoInfo* geoInfoP +); + +#endif // SRC_LIB_ORIONLD_PAYLOADCHECK_PCHECKQUERYPARAMS_H_ diff --git a/src/lib/orionld/regMatch/CMakeLists.txt b/src/lib/orionld/regMatch/CMakeLists.txt index 7b7fafdbc1..060c00856f 100644 --- a/src/lib/orionld/regMatch/CMakeLists.txt +++ b/src/lib/orionld/regMatch/CMakeLists.txt @@ -36,6 +36,7 @@ SET (SOURCES regMatchInformationItemForQuery.cpp regMatchAttributesForQuery.cpp regMatchEntityInfoForQuery.cpp + regMatchForEntitiesQuery.cpp ) # Include directories diff --git a/src/lib/orionld/regMatch/regMatchForEntitiesQuery.cpp b/src/lib/orionld/regMatch/regMatchForEntitiesQuery.cpp new file mode 100644 index 0000000000..f5fbbe0fb8 --- /dev/null +++ b/src/lib/orionld/regMatch/regMatchForEntitiesQuery.cpp @@ -0,0 +1,96 @@ +/* +* +* Copyright 2024 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD 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-LD 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-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/types/DistOp.h" // DistOp +#include "orionld/types/RegistrationMode.h" // RegistrationMode +#include "orionld/types/DistOpType.h" // DistOpType +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/types/RegCache.h" // RegCache +#include "orionld/types/RegCacheItem.h" // RegCacheItem +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/regMatch/regMatchOperation.h" // regMatchOperation +#include "orionld/regMatch/regMatchInformationArrayForQuery.h" // regMatchInformationArrayForQuery +#include "orionld/distOp/viaMatch.h" // viaMatch +#include "orionld/distOp/distOpListsMerge.h" // distOpListsMerge +#include "orionld/regMatch/regMatchForEntitiesQuery.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// regMatchForEntitiesquery - +// +DistOp* regMatchForEntitiesQuery +( + RegistrationMode regMode, + DistOpType opType, + StringArray* idListP, + StringArray* typeListP, + StringArray* attrListP +) +{ + DistOp* distOpList = NULL; + + LM_W(("Looping over regCache of tenant '%s' (cache at %p, first reg at %p)", orionldState.tenantP->mongoDbName, orionldState.tenantP->regCache, orionldState.tenantP->regCache->regList)); + for (RegCacheItem* regP = orionldState.tenantP->regCache->regList; regP != NULL; regP = regP->next) + { + LM_W(("In Loop")); + if ((regP->mode & regMode) == 0) + { + LM_T(LmtRegMatch, ("%s: No Reg Match due to RegistrationMode ('%s' vs '%s')", regP->regId, registrationModeToString(regP->mode), registrationModeToString(regMode))); + continue; + } + + if (regMatchOperation(regP, opType) == false) + { + LM_T(LmtRegMatch, ("%s: No Reg Match due to Operation (operation == '%s')", regP->regId, distOpTypeToString(opType))); + continue; + } + + // Loop detection + if (viaMatch(orionldState.in.via, regP->hostAlias) == true) + { + LM_T(LmtRegMatch, ("%s: No Reg Match due to Loop (Via)", regP->regId)); + continue; + } + + DistOp* distOpP = regMatchInformationArrayForQuery(regP, idListP, typeListP, attrListP); + if (distOpP == NULL) + { + LM_T(LmtRegMatch, ("%s: No Reg Match due to Information Array", regP->regId)); + continue; + } + + // + // Add distOpP to the linked list (distOpList) + // + LM_T(LmtRegMatch, ("%s: Reg Match !", regP->regId)); + + distOpList = distOpListsMerge(distOpList, distOpP); + } + + return distOpList; +} diff --git a/src/lib/orionld/regMatch/regMatchForEntitiesQuery.h b/src/lib/orionld/regMatch/regMatchForEntitiesQuery.h new file mode 100644 index 0000000000..aa6e53a81e --- /dev/null +++ b/src/lib/orionld/regMatch/regMatchForEntitiesQuery.h @@ -0,0 +1,48 @@ +#ifndef SRC_LIB_ORIONLD_REGMATCH_REGMATCHFORENTITIESQUERY_H_ +#define SRC_LIB_ORIONLD_REGMATCH_REGMATCHFORENTITIESQUERY_H_ + +/* +* +* Copyright 2024 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD 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-LD 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-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/types/DistOp.h" // DistOp +#include "orionld/types/RegistrationMode.h" // RegistrationMode +#include "orionld/types/DistOpType.h" // DistOpType +#include "orionld/types/StringArray.h" // StringArray + + + +// ----------------------------------------------------------------------------- +// +// regMatchForEntitiesquery - +// +extern DistOp* regMatchForEntitiesQuery +( + RegistrationMode regMode, + DistOpType opType, + StringArray* idListP, + StringArray* typeListP, + StringArray* attrListP +); + +#endif // SRC_LIB_ORIONLD_REGMATCH_REGMATCHFORENTITIESQUERY_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldBadVerb.cpp b/src/lib/orionld/serviceRoutines/orionldBadVerb.cpp index 0d762846ab..b01b5b0db6 100644 --- a/src/lib/orionld/serviceRoutines/orionldBadVerb.cpp +++ b/src/lib/orionld/serviceRoutines/orionldBadVerb.cpp @@ -63,8 +63,8 @@ bool orionldBadVerb(void) if (bitmask & (1 << HTTP_GET)) strcat(allowValue, ",GET"); if (bitmask & (1 << HTTP_PUT)) strcat(allowValue, ",PUT"); if (bitmask & (1 << HTTP_POST)) strcat(allowValue, ",POST"); - if (bitmask & (1 << HTTP_DELETE)) strcat(allowValue, ",PATCH"); - if (bitmask & (1 << HTTP_PATCH)) strcat(allowValue, ",DELETE"); + if (bitmask & (1 << HTTP_DELETE)) strcat(allowValue, ",DELETE"); + if (bitmask & (1 << HTTP_PATCH)) strcat(allowValue, ",PATCH"); orionldHeaderAdd(&orionldState.out.headers, HttpAllow, (char*) &allowValue[1], 0); // Skipping first comma of 'allowValue' diff --git a/src/lib/orionld/serviceRoutines/orionldDeleteEntities.cpp b/src/lib/orionld/serviceRoutines/orionldDeleteEntities.cpp index 6fc199e840..e2cb190359 100644 --- a/src/lib/orionld/serviceRoutines/orionldDeleteEntities.cpp +++ b/src/lib/orionld/serviceRoutines/orionldDeleteEntities.cpp @@ -35,25 +35,18 @@ extern "C" #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/orionldError.h" // orionldError #include "orionld/context/orionldContextItemExpand.h" // orionldContextItemExpand +#include "orionld/payloadCheck/pCheckQueryParams.h" // pCheckQueryParams #include "orionld/mongoc/mongocEntitiesQuery.h" // mongocEntitiesQuery #include "orionld/mongoc/mongocEntitiesDelete.h" // mongocEntitiesDelete #include "orionld/context/orionldContextItemAliasLookup.h" // orionldContextItemAliasLookup +#include "orionld/regMatch/regMatchOperation.h" // regMatchOperation +#include "orionld/regMatch/regMatchForEntitiesQuery.h" // regMatchForEntitiesQuery #include "orionld/distOp/distOpSuccess.h" // distOpSuccess #include "orionld/distOp/distOpFailure.h" // distOpFailure - -#if 0 -#include "orionld/common/responseFix.h" // responseFix -#include "orionld/mongoc/mongocEntityDelete.h" // mongocEntityDelete -#include "orionld/notifications/orionldAlterations.h" // orionldAlterations -#include "orionld/regMatch/regMatchForEntityGet.h" // regMatchForEntityGet #include "orionld/distOp/distOpListsMerge.h" // distOpListsMerge -#include "orionld/distOp/distOpSend.h" // distOpSend -#include "orionld/distOp/distOpLookupByCurlHandle.h" // distOpLookupByCurlHandle -#include "orionld/distOp/distOpResponses.h" // distOpResponses #include "orionld/distOp/distOpListRelease.h" // distOpListRelease -#include "orionld/distOp/xForwardedForCompose.h" // xForwardedForCompose -#include "orionld/distOp/viaCompose.h" // viaCompose -#endif +#include "orionld/distOp/distOpsSend.h" // distOpsSend +#include "orionld/distOp/distOpResponses.h" // distOpResponses #include "orionld/serviceRoutines/orionldDeleteEntities.h" // Own Interface @@ -100,21 +93,26 @@ static void alterationAdd(char* entityId, char* entityTypeExpanded, char* entity // ---------------------------------------------------------------------------- // -// pCheckQueryParams - FIXME: Own Module !!! +// distOpRequestsForEntitiesPurge - // -extern bool pCheckQueryParams -( - char* id, - char* type, - char* idPattern, - char* q, - char* geometry, - char* attrs, - bool local, - EntityMap* entityMap, - QNode** qNodeP, - OrionldGeoInfo* geoInfoP -); +DistOp* distOpRequestsForEntitiesPurge(char* idPattern, QNode* qNode) +{ + // FIXME: idPattern, qNode also need to be taken into account inside regMatchForEntitiesQuery + DistOp* auxiliarList = regMatchForEntitiesQuery(RegModeAuxiliary, DoPurgeEntity, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + DistOp* exclusiveList = regMatchForEntitiesQuery(RegModeExclusive, DoPurgeEntity, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + DistOp* redirectList = regMatchForEntitiesQuery(RegModeRedirect, DoPurgeEntity, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + // FIXME: Strip off attrs, entityId, entityType, etc from URI params (regMatchForEntitiesQuery(RegModeExclusive) already does it for each match + + DistOp* inclusiveList = regMatchForEntitiesQuery(RegModeInclusive, DoPurgeEntity, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + DistOp* distOpList; + + distOpList = distOpListsMerge(exclusiveList, redirectList); + distOpList = distOpListsMerge(distOpList, inclusiveList); + distOpList = distOpListsMerge(distOpList, auxiliarList); + + LM_W(("distOpList: %p", distOpList)); + return distOpList; +} @@ -122,7 +120,7 @@ extern bool pCheckQueryParams // // orionldDeleteEntities - // -// 01. Retrieve list of entity id+type from local database +// 01. Retrieve list of entity id+type from local database // 02. Create the array of alterations - for later comparison with subscriptions // 03. Create the array of DistOps // 04. Execute the array of DistOps @@ -212,14 +210,16 @@ bool orionldDeleteEntities(void) // // Distributed Ops // -#if 0 + DistOp* distOpList = distOpRequestsForEntitiesPurge(idPattern, qNode); + if (distOpList != NULL) { + distOpsSend(distOpList, orionldState.in.aerOS); distOpResponses(distOpList, responseBody); kjTreeLog(responseBody, "responseBody", LmtSR); distOpListRelease(distOpList); } -#endif + // // Delete the entities in the local DB @@ -245,8 +245,6 @@ bool orionldDeleteEntities(void) } } - // responseFix(responseBody, DoDeleteEntity, 204, entityId); - if (failures == 0) { orionldState.responseTree = NULL; diff --git a/src/lib/orionld/serviceRoutines/orionldGetEntities.cpp b/src/lib/orionld/serviceRoutines/orionldGetEntities.cpp index 66d6842c8d..1e900c875f 100644 --- a/src/lib/orionld/serviceRoutines/orionldGetEntities.cpp +++ b/src/lib/orionld/serviceRoutines/orionldGetEntities.cpp @@ -29,7 +29,6 @@ extern "C" } #include "logMsg/logMsg.h" // LM_* -#include "logMsg/traceLevels.h" // Lmt* #include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo #include "orionld/types/QNode.h" // QNode @@ -37,165 +36,24 @@ extern "C" #include "orionld/types/DistOp.h" // DistOp #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/orionldError.h" // orionldError +#include "orionld/q/qClone.h" // qClone #include "orionld/legacyDriver/legacyGetEntities.h" // legacyGetEntities #include "orionld/kjTree/kjTreeLog.h" // kjTreeLog -#include "orionld/q/qLex.h" // qLex -#include "orionld/q/qParse.h" // qParse -#include "orionld/q/qClone.h" // qClone -#include "orionld/payloadCheck/pCheckGeo.h" // pCheckGeo +#include "orionld/payloadCheck/pCheckQueryParams.h" // pCheckQueryParams #include "orionld/distOp/distOpRequests.h" // distOpRequests #include "orionld/distOp/distOpListsMerge.h" // distOpListsMerge #include "orionld/distOp/distOpListDebug.h" // distOpListDebug -#include "orionld/distOp/xForwardedForMatch.h" // xForwardedForMatchº +#include "orionld/distOp/xForwardedForMatch.h" // xForwardedForMatch #include "orionld/distOp/viaMatch.h" // viaMatch #include "orionld/distOp/distOpCreate.h" // distOpCreate #include "orionld/regMatch/regMatchOperation.h" // regMatchOperation -#include "orionld/regMatch/regMatchInformationArrayForQuery.h" // regMatchInformationArrayForQuery +#include "orionld/regMatch/regMatchForEntitiesQuery.h" // regMatchForEntitiesQuery #include "orionld/serviceRoutines/orionldGetEntitiesDistributed.h" // orionldGetEntitiesDistributed #include "orionld/serviceRoutines/orionldGetEntitiesLocal.h" // orionldGetEntitiesLocal #include "orionld/serviceRoutines/orionldGetEntities.h" // Own interface -// ---------------------------------------------------------------------------- -// -// qCheck - -// -static QNode* qCheck(char* qString) -{ - QNode* qList; - char* title; - char* detail; - - qList = qLex(qString, true, &title, &detail); - if (qList == NULL) - { - orionldError(OrionldBadRequestData, "Invalid Q-Filter", detail, 400); - LM_RE(NULL, ("Error (qLex: %s: %s)", title, detail)); - } - - QNode* qNode = qParse(qList, NULL, true, true, &title, &detail); // 3rd parameter: forDb=true - if (qNode == NULL) - { - orionldError(OrionldBadRequestData, title, detail, 400); - LM_E(("Error (qParse: %s: %s) - but, the subscription will be inserted in the sub-cache without 'q'", title, detail)); - } - - return qNode; -} - - - -// ----------------------------------------------------------------------------- -// -// pCheckQueryParams - -// -bool pCheckQueryParams(char* id, char* type, char* idPattern, char* q, char* geometry, char* attrs, bool local, EntityMap* entityMap, QNode** qNodeP, OrionldGeoInfo* geoInfoP) -{ - // - // URI param validity check - // - - if ((id == NULL) && - (idPattern == NULL) && - (type == NULL) && - (geometry == NULL) && - (attrs == NULL) && - (q == NULL) && - (local == false) && - (orionldState.in.entityMap == NULL)) - { - orionldError(OrionldBadRequestData, - "Too broad query", - "Need at least one of: entity-id, entity-type, geo-location, attribute-list, Q-filter, local=true, or an entity map", - 400); - - return false; - } - - - // - // If ONE or ZERO types in URI param 'type', the prepared array isn't used, just a simple char-pointer (named "type") - // - if (orionldState.in.typeList.items == 0) type = (char*) ".*"; - else if (orionldState.in.typeList.items == 1) type = orionldState.in.typeList.array[0]; - - if (pCheckGeo(geoInfoP, orionldState.uriParams.geometry, orionldState.uriParams.georel, orionldState.uriParams.coordinates, orionldState.uriParams.geoproperty) == false) - return false; - - QNode* qNode = NULL; - if (orionldState.uriParams.q != NULL) - { - qNode = qCheck(orionldState.uriParams.q); - if (qNode == NULL) - return false; - } - - *qNodeP = qNode; - - return true; -} - - - -// ----------------------------------------------------------------------------- -// -// regMatchForEntitiesQuery - FIXME: Move to orionld/forwarding/regMatchForEntitiesQuery.cpp/h -// -DistOp* regMatchForEntitiesQuery -( - RegistrationMode regMode, - StringArray* idListP, - StringArray* typeListP, - StringArray* attrListP -) -{ - DistOp* distOpList = NULL; - - for (RegCacheItem* regP = orionldState.tenantP->regCache->regList; regP != NULL; regP = regP->next) - { - if ((regP->mode & regMode) == 0) - continue; - - if (regMatchOperation(regP, DoQueryEntity) == false) - { - LM_T(LmtRegMatch, ("%s: No Reg Match due to Operation (operation == QueryEntity)", regP->regId)); - continue; - } - - // Loop detection - if (viaMatch(orionldState.in.via, regP->hostAlias) == true) - { - LM_T(LmtRegMatch, ("%s: No Reg Match due to Loop (Via)", regP->regId)); - continue; - } - - if (xForwardedForMatch(orionldState.in.xForwardedFor, regP->ipAndPort) == true) - { - LM_T(LmtRegMatch, ("%s: No Reg Match due to Loop (X-Forwarded-For)", regP->regId)); - continue; - } - - DistOp* distOpP = regMatchInformationArrayForQuery(regP, idListP, typeListP, attrListP); - if (distOpP == NULL) - { - LM_T(LmtRegMatch, ("%s: No Reg Match due to Information Array", regP->regId)); - continue; - } - - // - // Add distOpP to the linked list (distOpList) - // - LM_T(LmtRegMatch, ("%s: Reg Match !", regP->regId)); - - distOpList = distOpListsMerge(distOpList, distOpP); - } - - return distOpList; -} - - - // ---------------------------------------------------------------------------- // // distOpRequestsForEntitiesQuery - @@ -203,12 +61,12 @@ DistOp* regMatchForEntitiesQuery DistOp* distOpRequestsForEntitiesQuery(char* idPattern, QNode* qNode) { // FIXME: idPattern, qNode also need to be taken into account inside regMatchForEntitiesQuery - DistOp* auxiliarList = regMatchForEntitiesQuery(RegModeAuxiliary, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); - DistOp* exclusiveList = regMatchForEntitiesQuery(RegModeExclusive, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); - DistOp* redirectList = regMatchForEntitiesQuery(RegModeRedirect, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + DistOp* auxiliarList = regMatchForEntitiesQuery(RegModeAuxiliary, DoQueryEntity, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + DistOp* exclusiveList = regMatchForEntitiesQuery(RegModeExclusive, DoQueryEntity, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + DistOp* redirectList = regMatchForEntitiesQuery(RegModeRedirect, DoQueryEntity, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); // FIXME: Strip off attrs, entityId, entityType, etc from URI params (regMatchForEntitiesQuery(RegModeExclusive) already does it for each match - DistOp* inclusiveList = regMatchForEntitiesQuery(RegModeInclusive, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + DistOp* inclusiveList = regMatchForEntitiesQuery(RegModeInclusive, DoQueryEntity, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); DistOp* distOpList; distOpList = distOpListsMerge(exclusiveList, redirectList); @@ -228,7 +86,7 @@ DistOp* distOpRequestsForEntitiesQuery(char* idPattern, QNode* qNode) // // Three cases: // * legacy -// * local query (either by setting "local=true", or by "no matching registrations +// * local query (either by setting "local=true", or by "no matching registrations" // * distributed query (best effort, without freezing time) // // diff --git a/src/lib/orionld/types/DistOpType.cpp b/src/lib/orionld/types/DistOpType.cpp index f72cd36f64..33f200e36d 100644 --- a/src/lib/orionld/types/DistOpType.cpp +++ b/src/lib/orionld/types/DistOpType.cpp @@ -40,7 +40,7 @@ extern "C" // // distOpTypes - // -const char* distOpTypes[37] = { +const char* distOpTypes[38] = { "none", "createEntity", "updateEntity", @@ -124,7 +124,8 @@ const int distOpTypeUrlLen[37] = { 25, // strlen("/ngsi-ld/v1/subscriptions") 26, // strlen("/ngsi-ld/v1/subscriptions/") + strlen(subscriptionId) 26, // strlen("/ngsi-ld/v1/subscriptions/") + strlen(subscriptionId) - 26 // strlen("/ngsi-ld/v1/subscriptions/") + strlen(subscriptionId) + 26, // strlen("/ngsi-ld/v1/subscriptions/") + strlen(subscriptionId) + 20 // strlen("/ngsi-ld/v1/entities") }; diff --git a/src/lib/orionld/types/DistOpType.h b/src/lib/orionld/types/DistOpType.h index 8d49248315..5f4ca9e95f 100644 --- a/src/lib/orionld/types/DistOpType.h +++ b/src/lib/orionld/types/DistOpType.h @@ -79,7 +79,7 @@ typedef enum DistOpType DoQuerySubscription, // 35 DoDeleteSubscription, - DoPurgeEntities + DoPurgeEntity } DistOpType; @@ -88,7 +88,7 @@ typedef enum DistOpType // // distOpTypes - // -extern const char* distOpTypes[37]; +extern const char* distOpTypes[38]; diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_bad_verb_with_supported_url_path.test b/test/functionalTest/cases/0000_ngsild/ngsild_bad_verb_with_supported_url_path.test index 64b9c39f42..3d0278d55a 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_bad_verb_with_supported_url_path.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_bad_verb_with_supported_url_path.test @@ -30,12 +30,12 @@ orionldStart CB --SHELL-- # -# 01. PATCH /ngsi-ld/v1/entities, see 405 and Allow: POST,GET +# 01. PATCH /ngsi-ld/v1/entities, see 405 and Allow: POST,GET,DELETE # -echo "01. PATCH /ngsi-ld/v1/entities, see 405 and Allow: POST,GET" -echo "===========================================================" +echo "01. PATCH /ngsi-ld/v1/entities, see 405 and Allow: POST,GET,DELETE" +echo "==================================================================" payload='{ "id": "http://a.b.c/entity/E1", "type": "A", @@ -50,10 +50,10 @@ echo --REGEXPECT-- -01. PATCH /ngsi-ld/v1/entities, see 405 and Allow: POST,GET -=========================================================== +01. PATCH /ngsi-ld/v1/entities, see 405 and Allow: POST,GET,DELETE +================================================================== HTTP/1.1 405 Method Not Allowed -Allow: GET,POST +Allow: GET,POST,DELETE Content-Length: 0 Date: REGEX(.*) diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_delete_entities.test b/test/functionalTest/cases/0000_ngsild/ngsild_delete_entities.test index 29e17a2620..b35239ec2a 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_delete_entities.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_delete_entities.test @@ -25,10 +25,11 @@ Entity Batch Delete Test --SHELL-INIT-- dbInit CB -orionldStart CB +dbInit CP1 +orionldStart CB -experimental -forwarding +orionldStart CP1 -experimental accumulatorStart --pretty-print 127.0.0.1 ${LISTENER_PORT} - --SHELL-- # @@ -36,8 +37,12 @@ accumulatorStart --pretty-print 127.0.0.1 ${LISTENER_PORT} # 02. Create a subscription on entity type T1 # 03. Remove entities E1 and E3 using DELETE /ngsi-ld/v1/entities?type=T1 # 04. Get all entities - see E2 and E4 -# 05. Dump accumulator, see ONE notification with TWO deleted entities +# 05. Dump/Reset accumulator, see ONE notification with TWO deleted entities # 06. Attempt to remove entities with type T1 again - see 204 +# 07. Create entity urn:E5, of type T2, in CP1 +# 08. Create a registration for entity type T2, pointing to CP1 +# 09. Remove entities E2 and E4 using DELETE /ngsi-ld/v1/entities?type=T2 - provoke forwarded request to CP1 +# # echo "01. Create 4 entities: E1, E2, E3, and E4, odd ids with type T1, even with type T2" @@ -120,8 +125,8 @@ echo echo -echo "05. Dump accumulator, see ONE notification with TWO deleted entities" -echo "====================================================================" +echo "05. Dump/Reset accumulator, see ONE notification with TWO deleted entities" +echo "==========================================================================" accumulatorDump echo echo @@ -134,6 +139,63 @@ echo echo +echo "07. Create entity urn:E5, of type T2, in CP1" +echo "============================================" +payload='{ + "id": "urn:E5", + "type": "T2", + "A": { + "type": "Property", + "value": 5 + } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "08. Create a registration for entity type T2, pointing to CP1" +echo "=============================================================" +payload='{ + "id": "urn:R1", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T2" + } + ] + } + ], + "endpoint": "http://'$HOSTNAME':'$CP1_PORT'", + "operations": [ "purgeEntity" ] +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "09. Remove entities E2 and E4 using DELETE /ngsi-ld/v1/entities?type=T2 - provoke forwarded request to CP1" +echo "==========================================================================================================" +orionCurl --url /ngsi-ld/v1/entities?type=T2 -X DELETE +echo +echo + + +echo "10. GET all entities locally in CB - see empty array" +echo "====================================================" +orionCurl --url "/ngsi-ld/v1/entities?options=keyValues&local=true" +echo +echo + + +echo "11. GET all entities locally in CP1 - see empty array" +echo "=====================================================" +orionCurl --url "/ngsi-ld/v1/entities?options=keyValues&local=true" --port $CP1_PORT +echo +echo + --REGEXPECT-- 01. Create 4 entities: E1, E2, E3, and E4, odd ids with type T1, even with type T2 @@ -198,8 +260,8 @@ Link: /attrs/{attrId}/{instanceId} ==================================================================================== HTTP/1.1 405 Method Not Allowed -Allow: GET,PATCH,DELETE +Allow: GET,DELETE,PATCH Content-Length: 0 Date: REGEX(.*)