From 56e950df519be8bddd79c4db7331bc49b633107b Mon Sep 17 00:00:00 2001 From: Ken Zangelin Date: Mon, 26 Jun 2023 17:20:12 +0200 Subject: [PATCH 1/7] Framework for system attributes in notifications in place --- CHANGES_NEXT_RELEASE | 1 + src/lib/cache/CachedSubscription.h | 1 + src/lib/logMsg/traceLevels.h | 1 + .../common/subCacheApiSubscriptionInsert.cpp | 13 +- .../common/subCacheApiSubscriptionInsert.h | 6 +- .../dbModel/dbModelFromApiSubscription.cpp | 5 + .../dbModel/dbModelToApiSubscription.cpp | 34 +- .../dbModel/dbModelToApiSubscription.h | 18 +- .../kjTree/kjTreeFromCachedSubscription.cpp | 8 + .../mongoc/mongocSubCachePopulateByTenant.cpp | 25 +- .../notifications/notificationSend.cpp | 5 +- src/lib/orionld/payloadCheck/fieldPaths.cpp | 1 + src/lib/orionld/payloadCheck/fieldPaths.h | 1 + .../payloadCheck/pCheckNotification.cpp | 32 +- .../orionld/payloadCheck/pCheckNotification.h | 14 +- .../payloadCheck/pCheckSubscription.cpp | 41 +- .../orionld/payloadCheck/pCheckSubscription.h | 37 +- .../orionldGetSubscription.cpp | 17 +- .../orionldGetSubscriptions.cpp | 21 +- .../orionldPatchSubscription.cpp | 38 +- .../serviceRoutines/orionldPostEntities.cpp | 51 ++- .../orionldPostSubscriptions.cpp | 47 ++- src/lib/orionld/types/OrionldAlteration.h | 13 +- .../ngsild_subscription_with_sysAttrs.test | 353 ++++++++++++++++++ 24 files changed, 660 insertions(+), 123 deletions(-) create mode 100644 test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 8f979b3377..d265e1c6ea 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -4,3 +4,4 @@ Fixed issues: #1381 Bug in GeoJSON notifications #1385 Fixed a bug in PATCH /entities/{EID}/attrs, about patching a relationship object to an array (this is not supported by the NGSI-LD spec, but Orion-LD supports it anyway - for now) #1387 Part 1 of showChanges + previous values of attributes in notifications - for non-batch operations, non-distributed attributes, and non-delete operations + #1394 System Attributes in Notifications diff --git a/src/lib/cache/CachedSubscription.h b/src/lib/cache/CachedSubscription.h index e3780d6dbb..e0bc402f87 100644 --- a/src/lib/cache/CachedSubscription.h +++ b/src/lib/cache/CachedSubscription.h @@ -110,6 +110,7 @@ struct CachedSubscription char* qText; // Note that NGSIv2/mongoBackend q/mq are inside SubscriptionExpression KjNode* geoCoordinatesP; bool showChanges; + bool sysAttrs; bool isActive; std::string status; diff --git a/src/lib/logMsg/traceLevels.h b/src/lib/logMsg/traceLevels.h index b639677feb..5520fc5db9 100644 --- a/src/lib/logMsg/traceLevels.h +++ b/src/lib/logMsg/traceLevels.h @@ -106,6 +106,7 @@ typedef enum TraceLevels LmtCurl = 250, // CURL library LmtToDo, // To Do list LmtPatchEntity, // Real merge+patch + LmtSysAttrs, // System Attributes LmtLeak // Used when debugging leaks and valgrind errors } TraceLevels; diff --git a/src/lib/orionld/common/subCacheApiSubscriptionInsert.cpp b/src/lib/orionld/common/subCacheApiSubscriptionInsert.cpp index acff25b339..a0462e33a2 100644 --- a/src/lib/orionld/common/subCacheApiSubscriptionInsert.cpp +++ b/src/lib/orionld/common/subCacheApiSubscriptionInsert.cpp @@ -38,7 +38,7 @@ extern "C" #include "cache/CachedSubscription.h" // CachedSubscription #include "cache/subCache.h" // subCacheItemInsert -#include "common/RenderFormat.h" // stringToRenderFormat +#include "common/RenderFormat.h" // RenderFormat, stringToRenderFormat #include "orionld/q/QNode.h" // QNode #include "orionld/context/OrionldContext.h" // OrionldContext @@ -62,7 +62,9 @@ CachedSubscription* subCacheApiSubscriptionInsert KjNode* geoCoordinatesP, OrionldContext* contextP, const char* tenant, - KjNode* showChangesP + KjNode* showChangesP, + KjNode* sysAttrsP, + RenderFormat renderFormat ) { CachedSubscription* cSubP = new CachedSubscription(); @@ -74,6 +76,10 @@ CachedSubscription* subCacheApiSubscriptionInsert cSubP->ldContext = (contextP != NULL)? contextP->url : ""; cSubP->geoCoordinatesP = NULL; cSubP->showChanges = (showChangesP != NULL)? showChangesP->value.b : false; + cSubP->sysAttrs = (sysAttrsP != NULL)? sysAttrsP->value.b : false; + cSubP->renderFormat = renderFormat; + + LM_T(LmtSysAttrs, ("sysAttrs: %s", (cSubP->sysAttrs == true)? "true" : "false")); KjNode* subscriptionIdP = kjLookup(apiSubscriptionP, "_id"); // "id" was changed to "_id" by orionldPostSubscriptions to accomodate the DB insertion KjNode* subscriptionNameP = kjLookup(apiSubscriptionP, "subscriptionName"); // "name" is accepted too ... @@ -243,7 +249,7 @@ CachedSubscription* subCacheApiSubscriptionInsert if (notificationP != NULL) { KjNode* attributesP = kjLookup(notificationP, "attributes"); - KjNode* formatP = kjLookup(notificationP, "format"); + KjNode* formatP = kjLookup(notificationP, "format"); // FIXME: pCheckSubscription has already found this field. Can be removed KjNode* endpointP = kjLookup(notificationP, "endpoint"); if (attributesP != NULL) @@ -254,6 +260,7 @@ CachedSubscription* subCacheApiSubscriptionInsert } } + // FIXME: pCheckSubscription has already found the "notificatrion::format" field. Can be removed if (formatP != NULL) { cSubP->renderFormat = stringToRenderFormat(formatP->value.s, true); diff --git a/src/lib/orionld/common/subCacheApiSubscriptionInsert.h b/src/lib/orionld/common/subCacheApiSubscriptionInsert.h index 3c00ae37f9..47e5dc7e2b 100644 --- a/src/lib/orionld/common/subCacheApiSubscriptionInsert.h +++ b/src/lib/orionld/common/subCacheApiSubscriptionInsert.h @@ -30,7 +30,9 @@ extern "C" #include "kjson/KjNode.h" // KjNode } +#include "common/RenderFormat.h" // RenderFormat #include "cache/subCache.h" // CachedSubscription + #include "orionld/q/QNode.h" // QNode #include "orionld/context/OrionldContext.h" // OrionldContext @@ -47,7 +49,9 @@ extern CachedSubscription* subCacheApiSubscriptionInsert KjNode* geoCoordinatesP, OrionldContext* contextP, const char* tenant, - KjNode* showChangesP + KjNode* showChangesP, + KjNode* sysAttrsP, + RenderFormat renderFormat ); #endif // SRC_LIB_ORIONLD_COMMON_SUBCACHEAPISUBSCRIPTIONINSERT_H_ diff --git a/src/lib/orionld/dbModel/dbModelFromApiSubscription.cpp b/src/lib/orionld/dbModel/dbModelFromApiSubscription.cpp index 4772741f70..8ec86f340c 100644 --- a/src/lib/orionld/dbModel/dbModelFromApiSubscription.cpp +++ b/src/lib/orionld/dbModel/dbModelFromApiSubscription.cpp @@ -379,6 +379,11 @@ bool dbModelFromApiSubscription(KjNode* apiSubscriptionP, bool patch) kjChildRemove(notificationP, nItemP); kjChildAdd(apiSubscriptionP, nItemP); } + else if (strcmp(nItemP->name, "sysAttrs") == 0) + { + kjChildRemove(notificationP, nItemP); + kjChildAdd(apiSubscriptionP, nItemP); + } else if (strcmp(nItemP->name, "endpoint") == 0) { KjNode* uriP = kjLookup(nItemP, "uri"); diff --git a/src/lib/orionld/dbModel/dbModelToApiSubscription.cpp b/src/lib/orionld/dbModel/dbModelToApiSubscription.cpp index 9bbb73ed59..c43c56066d 100644 --- a/src/lib/orionld/dbModel/dbModelToApiSubscription.cpp +++ b/src/lib/orionld/dbModel/dbModelToApiSubscription.cpp @@ -30,6 +30,7 @@ extern "C" } #include "logMsg/logMsg.h" // LM_* +#include "common/RenderFormat.h" // RenderFormat #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/orionldError.h" // orionldError @@ -172,13 +173,15 @@ static bool notificationStatus(KjNode* dbLastSuccessP, KjNode* dbLastFailureP) // KjNode* dbModelToApiSubscription ( - KjNode* dbSubP, - const char* tenant, - bool forSubCache, - QNode** qNodePP, - KjNode** coordinatesPP, - KjNode** contextNodePP, - KjNode** showChangesP + KjNode* dbSubP, + const char* tenant, + bool forSubCache, + QNode** qNodePP, + KjNode** coordinatesPP, + KjNode** contextNodePP, + KjNode** showChangesP, + KjNode** sysAttrsP, + RenderFormat* renderFormatP ) { KjNode* dbSubIdP = kjLookup(dbSubP, "_id"); DB_ITEM_NOT_FOUND(dbSubIdP, "id", tenant); @@ -204,6 +207,7 @@ KjNode* dbModelToApiSubscription KjNode* dbLastSuccessP = kjLookup(dbSubP, "lastSuccess"); KjNode* dbLastFailureP = kjLookup(dbSubP, "lastFailure"); KjNode* dbShowChangesP = kjLookup(dbSubP, "showChanges"); + KjNode* dbSysAttrsP = kjLookup(dbSubP, "sysAttrs"); KjNode* dbCreatedAtP = NULL; KjNode* dbModifiedAtP = NULL; @@ -229,6 +233,10 @@ KjNode* dbModelToApiSubscription if ((dbShowChangesP != NULL) && (showChangesP != NULL)) *showChangesP = dbShowChangesP; + // sysAttrs + if ((dbSysAttrsP != NULL) && (sysAttrsP != NULL)) + *sysAttrsP = dbShowChangesP; + // // If dbSubIdP is a JSON Object, it's an NGSIv2 subscription and its "id" looks like this: // "id": { "$oid": "6290eafec8112b5716a931a7" } @@ -447,19 +455,29 @@ KjNode* dbModelToApiSubscription } if (dbFormatP != NULL) + { kjChildAdd(notificationP, dbFormatP); + *renderFormatP = stringToRenderFormat(dbFormatP->value.s); + } KjNode* endpointP = kjObject(orionldState.kjsonP, "endpoint"); kjChildAdd(notificationP, endpointP); - // notification::showChanges + // Notification::showChanges if ((dbShowChangesP != NULL) && (dbShowChangesP->value.b == true)) { KjNode* showChangesP = kjBoolean(orionldState.kjsonP, "showChanges", true); kjChildAdd(notificationP, showChangesP); } + // notification::sysAttrs + if ((dbSysAttrsP != NULL) && (dbSysAttrsP->value.b == true)) + { + KjNode* sysAttrsP = kjBoolean(orionldState.kjsonP, "sysAttrs", true); + kjChildAdd(notificationP, sysAttrsP); + } + // notification::status bool nStatus = notificationStatus(dbLastSuccessP, dbLastFailureP); KjNode* nStatusNodeP = kjString(orionldState.kjsonP, "status", (nStatus == true)? "ok" : "failed"); diff --git a/src/lib/orionld/dbModel/dbModelToApiSubscription.h b/src/lib/orionld/dbModel/dbModelToApiSubscription.h index ab170198de..256ecbf0c9 100644 --- a/src/lib/orionld/dbModel/dbModelToApiSubscription.h +++ b/src/lib/orionld/dbModel/dbModelToApiSubscription.h @@ -30,6 +30,8 @@ extern "C" #include "kjson/KjNode.h" // KjNode } +#include "common/RenderFormat.h" // RenderFormat + #include "orionld/q/QNode.h" // QNode @@ -40,13 +42,15 @@ extern "C" // extern KjNode* dbModelToApiSubscription ( - KjNode* dbSubP, - const char* tenant, - bool forSubCache, - QNode** qNodePP, - KjNode** coordinatesPP, - KjNode** contextNodePP, - KjNode** showChangesP + KjNode* dbSubP, + const char* tenant, + bool forSubCache, + QNode** qNodePP, + KjNode** coordinatesPP, + KjNode** contextNodePP, + KjNode** showChangesP, + KjNode** sysAttrsP, + RenderFormat* renderFormatP ); #endif // SRC_LIB_ORIONLD_DBMODEL_DBMODELTOAPISUBSCRIPTION_H_ diff --git a/src/lib/orionld/kjTree/kjTreeFromCachedSubscription.cpp b/src/lib/orionld/kjTree/kjTreeFromCachedSubscription.cpp index dc4a9c1305..9537f3b001 100644 --- a/src/lib/orionld/kjTree/kjTreeFromCachedSubscription.cpp +++ b/src/lib/orionld/kjTree/kjTreeFromCachedSubscription.cpp @@ -290,6 +290,14 @@ KjNode* kjTreeFromCachedSubscription(CachedSubscription* cSubP, bool sysAttrs, b kjChildAdd(notificationNodeP, nodeP); } + // notification::sysAttrs + if (cSubP->sysAttrs == true) + { + nodeP = kjBoolean(orionldState.kjsonP, "sysAttrs", true); + NULL_CHECK(nodeP); + kjChildAdd(notificationNodeP, nodeP); + } + // notification::endpoint KjNode* endpointNodeP = kjObject(orionldState.kjsonP, "endpoint"); NULL_CHECK(endpointNodeP); diff --git a/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp b/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp index 79d0d104d8..fede4a67d4 100644 --- a/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp +++ b/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp @@ -74,8 +74,7 @@ bool mongocSubCachePopulateByTenant(OrionldTenant* tenantP) // bson_init(&mongoFilter); - mongoc_client_t* connectionP = mongoc_client_pool_pop(mongocPool); - + mongoc_client_t* connectionP = mongoc_client_pool_pop(mongocPool); mongoc_collection_t* subscriptionsP = mongoc_client_get_collection(connectionP, tenantP->mongoDbName, "csubs"); // @@ -99,11 +98,21 @@ bool mongocSubCachePopulateByTenant(OrionldTenant* tenantP) continue; } - QNode* qTree = NULL; - KjNode* contextNodeP = NULL; - KjNode* coordinatesP = NULL; - KjNode* showChangesP = NULL; - KjNode* apiSubP = dbModelToApiSubscription(dbSubP, tenantP->tenant, true, &qTree, &coordinatesP, &contextNodeP, &showChangesP); + QNode* qTree = NULL; + KjNode* contextNodeP = NULL; + KjNode* coordinatesP = NULL; + KjNode* showChangesP = NULL; + KjNode* sysAttrsP = NULL; + RenderFormat renderFormat = RF_NORMALIZED; + KjNode* apiSubP = dbModelToApiSubscription(dbSubP, + tenantP->tenant, + true, + &qTree, + &coordinatesP, + &contextNodeP, + &showChangesP, + &sysAttrsP, + &renderFormat); if (apiSubP == NULL) continue; @@ -112,7 +121,7 @@ bool mongocSubCachePopulateByTenant(OrionldTenant* tenantP) if (contextNodeP != NULL) contextP = orionldContextFromUrl(contextNodeP->value.s, NULL); - subCacheApiSubscriptionInsert(apiSubP, qTree, coordinatesP, contextP, tenantP->tenant, showChangesP); + subCacheApiSubscriptionInsert(apiSubP, qTree, coordinatesP, contextP, tenantP->tenant, showChangesP, sysAttrsP, renderFormat); } mongoc_client_pool_push(mongocPool, connectionP); diff --git a/src/lib/orionld/notifications/notificationSend.cpp b/src/lib/orionld/notifications/notificationSend.cpp index 4bea390a81..fef2f1d519 100644 --- a/src/lib/orionld/notifications/notificationSend.cpp +++ b/src/lib/orionld/notifications/notificationSend.cpp @@ -556,8 +556,7 @@ KjNode* notificationTree(OrionldAlterationMatch* matchList) for (OrionldAlterationMatch* matchP = matchList; matchP != NULL; matchP = matchP->next) { - kjTreeLog(matchP->altP->finalApiEntityP, "matchP->altP->finalApiEntityP", LmtSR); - KjNode* apiEntityP = matchP->altP->finalApiEntityP; + KjNode* apiEntityP = (subP->sysAttrs == false)? matchP->altP->finalApiEntityP : matchP->altP->finalApiEntityWithSysAttrsP; // If the entity is already in "data", and, it's not a BATCH Operation, skip - already there if (orionldState.serviceP->isBatchOp == false) @@ -578,7 +577,6 @@ KjNode* notificationTree(OrionldAlterationMatch* matchList) if (matchP->subP->attributes.size() > 0) apiEntityP = attributeFilter(apiEntityP, matchP); - kjTreeLog(apiEntityP, "apiEntityP before entityFix", LmtSR); apiEntityP = entityFix(apiEntityP, subP); kjChildAdd(dataNodeP, apiEntityP); } @@ -634,6 +632,7 @@ int notificationSend(OrionldAlterationMatch* mAltP, double timestamp, CURL** cur // KjNode* notificationP = (ngsiv2 == false)? notificationTree(mAltP) : notificationTreeForNgsiV2(mAltP); char* preferHeader = NULL; + if ((ngsiv2 == false) && (mAltP->subP->httpInfo.mimeType == GEOJSON)) { char* geometryProperty = (char*) mAltP->subP->expression.geoproperty.c_str(); diff --git a/src/lib/orionld/payloadCheck/fieldPaths.cpp b/src/lib/orionld/payloadCheck/fieldPaths.cpp index eb859fa626..98d0771f24 100644 --- a/src/lib/orionld/payloadCheck/fieldPaths.cpp +++ b/src/lib/orionld/payloadCheck/fieldPaths.cpp @@ -46,6 +46,7 @@ const char* SubscriptionNotificationAttributesPath = "Subscription::notifica const char* SubscriptionNotificationAttributesItemPath = "Subscription::notification::attributes[X]"; const char* SubscriptionNotificationEndpointPath = "Subscription::notification::endpoint"; const char* SubscriptionNotificationShowChangesPath = "Subscription::notification::showChanges"; +const char* SubscriptionNotificationSysAttrsPath = "Subscription::notification::sysAttrs"; const char* SubscriptionQPath = "Subscription::q"; const char* SubscriptionGeoqPath = "Subscription::geoQ"; const char* SubscriptionIsActivePath = "Subscription::isActive"; diff --git a/src/lib/orionld/payloadCheck/fieldPaths.h b/src/lib/orionld/payloadCheck/fieldPaths.h index dd3ad3bf29..274502887a 100644 --- a/src/lib/orionld/payloadCheck/fieldPaths.h +++ b/src/lib/orionld/payloadCheck/fieldPaths.h @@ -44,6 +44,7 @@ extern const char* SubscriptionNotificationAttributesPath; extern const char* SubscriptionNotificationAttributesItemPath; extern const char* SubscriptionNotificationEndpointPath; extern const char* SubscriptionNotificationShowChangesPath; +extern const char* SubscriptionNotificationSysAttrsPath; extern const char* SubscriptionWatchedAttributesPath; extern const char* SubscriptionWatchedAttributesItemPath; extern const char* SubscriptionQPath; diff --git a/src/lib/orionld/payloadCheck/pCheckNotification.cpp b/src/lib/orionld/payloadCheck/pCheckNotification.cpp index 17cdafc700..f5bf56aa52 100644 --- a/src/lib/orionld/payloadCheck/pCheckNotification.cpp +++ b/src/lib/orionld/payloadCheck/pCheckNotification.cpp @@ -28,8 +28,8 @@ extern "C" } #include "logMsg/logMsg.h" // LM_* - #include "common/RenderFormat.h" // RenderFormat, stringToRenderFormat + #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/orionldError.h" // orionldError #include "orionld/context/orionldAttributeExpand.h" // orionldAttributeExpand @@ -44,12 +44,23 @@ extern "C" // // pCheckNotification - // -bool pCheckNotification(KjNode* notificationP, bool patch, KjNode** uriPP, KjNode** notifierInfoPP, bool* mqttChangeP, KjNode** showChangesOutP) +bool pCheckNotification +( + KjNode* notificationP, + bool patch, + KjNode** uriPP, + KjNode** notifierInfoPP, + bool* mqttChangeP, + KjNode** showChangesOutP, + KjNode** sysAttrsOutP, + RenderFormat* renderFormatP +) { KjNode* attributesP = NULL; KjNode* formatP = NULL; KjNode* endpointP = NULL; KjNode* showChangesP = NULL; + KjNode* sysAttrsP = NULL; PCHECK_OBJECT(notificationP, 0, NULL, SubscriptionNotificationPath, 400); PCHECK_OBJECT_EMPTY(notificationP, 0, NULL, SubscriptionNotificationPath, 400); @@ -80,6 +91,7 @@ bool pCheckNotification(KjNode* notificationP, bool patch, KjNode** uriPP, KjNod orionldError(OrionldBadRequestData, "Invalid value for 'Subscription::notification::format'", formatP->value.s, 400); return false; } + *renderFormatP = rf; } else if (strcmp(nItemP->name, "endpoint") == 0) { @@ -96,6 +108,13 @@ bool pCheckNotification(KjNode* notificationP, bool patch, KjNode** uriPP, KjNod PCHECK_BOOL(showChangesP, 0, NULL, SubscriptionNotificationShowChangesPath, 400); *showChangesOutP = showChangesP; } + else if (strcmp(nItemP->name, "sysAttrs") == 0) + { + PCHECK_DUPLICATE(sysAttrsP, nItemP, 0, NULL, SubscriptionNotificationSysAttrsPath, 400); + PCHECK_BOOL(sysAttrsP, 0, NULL, SubscriptionNotificationSysAttrsPath, 400); + *sysAttrsOutP = sysAttrsP; + LM_T(LmtSysAttrs, ("Found a 'sysAttrs' in Subscription::notification (%s)", (sysAttrsP->value.b == true)? "true" : "false")); + } else if (strcmp(nItemP->name, "status") == 0) {} // Ignored else if (strcmp(nItemP->name, "timesSent") == 0) {} // Ignored else if (strcmp(nItemP->name, "lastNotification") == 0) {} // Ignored @@ -114,5 +133,14 @@ bool pCheckNotification(KjNode* notificationP, bool patch, KjNode** uriPP, KjNod return false; } + if ((formatP != NULL) && (sysAttrsP != NULL)) + { + if ((sysAttrsP->value.b == true) && (*renderFormatP == RF_KEYVALUES)) + { + orionldError(OrionldBadRequestData, "Inconsistent fields in Subscription (format=simplified + sysAttrs=true)", SubscriptionNotificationSysAttrsPath, 400); + return false; + } + } + return true; } diff --git a/src/lib/orionld/payloadCheck/pCheckNotification.h b/src/lib/orionld/payloadCheck/pCheckNotification.h index 22eb3ead62..84c23c7226 100644 --- a/src/lib/orionld/payloadCheck/pCheckNotification.h +++ b/src/lib/orionld/payloadCheck/pCheckNotification.h @@ -30,12 +30,24 @@ extern "C" #include "kjson/KjNode.h" // KjNode } +#include "common/RenderFormat.h" // RenderFormat + // ----------------------------------------------------------------------------- // // pCheckNotification - // -extern bool pCheckNotification(KjNode* notificationP, bool patch, KjNode** uriPP, KjNode** notifierInfoPP, bool* mqttChangeP, KjNode** showChangesP); +extern bool pCheckNotification +( + KjNode* notificationP, + bool patch, + KjNode** uriPP, + KjNode** notifierInfoPP, + bool* mqttChangeP, + KjNode** showChangesOutP, + KjNode** sysAttrsOutP, + RenderFormat* renderFormatP +); #endif // SRC_LIB_ORIONLD_PAYLOADCHECK_PCHECKNOTIFICATION_H_ diff --git a/src/lib/orionld/payloadCheck/pCheckSubscription.cpp b/src/lib/orionld/payloadCheck/pCheckSubscription.cpp index f1794cf416..a20662c161 100644 --- a/src/lib/orionld/payloadCheck/pCheckSubscription.cpp +++ b/src/lib/orionld/payloadCheck/pCheckSubscription.cpp @@ -30,6 +30,7 @@ extern "C" } #include "logMsg/logMsg.h" // LM_* +#include "common/RenderFormat.h" // RenderFormat #include "orionld/q/QNode.h" // QNode #include "orionld/q/qBuild.h" // qBuild @@ -96,22 +97,24 @@ extern "C" // bool pCheckSubscription ( - KjNode* subP, - bool isCreate, // true if POST, false if PATCH - char* subscriptionId, // non-NULL if PATCH - KjNode* idP, - KjNode* typeP, - KjNode** endpointP, - KjNode** qNodeP, - QNode** qTreeP, - char** qRenderedForDbP, - bool* qValidForV2P, - bool* qIsMqP, - KjNode** uriPP, - KjNode** notifierInfoPP, - KjNode** geoCoordinatesPP, - bool* mqttChangeP, - KjNode** showChangesP + KjNode* subP, + bool isCreate, // true if POST, false if PATCH + char* subscriptionId, // non-NULL if PATCH + KjNode* idP, + KjNode* typeP, + KjNode** endpointP, + KjNode** qNodeP, + QNode** qTreeP, + char** qRenderedForDbP, + bool* qValidForV2P, + bool* qIsMqP, + KjNode** uriPP, + KjNode** notifierInfoPP, + KjNode** geoCoordinatesPP, + bool* mqttChangeP, + KjNode** showChangesP, + KjNode** sysAttrsP, + RenderFormat* renderFormatP ) { PCHECK_OBJECT(subP, 0, NULL, "A Subscription must be a JSON Object", 400); @@ -129,6 +132,10 @@ bool pCheckSubscription KjNode* langP = NULL; double expiresAt; + *mqttChangeP = NULL; + *showChangesP = NULL; + *sysAttrsP = NULL; + if (idP != NULL) { PCHECK_STRING(idP, 0, NULL, SubscriptionIdPath, 400); @@ -231,7 +238,7 @@ bool pCheckSubscription { PCHECK_OBJECT(subItemP, 0, NULL, SubscriptionNotificationPath, 400); PCHECK_DUPLICATE(notificationP, subItemP, 0, NULL, SubscriptionNotificationPath, 400); - if (pCheckNotification(notificationP, isCreate == false, uriPP, notifierInfoPP, mqttChangeP, showChangesP) == false) + if (pCheckNotification(notificationP, isCreate == false, uriPP, notifierInfoPP, mqttChangeP, showChangesP, sysAttrsP, renderFormatP) == false) return false; } else if ((strcmp(subItemP->name, "expiresAt") == 0) || (strcmp(subItemP->name, "expires") == 0)) diff --git a/src/lib/orionld/payloadCheck/pCheckSubscription.h b/src/lib/orionld/payloadCheck/pCheckSubscription.h index 48ecebddcf..b8c3087640 100644 --- a/src/lib/orionld/payloadCheck/pCheckSubscription.h +++ b/src/lib/orionld/payloadCheck/pCheckSubscription.h @@ -30,6 +30,9 @@ extern "C" #include "kjson/KjNode.h" // KjNode } +#include "common/RenderFormat.h" // RenderFormat +#include "orionld/q//QNode.h" // QNode + // ----------------------------------------------------------------------------- @@ -38,22 +41,24 @@ extern "C" // extern bool pCheckSubscription ( - KjNode* subP, - bool isCreate, // true if POST, false if PATCH - char* subscriptionId, // non-NULL if PATCH - KjNode* idP, - KjNode* typeP, - KjNode** endpointP, - KjNode** qNodeP, - QNode** qTreeP, - char** qTextP, - bool* qValidForV2P, - bool* qIsMqP, - KjNode** uriPP, - KjNode** notifierInfoPP, - KjNode** geoCoordinatesPP, - bool* mqttChangeP, - KjNode** showChangesP + KjNode* subP, + bool isCreate, // true if POST, false if PATCH + char* subscriptionId, // non-NULL if PATCH + KjNode* idP, + KjNode* typeP, + KjNode** endpointP, + KjNode** qNodeP, + QNode** qTreeP, + char** qTextP, + bool* qValidForV2P, + bool* qIsMqP, + KjNode** uriPP, + KjNode** notifierInfoPP, + KjNode** geoCoordinatesPP, + bool* mqttChangeP, + KjNode** showChangesP, + KjNode** sysAttrsP, + RenderFormat* renderFormatP ); #endif // SRC_LIB_ORIONLD_PAYLOADCHECK_PCHECKSUBSCRIPTION_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldGetSubscription.cpp b/src/lib/orionld/serviceRoutines/orionldGetSubscription.cpp index 614bada8b6..496002d845 100644 --- a/src/lib/orionld/serviceRoutines/orionldGetSubscription.cpp +++ b/src/lib/orionld/serviceRoutines/orionldGetSubscription.cpp @@ -144,9 +144,20 @@ static bool orionldGetSubscriptionFromDb(void) return false; } - KjNode* contextNodeP; // Not used, but dbModelToApiSubscription requires it - KjNode* coordinatesNodeP; // Not used, but dbModelToApiSubscription requires it - KjNode* apiSubP = dbModelToApiSubscription(dbSubP, orionldState.tenantP->tenant, false, NULL, &coordinatesNodeP, &contextNodeP, NULL); + KjNode* coordinatesNodeP = NULL; // Not needed here, but dbModelToApiSubscription requires it + KjNode* contextNodeP = NULL; // Not needed here, but dbModelToApiSubscription requires it + KjNode* showChangesP = NULL; // Not needed here, but dbModelToApiSubscription requires it + KjNode* sysAttrsP = NULL; // Not needed here, but dbModelToApiSubscription requires it + RenderFormat renderFormat = RF_NORMALIZED; // Not needed here, but dbModelToApiSubscription requires it + KjNode* apiSubP = dbModelToApiSubscription(dbSubP, + orionldState.tenantP->tenant, + false, + NULL, + &coordinatesNodeP, + &contextNodeP, + &showChangesP, + &sysAttrsP, + &renderFormat); if (apiSubP == NULL) { diff --git a/src/lib/orionld/serviceRoutines/orionldGetSubscriptions.cpp b/src/lib/orionld/serviceRoutines/orionldGetSubscriptions.cpp index fde59b512a..611e1ee585 100644 --- a/src/lib/orionld/serviceRoutines/orionldGetSubscriptions.cpp +++ b/src/lib/orionld/serviceRoutines/orionldGetSubscriptions.cpp @@ -29,8 +29,8 @@ extern "C" } #include "logMsg/logMsg.h" // LM_* - #include "cache/subCache.h" // CachedSubscription, subCacheHeadGet, subCacheItemLookup +#include "common/RenderFormat.h" // RenderFormat #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/orionldError.h" // orionldError @@ -107,10 +107,21 @@ static bool orionldGetSubscriptionsFromDb(void) // for (KjNode* dbSubP = dbSubV->value.firstChildP; dbSubP != NULL; dbSubP = dbSubP->next) { - QNode* qTree = NULL; - KjNode* contextNodeP = NULL; - KjNode* coordinatesP = NULL; - KjNode* apiSubP = dbModelToApiSubscription(dbSubP, orionldState.tenantP->tenant, false, &qTree, &coordinatesP, &contextNodeP, NULL); + QNode* qTree = NULL; + KjNode* contextNodeP = NULL; + KjNode* coordinatesP = NULL; + KjNode* showChangesP = NULL; + KjNode* sysAttrsP = NULL; + RenderFormat renderFormat = RF_NORMALIZED; + KjNode* apiSubP = dbModelToApiSubscription(dbSubP, + orionldState.tenantP->tenant, + false, + &qTree, + &coordinatesP, + &contextNodeP, + &showChangesP, + &sysAttrsP, + &renderFormat); if (apiSubP == NULL) { diff --git a/src/lib/orionld/serviceRoutines/orionldPatchSubscription.cpp b/src/lib/orionld/serviceRoutines/orionldPatchSubscription.cpp index fc5919ca20..d61ca31ee3 100644 --- a/src/lib/orionld/serviceRoutines/orionldPatchSubscription.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPatchSubscription.cpp @@ -802,22 +802,24 @@ bool orionldPatchSubscription(void) { PCHECK_URI(orionldState.wildcard[0], true, 0, "Subscription ID must be a valid URI", orionldState.wildcard[0], 400); - char* subscriptionId = orionldState.wildcard[0]; - KjNode* qP = NULL; - KjNode* geoqP = kjLookup(orionldState.requestTree, "geoQ"); - KjNode* geoCoordinatesP = NULL; - bool mqttChange = false; - KjNode* subTree = orionldState.requestTree; - KjNode* idNode = orionldState.payloadIdNode; - KjNode* typeNode = orionldState.payloadTypeNode; - QNode* qNodeP = NULL; - char* qRenderedForDb = NULL; - bool qValidForV2 = false; - bool qIsMq = false; - KjNode* uriP = NULL; - KjNode* notifierInfoP = NULL; - KjNode* showChangesP = NULL; - bool r; + char* subscriptionId = orionldState.wildcard[0]; + KjNode* qP = NULL; + KjNode* geoqP = kjLookup(orionldState.requestTree, "geoQ"); + KjNode* geoCoordinatesP = NULL; + bool mqttChange = false; + KjNode* subTree = orionldState.requestTree; + KjNode* idNode = orionldState.payloadIdNode; + KjNode* typeNode = orionldState.payloadTypeNode; + QNode* qNodeP = NULL; + char* qRenderedForDb = NULL; + bool qValidForV2 = false; + bool qIsMq = false; + KjNode* uriP = NULL; + KjNode* notifierInfoP = NULL; + KjNode* showChangesP = NULL; + KjNode* sysAttrsP = NULL; + RenderFormat renderFormat = RF_NORMALIZED; + bool r; r = pCheckSubscription(subTree, false, @@ -834,7 +836,9 @@ bool orionldPatchSubscription(void) ¬ifierInfoP, &geoCoordinatesP, &mqttChange, - &showChangesP); + &showChangesP, + &sysAttrsP, + &renderFormat); if (r == false) { if (qNodeP != NULL) diff --git a/src/lib/orionld/serviceRoutines/orionldPostEntities.cpp b/src/lib/orionld/serviceRoutines/orionldPostEntities.cpp index 40a793599e..5ec5ba91c6 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostEntities.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostEntities.cpp @@ -65,6 +65,35 @@ extern "C" +// ----------------------------------------------------------------------------- +// +// sysAttrsToEntity - +// +void sysAttrsToEntity(KjNode* apiEntityP) +{ + KjNode* createdAtP = kjString(orionldState.kjsonP, "createdAt", orionldState.requestTimeString); + KjNode* modifiedAtP = kjString(orionldState.kjsonP, "modifiedAt", orionldState.requestTimeString); + + kjChildAdd(apiEntityP, createdAtP); + kjChildAdd(apiEntityP, modifiedAtP); + + // Loop over all attributes + for (KjNode* attrP = apiEntityP->value.firstChildP; attrP != NULL; attrP = attrP->next) + { + // Not an object? Not an attribute! (Arrays(datasetId) comes later) + if (attrP->type != KjObject) + continue; + + KjNode* createdAtP = kjString(orionldState.kjsonP, "createdAt", orionldState.requestTimeString); + KjNode* modifiedAtP = kjString(orionldState.kjsonP, "modifiedAt", orionldState.requestTimeString); + + kjChildAdd(attrP, createdAtP); + kjChildAdd(attrP, modifiedAtP); + } +} + + + // ---------------------------------------------------------------------------- // // orionldPostEntities - @@ -212,14 +241,20 @@ bool orionldPostEntities(void) // Prepare for notifications // orionldState.alterations = (OrionldAlteration*) kaAlloc(&orionldState.kalloc, sizeof(OrionldAlteration)); - orionldState.alterations->entityId = entityId; - orionldState.alterations->entityType = entityType; - orionldState.alterations->inEntityP = apiEntityP; - orionldState.alterations->dbEntityP = NULL; - orionldState.alterations->finalApiEntityP = apiEntityP; // entity id, createdAt, modifiedAt ... - orionldState.alterations->alteredAttributes = 0; - orionldState.alterations->alteredAttributeV = NULL; - orionldState.alterations->next = NULL; + orionldState.alterations->entityId = entityId; + orionldState.alterations->entityType = entityType; + orionldState.alterations->inEntityP = apiEntityP; + orionldState.alterations->dbEntityP = NULL; + orionldState.alterations->finalApiEntityP = apiEntityP; + orionldState.alterations->finalApiEntityWithSysAttrsP = kjClone(orionldState.kjsonP, apiEntityP); // Later we add createdAt+modifiedAt + orionldState.alterations->alteredAttributes = 0; + orionldState.alterations->alteredAttributeV = NULL; + orionldState.alterations->next = NULL; + + // + // Must add the sysAttrs to the "Final API Entity" as subscriptions may have "notification::sysAttrs" set to true ... + // + sysAttrsToEntity(orionldState.alterations->finalApiEntityWithSysAttrsP); if (cloneForTroeP != NULL) orionldState.requestTree = cloneForTroeP; diff --git a/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp b/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp index 220b068eba..2342d97820 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp @@ -42,7 +42,6 @@ extern "C" #include "orionld/common/subCacheApiSubscriptionInsert.h" // subCacheApiSubscriptionInsert #include "orionld/legacyDriver/legacyPostSubscriptions.h" // legacyPostSubscriptions #include "orionld/kjTree/kjChildPrepend.h" // kjChildPrepend -#include "orionld/kjTree/kjTreeLog.h" // kjTreeLog #include "orionld/dbModel/dbModelFromApiSubscription.h" // dbModelFromApiSubscription #include "orionld/mongoc/mongocSubscriptionExists.h" // mongocSubscriptionExists #include "orionld/mongoc/mongocSubscriptionInsert.h" // mongocSubscriptionInsert @@ -68,21 +67,23 @@ bool orionldPostSubscriptions(void) if ((experimental == false) || (orionldState.in.legacy != NULL)) return legacyPostSubscriptions(); // this will be removed!! (after thorough testing) - KjNode* subP = orionldState.requestTree; - KjNode* subIdP = orionldState.payloadIdNode; - KjNode* endpointP = NULL; - KjNode* ldqNodeP = NULL; - KjNode* uriP = NULL; - KjNode* notifierInfoP = NULL; - KjNode* geoCoordinatesP = NULL; - QNode* qTree = NULL; - char* qRenderedForDb = NULL; - bool mqtt = false; - char* subId = NULL; - bool b = false; - bool qValidForV2 = false; - bool qIsMq = false; - KjNode* showChangesP = NULL; + KjNode* subP = orionldState.requestTree; + KjNode* subIdP = orionldState.payloadIdNode; + KjNode* endpointP = NULL; + KjNode* ldqNodeP = NULL; + KjNode* uriP = NULL; + KjNode* notifierInfoP = NULL; + KjNode* geoCoordinatesP = NULL; + QNode* qTree = NULL; + char* qRenderedForDb = NULL; + bool mqtt = false; + char* subId = NULL; + bool b = false; + bool qValidForV2 = false; + bool qIsMq = false; + KjNode* showChangesP = NULL; + KjNode* sysAttrsP = NULL; + RenderFormat renderFormat = RF_NORMALIZED; b = pCheckSubscription(subP, true, @@ -99,7 +100,10 @@ bool orionldPostSubscriptions(void) ¬ifierInfoP, &geoCoordinatesP, &mqtt, - &showChangesP); + &showChangesP, + &sysAttrsP, + &renderFormat); + if (qRenderedForDb != NULL) LM_T(LmtSR, ("qRenderedForDb: '%s'", qRenderedForDb)); @@ -246,7 +250,14 @@ bool orionldPostSubscriptions(void) } // sub to cache - BEFORE we change the tree to be according to the DB Model (as the DB model might change some day ...) - CachedSubscription* cSubP = subCacheApiSubscriptionInsert(subP, qTree, geoCoordinatesP, orionldState.contextP, orionldState.tenantP->tenant, showChangesP); + CachedSubscription* cSubP = subCacheApiSubscriptionInsert(subP, + qTree, + geoCoordinatesP, + orionldState.contextP, + orionldState.tenantP->tenant, + showChangesP, + sysAttrsP, + renderFormat); // dbModel KjNode* dbSubscriptionP = subP; diff --git a/src/lib/orionld/types/OrionldAlteration.h b/src/lib/orionld/types/OrionldAlteration.h index 64f9c47a18..de1701a088 100644 --- a/src/lib/orionld/types/OrionldAlteration.h +++ b/src/lib/orionld/types/OrionldAlteration.h @@ -80,12 +80,13 @@ typedef struct OrionldAlteration { char* entityId; char* entityType; - KjNode* inEntityP; // Incoming payload body - normalized, expanded and perhaps complimented from DB (for merge+patch) - KjNode* dbEntityP; // To be REMOVED (and perhaps put back later - for 'previousValue') - KjNode* finalApiEntityP; // Final complete API entity - after the modification is applied - KjNode* deletedAttrV; // Used for Replace operations - to inform about attributes that have been removed after a REPLACE - OrionldAttributeAlteration* alteredAttributeV; // Linked list of all altered attributes - for merge+patch - int alteredAttributes; // No of altered attributes in alteredAttributeV - for merge+patch + KjNode* inEntityP; // Incoming payload body - normalized, expanded and perhaps complimented from DB (for merge+patch) + KjNode* dbEntityP; // To be REMOVED (and perhaps put back later - for 'previousValue') + KjNode* finalApiEntityP; // Final complete API entity - after the modification is applied + KjNode* finalApiEntityWithSysAttrsP; // finalApiEntityP + sysAttrs + KjNode* deletedAttrV; // Used for Replace operations - to inform about attributes that have been removed after a REPLACE + OrionldAttributeAlteration* alteredAttributeV; // Linked list of all altered attributes - for merge+patch + int alteredAttributes; // Number of altered attributes in alteredAttributeV - for merge+patch struct OrionldAlteration* next; } OrionldAlteration; diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test new file mode 100644 index 0000000000..46446a0d01 --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test @@ -0,0 +1,353 @@ +# Copyright 2023 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 + +# VALGRIND_READY - to mark the test ready for valgrindTestSuite.sh + +--NAME-- +Subscription with sysAttrs + +--SHELL-INIT-- +dbInit CB +orionldStart CB -experimental +accumulatorStart --pretty-print + +--SHELL-- + +# +# 01. Attempt to create a subscription with sysAttrs AND format==simplified - see error +# 02. Attempt to create a subscription with a non-bool sysAttrs - see error +# 03. Create a subscription with sysAttrs set to true +# 04. GET the subscription and see the sysAttrs=true +# 05. GET the subscription from DB and see the sysAttrs=true +# 06. See the subscription in the database - sysAttrs=true on top level +# 07. POST /entities - Create an entity matching the subscription +# 08. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +# 09. PATCH /entities/urn:E1/attrs/P1 +# 10. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +# 11. PATCH /entities/urn:E1 +# 12. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +# 13. PATCH /entities/urn:E1/attrs +# 14. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +# 15. POST /entities/urn:E1/attrs +# 16. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +# 17. PUT /ngsi-ld/v1/entities/urn:E1/attrs/P1 +# 18. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +# 19. PUT /ngsi-ld/v1/entities/urn:E1 +# 20. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +# + +echo "01. Attempt to create a subscription with sysAttrs AND format==simplified - see error" +echo "=====================================================================================" +payload='{ + "type": "Subscription", + "entities": [ + { + "type": "T" + } + ], + "notification": { + "sysAttrs": true, + "format": "simplified", + "endpoint": { + "uri": "http://localhost:'$LISTENER_PORT'/notify" + } + } +}' +orionCurl --url /ngsi-ld/v1/subscriptions --payload "$payload" +echo +echo + + +echo "02. Attempt to create a subscription with a non-bool sysAttrs - see error" +echo "=========================================================================" +payload='{ + "type": "Subscription", + "entities": [ + { + "type": "T" + } + ], + "notification": { + "sysAttrs": 14, + "endpoint": { + "uri": "http://localhost:'$LISTENER_PORT'/notify" + } + } +}' +orionCurl --url /ngsi-ld/v1/subscriptions --payload "$payload" +echo +echo + + +echo "03. Create a subscription with sysAttrs set to true" +echo "===================================================" +payload='{ + "type": "Subscription", + "id": "urn:S1", + "entities": [ + { + "type": "T" + } + ], + "notification": { + "sysAttrs": true, + "endpoint": { + "uri": "http://localhost:'$LISTENER_PORT'/notify" + } + } +}' +orionCurl --url /ngsi-ld/v1/subscriptions --payload "$payload" +echo +echo + + +echo "04. GET the subscription and see the sysAttrs=true" +echo "==================================================" +orionCurl --url /ngsi-ld/v1/subscriptions/urn:S1 +echo +echo + + +echo "05. GET the subscription from DB and see the sysAttrs=true" +echo "==========================================================" +orionCurl --url /ngsi-ld/v1/subscriptions/urn:S1?options=fromDb +echo +echo + + +echo "06. See the subscription in the database - sysAttrs=true on top level" +echo "=====================================================================" +mongoCmd2 ftest "db.csubs.findOne()" +echo +echo + + +echo "07. POST /entities - Create an entity matching the subscription" +echo "===============================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": { + "type": "Property", + "value": 127 + } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "08. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt)" +echo "===========================================================================================" +accumulatorDump +accumulatorReset +echo +echo + + +--REGEXPECT-- +01. Attempt to create a subscription with sysAttrs AND format==simplified - see error +===================================================================================== +HTTP/1.1 400 Bad Request +Content-Length: 191 +Content-Type: application/json +Date: REGEX(.*) + +{ + "detail": "Subscription::notification::sysAttrs", + "title": "Inconsistent fields in Subscription (format=simplified + sysAttrs=true)", + "type": "https://uri.etsi.org/ngsi-ld/errors/BadRequestData" +} + + +02. Attempt to create a subscription with a non-bool sysAttrs - see error +========================================================================= +HTTP/1.1 400 Bad Request +Content-Length: 138 +Content-Type: application/json +Date: REGEX(.*) + +{ + "detail": "Subscription::notification::sysAttrs", + "title": "Not a JSON Boolean", + "type": "https://uri.etsi.org/ngsi-ld/errors/BadRequestData" +} + + +03. Create a subscription with sysAttrs set to true +=================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/subscriptions/urn:S1 + + + +04. GET the subscription and see the sysAttrs=true +================================================== +HTTP/1.1 200 OK +Content-Length: 261 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + +{ + "entities": [ + { + "type": "T" + } + ], + "id": "urn:S1", + "isActive": true, + "notification": { + "endpoint": { + "accept": "application/json", + "uri": "http://localhost:9997/notify" + }, + "format": "normalized", + "status": "ok", + "sysAttrs": true + }, + "origin": "cache", + "status": "active", + "type": "Subscription" +} + + +05. GET the subscription from DB and see the sysAttrs=true +========================================================== +HTTP/1.1 200 OK +Content-Length: 264 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + +{ + "entities": [ + { + "type": "T" + } + ], + "id": "urn:S1", + "isActive": true, + "notification": { + "endpoint": { + "accept": "application/json", + "uri": "http://localhost:9997/notify" + }, + "format": "normalized", + "status": "ok", + "sysAttrs": true + }, + "origin": "database", + "status": "active", + "type": "Subscription" +} + + +06. See the subscription in the database - sysAttrs=true on top level +===================================================================== +MongoDB shell version REGEX(.*) +connecting to: REGEX(.*) +MongoDB server version: REGEX(.*) +{ + "_id" : "urn:S1", + "entities" : [ + { + "type" : "https://uri.etsi.org/ngsi-ld/default-context/T", + "id" : ".*", + "isPattern" : "true", + "isTypePattern" : false + } + ], + "createdAt" : REGEX(.*), + "modifiedAt" : REGEX(.*), + "throttling" : 0, + "expression" : { + "geometry" : "", + "coords" : "", + "georel" : "", + "geoproperty" : "", + "q" : "", + "mq" : "" + }, + "sysAttrs" : true, + "reference" : "http://localhost:9997/notify", + "mimeType" : "application/json", + "attrs" : [ ], + "format" : "normalized", + "conditions" : [ ], + "status" : "active", + "custom" : false, + "servicePath" : "/#", + "blacklist" : false, + "ldContext" : "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld" +} +bye + + +07. POST /entities - Create an entity matching the subscription +=============================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +08. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +=========================================================================================== +POST http://REGEX(.*)/notify?subscriptionId=urn:S1 +Content-Length: 389 +User-Agent: REGEX(.*) +Host: REGEX(.*) +Accept: application/json +Content-Type: application/json +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" +Ngsild-Attribute-Format: Normalized + +{ + "data": [ + { + "P1": { + "createdAt": "202REGEX(.*)", + "modifiedAt": "202REGEX(.*)", + "type": "Property", + "value": 127 + }, + "createdAt": "202REGEX(.*)", + "id": "urn:E1", + "modifiedAt": "202REGEX(.*)", + "type": "T" + } + ], + "id": "urn:ngsi-ld:Notification:REGEX(.*)", + "notifiedAt": "REGEX(.*)", + "subscriptionId": "urn:S1", + "type": "Notification" +} +======================================= + + +--TEARDOWN-- +brokerStop CB +accumulatorStop +dbDrop CB +dbDrop CB openiot From 0668003fdd00948241fb4319d0118aad4d35c3c5 Mon Sep 17 00:00:00 2001 From: Ken Zangelin Date: Mon, 26 Jun 2023 18:36:02 +0200 Subject: [PATCH 2/7] PATCH Attribute now supports sysAttrs in notifications --- src/lib/orionld/notifications/CMakeLists.txt | 1 + src/lib/orionld/notifications/alteration.cpp | 4 +- src/lib/orionld/notifications/alteration.h | 2 +- .../notifications/notificationSend.cpp | 4 + .../orionld/notifications/sysAttrsStrip.cpp | 78 +++++++++++++++++++ src/lib/orionld/notifications/sysAttrsStrip.h | 41 ++++++++++ .../serviceRoutines/orionldPatchAttribute.cpp | 9 ++- .../ngsild_subscription_with_sysAttrs.test | 59 ++++++++++++++ 8 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 src/lib/orionld/notifications/sysAttrsStrip.cpp create mode 100644 src/lib/orionld/notifications/sysAttrsStrip.h diff --git a/src/lib/orionld/notifications/CMakeLists.txt b/src/lib/orionld/notifications/CMakeLists.txt index 150fd18adc..57cac4e80e 100644 --- a/src/lib/orionld/notifications/CMakeLists.txt +++ b/src/lib/orionld/notifications/CMakeLists.txt @@ -34,6 +34,7 @@ SET (SOURCES previousValues.cpp previousValuePopulate.cpp previousValueAdd.cpp + sysAttrsStrip.cpp ) # Include directories diff --git a/src/lib/orionld/notifications/alteration.cpp b/src/lib/orionld/notifications/alteration.cpp index adac33a17c..6b4c42449d 100644 --- a/src/lib/orionld/notifications/alteration.cpp +++ b/src/lib/orionld/notifications/alteration.cpp @@ -41,7 +41,7 @@ extern "C" // // alteration - // -void alteration(const char* entityId, const char* entityType, KjNode* apiEntityP, KjNode* incomingP, KjNode* dbEntityBeforeP) +OrionldAlteration* alteration(const char* entityId, const char* entityType, KjNode* apiEntityP, KjNode* incomingP, KjNode* dbEntityBeforeP) { OrionldAlteration* alterationP = (OrionldAlteration*) kaAlloc(&orionldState.kalloc, sizeof(OrionldAlteration)); @@ -71,4 +71,6 @@ void alteration(const char* entityId, const char* entityType, KjNode* apiEntityP orionldState.alterationsTail = alterationP; LM_T(LmtAlt, ("Added alteration for entity '%s'", entityId)); + + return alterationP; } diff --git a/src/lib/orionld/notifications/alteration.h b/src/lib/orionld/notifications/alteration.h index 3671b992bf..e41f9f33ce 100644 --- a/src/lib/orionld/notifications/alteration.h +++ b/src/lib/orionld/notifications/alteration.h @@ -33,6 +33,6 @@ // // alteration - // -extern void alteration(const char* entityId, const char* entityType, KjNode* apiEntityP, KjNode* incomingP, KjNode* dbEntityBeforeP); +extern OrionldAlteration* alteration(const char* entityId, const char* entityType, KjNode* apiEntityP, KjNode* incomingP, KjNode* dbEntityBeforeP); #endif // SRC_LIB_ORIONLD_NOTIFICATIONS_ALTERATION_H_ diff --git a/src/lib/orionld/notifications/notificationSend.cpp b/src/lib/orionld/notifications/notificationSend.cpp index fef2f1d519..704fadd2af 100644 --- a/src/lib/orionld/notifications/notificationSend.cpp +++ b/src/lib/orionld/notifications/notificationSend.cpp @@ -558,6 +558,10 @@ KjNode* notificationTree(OrionldAlterationMatch* matchList) { KjNode* apiEntityP = (subP->sysAttrs == false)? matchP->altP->finalApiEntityP : matchP->altP->finalApiEntityWithSysAttrsP; + LM_T(LmtSysAttrs, ("sysAttrs:%s, apiEntityP at %p", (subP->sysAttrs == true)? "true" : "false", apiEntityP)); + if (apiEntityP == NULL) + apiEntityP = matchP->altP->finalApiEntityP; // Temporary !!! + // If the entity is already in "data", and, it's not a BATCH Operation, skip - already there if (orionldState.serviceP->isBatchOp == false) { diff --git a/src/lib/orionld/notifications/sysAttrsStrip.cpp b/src/lib/orionld/notifications/sysAttrsStrip.cpp new file mode 100644 index 0000000000..a4882fe78c --- /dev/null +++ b/src/lib/orionld/notifications/sysAttrsStrip.cpp @@ -0,0 +1,78 @@ +/* +* +* Copyright 2023 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 // NULL + +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjLookup.h" // kjLookup +#include "kjson/kjBuilder.h" // kjChildRemove +} + +#include "orionld/notifications/sysAttrsStrip.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// kjFieldRemove - +// +static inline void kjFieldRemove(KjNode* tree, const char* fieldName) +{ + KjNode* toRemove = kjLookup(tree, fieldName); + + if (toRemove != NULL) + kjChildRemove(tree, toRemove); +} + + + +// ----------------------------------------------------------------------------- +// +// sysAttrsStrip - +// +void sysAttrsStrip(KjNode* finalApiEntityWithoutSysAttrsP) +{ + kjFieldRemove(finalApiEntityWithoutSysAttrsP, "createdAt"); + kjFieldRemove(finalApiEntityWithoutSysAttrsP, "modifiedAt"); + + for (KjNode* attrP = finalApiEntityWithoutSysAttrsP->value.firstChildP; attrP != NULL; attrP = attrP->next) + { + if (attrP->type == KjObject) // FIXME: or Array if datasetId + { + kjFieldRemove(attrP, "createdAt"); + kjFieldRemove(attrP, "modifiedAt"); + + for (KjNode* subAttrP = attrP->value.firstChildP; subAttrP != NULL; subAttrP = subAttrP->next) + { + if (subAttrP->type == KjObject) + { + kjFieldRemove(subAttrP, "createdAt"); + kjFieldRemove(subAttrP, "modifiedAt"); + } + } + } + } +} diff --git a/src/lib/orionld/notifications/sysAttrsStrip.h b/src/lib/orionld/notifications/sysAttrsStrip.h new file mode 100644 index 0000000000..ecbe00aa41 --- /dev/null +++ b/src/lib/orionld/notifications/sysAttrsStrip.h @@ -0,0 +1,41 @@ +#ifndef SRC_LIB_ORIONLD_NOTIFICATIONS_SYSATTRSSTRIP_H_ +#define SRC_LIB_ORIONLD_NOTIFICATIONS_SYSATTRSSTRIP_H_ + +/* +* +* Copyright 2023 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 +} + + + +// ----------------------------------------------------------------------------- +// +// sysAttrsStrip - +// +extern void sysAttrsStrip(KjNode* finalApiEntityWithoutSysAttrsP); + +#endif // SRC_LIB_ORIONLD_NOTIFICATIONS_SYSATTRSSTRIP_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldPatchAttribute.cpp b/src/lib/orionld/serviceRoutines/orionldPatchAttribute.cpp index 489b1e4b5a..e50421ea79 100644 --- a/src/lib/orionld/serviceRoutines/orionldPatchAttribute.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPatchAttribute.cpp @@ -55,6 +55,7 @@ extern "C" #include "orionld/forwarding/distOpResponses.h" // distOpResponses #include "orionld/forwarding/distOpListRelease.h" // distOpListRelease #include "orionld/notifications/alteration.h" // alteration +#include "orionld/notifications/sysAttrsStrip.h" // sysAttrsStrip #include "orionld/notifications/previousValuePopulate.h" // previousValuePopulate #include "orionld/serviceRoutines/orionldPatchAttribute.h" // Own interface @@ -427,7 +428,7 @@ bool orionldPatchAttribute(void) if (entityType != NULL) { - KjNode* finalApiEntityP = dbModelToApiEntity2(dbEntityP, false, RF_NORMALIZED, NULL, false, &orionldState.pd); + KjNode* finalApiEntityWithSysAttrsP = dbModelToApiEntity2(dbEntityP, true, RF_NORMALIZED, NULL, false, &orionldState.pd); // We need the "Incoming Entity", we have just an attribute ... KjNode* attributeNodeP = incomingP; @@ -440,7 +441,11 @@ bool orionldPatchAttribute(void) kjChildAdd(inEntityP, attributeNodeP); - alteration(entityId, entityType, finalApiEntityP, inEntityP, initialDbEntityP); + KjNode* finalApiEntityWithoutSysAttrsP = kjClone(orionldState.kjsonP, finalApiEntityWithSysAttrsP); + sysAttrsStrip(finalApiEntityWithoutSysAttrsP); + + OrionldAlteration* alterationP = alteration(entityId, entityType, finalApiEntityWithoutSysAttrsP, inEntityP, initialDbEntityP); + alterationP->finalApiEntityWithSysAttrsP = finalApiEntityWithSysAttrsP; } else LM_E(("Database Error (no _id::type in the DB for entity '%s')", entityId)); diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test index 46446a0d01..ba56c0b190 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test @@ -162,6 +162,24 @@ echo echo +echo "09. PATCH /entities/urn:E1/attrs/P1" +echo "===================================" +payload='{ + "value": 9 +}' +orionCurl --url /ngsi-ld/v1/entities/urn:E1/attrs/P1 --payload "$payload" -X PATCH +echo +echo + + +echo "10. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt)" +echo "===========================================================================================" +accumulatorDump +accumulatorReset +echo +echo + + --REGEXPECT-- 01. Attempt to create a subscription with sysAttrs AND format==simplified - see error ===================================================================================== @@ -346,6 +364,47 @@ Ngsild-Attribute-Format: Normalized ======================================= +09. PATCH /entities/urn:E1/attrs/P1 +=================================== +HTTP/1.1 204 No Content +Date: REGEX(.*) + + + +10. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +=========================================================================================== +POST http://REGEX(.*)/notify?subscriptionId=urn:S1 +Content-Length: 387 +User-Agent: orionld/REGEX(.*) +Host: REGEX(.*) +Accept: application/json +Content-Type: application/json +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" +Ngsild-Attribute-Format: Normalized + +{ + "data": [ + { + "P1": { + "createdAt": "202REGEX(.*)Z", + "modifiedAt": "202REGEX(.*)Z", + "type": "Property", + "value": 9 + }, + "createdAt": "202REGEX(.*)Z", + "id": "urn:E1", + "modifiedAt": "202REGEX(.*)Z", + "type": "T" + } + ], + "id": "urn:ngsi-ld:Notification:REGEX(.*)", + "notifiedAt": "202REGEX(.*)Z", + "subscriptionId": "urn:S1", + "type": "Notification" +} +======================================= + + --TEARDOWN-- brokerStop CB accumulatorStop From 835ebf29685950fa55c9b1513207cf463009b3df Mon Sep 17 00:00:00 2001 From: Ken Zangelin Date: Wed, 28 Jun 2023 22:25:22 +0200 Subject: [PATCH 3/7] sysAttrs in notifications triggered by PATCH /entities/{EID}/attrs --- src/lib/logMsg/traceLevels.h | 1 + .../orionld/dbModel/dbModelToApiAttribute.cpp | 14 ++- .../serviceRoutines/orionldPatchEntity.cpp | 62 ++++++--- .../serviceRoutines/orionldPatchEntity2.cpp | 4 + .../ngsild_subscription_with_sysAttrs.test | 119 +++++++++++++++++- test/functionalTest/testHarness.sh | 14 +++ 6 files changed, 187 insertions(+), 27 deletions(-) diff --git a/src/lib/logMsg/traceLevels.h b/src/lib/logMsg/traceLevels.h index 5520fc5db9..094a9123c8 100644 --- a/src/lib/logMsg/traceLevels.h +++ b/src/lib/logMsg/traceLevels.h @@ -106,6 +106,7 @@ typedef enum TraceLevels LmtCurl = 250, // CURL library LmtToDo, // To Do list LmtPatchEntity, // Real merge+patch + LmtPatchEntity2, // Real merge+patch: merging for final API Entity, for notifications LmtSysAttrs, // System Attributes LmtLeak // Used when debugging leaks and valgrind errors } TraceLevels; diff --git a/src/lib/orionld/dbModel/dbModelToApiAttribute.cpp b/src/lib/orionld/dbModel/dbModelToApiAttribute.cpp index 90e2612969..33f6658328 100644 --- a/src/lib/orionld/dbModel/dbModelToApiAttribute.cpp +++ b/src/lib/orionld/dbModel/dbModelToApiAttribute.cpp @@ -80,11 +80,15 @@ void dbModelToApiAttribute(KjNode* dbAttrP, bool sysAttrs, bool eqsForDots) { if ((sysAttrs == true) && (ix > 2)) { - char* dateTimeBuf = kaAlloc(&orionldState.kalloc, 32); - numberToDate(dbAttrP->value.f, dateTimeBuf, 32); - dbAttrP->name = (char*) ngsildName[ix]; - dbAttrP->value.s = dateTimeBuf; - dbAttrP->type = KjString; + nodeP->name = (char*) ngsildName[ix]; + + if (dbAttrP->type == KjFloat) + { + char* dateTimeBuf = kaAlloc(&orionldState.kalloc, 32); + numberToDate(dbAttrP->value.f, dateTimeBuf, 32); + nodeP->value.s = dateTimeBuf; + nodeP->type = KjString; + } } else kjChildRemove(dbAttrP, nodeP); diff --git a/src/lib/orionld/serviceRoutines/orionldPatchEntity.cpp b/src/lib/orionld/serviceRoutines/orionldPatchEntity.cpp index 3bbe15ad47..8ab31b305e 100644 --- a/src/lib/orionld/serviceRoutines/orionldPatchEntity.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPatchEntity.cpp @@ -62,6 +62,7 @@ extern "C" #include "orionld/forwarding/distOpFailure.h" // distOpFailure #include "orionld/notifications/alteration.h" // alteration #include "orionld/notifications/previousValuePopulate.h" // previousValuePopulate +#include "orionld/notifications/sysAttrsStrip.h" // sysAttrsStrip #include "orionld/kjTree/kjSort.h" // kjStringArraySort #include "orionld/kjTree/kjChildCount.h" // kjChildCount #include "orionld/serviceRoutines/orionldPatchEntity.h" // Own interface @@ -84,12 +85,24 @@ static void attributesMerge(KjNode* finalApiEntityP, KjNode* apiAttrs) { for (KjNode* apiAttrP = apiAttrs->value.firstChildP; apiAttrP != NULL; apiAttrP = apiAttrP->next) { - KjNode* oldAttrP = kjLookup(finalApiEntityP, apiAttrP->name); + KjNode* oldAttrP = kjLookup(finalApiEntityP, apiAttrP->name); + KjNode* createdAtP = NULL; + KjNode* modifiedAtP = NULL; if (oldAttrP != NULL) + { kjChildRemove(finalApiEntityP, oldAttrP); + createdAtP = kjLookup(oldAttrP, "createdAt"); + modifiedAtP = kjLookup(oldAttrP, "modifiedAt"); + } - kjChildAdd(finalApiEntityP, kjClone(orionldState.kjsonP, apiAttrP)); + KjNode* attrCopy = kjClone(orionldState.kjsonP, apiAttrP); + kjChildAdd(finalApiEntityP, attrCopy); + + if (createdAtP != NULL) + kjChildAdd(attrCopy, createdAtP); + if (modifiedAtP != NULL) + kjChildAdd(attrCopy, modifiedAtP); } } @@ -219,14 +232,16 @@ bool orionldPatchEntity(void) if ((experimental == false) || (orionldState.in.legacy != NULL)) // If Legacy header - use old implementation return legacyPatchEntity(); - KjNode* incomingP = NULL; - KjNode* finalApiEntityP = NULL; - KjNode* dbEntityCopy = NULL; - DistOp* distOpList = NULL; - char* entityId = orionldState.wildcard[0]; - char* entityType = orionldState.uriParams.type; - KjNode* attrP; - KjNode* next; + KjNode* finalApiEntityWithSysAttrs = NULL; + KjNode* finalApiEntityP = NULL; + OrionldAlteration* alterationP = NULL; + KjNode* incomingP = NULL; + KjNode* dbEntityCopy = NULL; + DistOp* distOpList = NULL; + char* entityId = orionldState.wildcard[0]; + char* entityType = orionldState.uriParams.type; + KjNode* attrP; + KjNode* next; // // Initial Validity Checks; @@ -247,7 +262,6 @@ bool orionldPatchEntity(void) return false; } - // // If entity type is present in the payload body, it must be a String and identical to the entity type in the database. // [ It is already extracted (by orionldMhdConnectionTreat) and checked for String ] @@ -287,15 +301,17 @@ bool orionldPatchEntity(void) goto done; } + dbEntityCopy = kjClone(orionldState.kjsonP, dbEntityP); // dbModelToApiEntity2 is DESTRUCTIVE + // // Transform the remaining attributes to DB format, for storage in local database // But before that, as this is DESTRUCTIVE, save a copy of the attributes for Alterations and TRoE // incomingP = kjClone(orionldState.kjsonP, orionldState.requestTree); // For Alterations and TRoE - attrP = orionldState.requestTree->value.firstChildP; - LM_T(LmtShowChanges, ("Looping over modified attributes")); + + attrP = orionldState.requestTree->value.firstChildP; while (attrP != NULL) { next = attrP->next; @@ -303,6 +319,7 @@ bool orionldPatchEntity(void) LM_T(LmtShowChanges, ("Modified attribute: '%s'", attrP->name)); if (attributeLookup(dbAttrsP, attrP->name) == false) { + LM_T(LmtSR, ("Removing attribute '%s' from the incoming tree", attrP->name)); kjChildRemove(orionldState.requestTree, attrP); distOpFailure(responseBody, NULL, "Attribute Not Found", NULL, 404, attrP->name); @@ -318,7 +335,7 @@ bool orionldPatchEntity(void) dotForEq(eqName); previousValuePopulate(dbAttrsP, NULL, eqName); - dbModelFromApiAttribute(attrP, dbAttrsP, NULL, NULL, NULL, true); + dbModelFromApiAttribute(attrP, dbAttrsP, NULL, NULL, NULL, true); // Removes creDate for attributes } attrP = next; @@ -362,7 +379,6 @@ bool orionldPatchEntity(void) distOpSuccess(responseBody, &local, NULL); } - // // For alteration() we need: // - Initial DB entity, before the merge (dbEntityP) @@ -370,9 +386,10 @@ bool orionldPatchEntity(void) // - Final API Entity, for q, etc AND for the Notifications (finalApiEntityP) // Note; the Final API Entity needs to be Expanded and Normalized - each subscription may have a different context to for compaction // - dbEntityCopy = kjClone(orionldState.kjsonP, dbEntityP); // dbModelToApiEntity2 is DESTRUCTIVE - finalApiEntityP = dbModelToApiEntity2(dbEntityCopy, false, RF_NORMALIZED, NULL, false, &orionldState.pd); - if (finalApiEntityP == NULL) + + + finalApiEntityWithSysAttrs = dbModelToApiEntity2(dbEntityCopy, true, RF_NORMALIZED, NULL, false, &orionldState.pd); + if (finalApiEntityWithSysAttrs == NULL) { // There will be no notifications for this update :( - Well. cause something important failed ... LM_E(("dbModelToApiEntity2: %s: %s", orionldState.pd.title, orionldState.pd.detail)); @@ -383,8 +400,13 @@ bool orionldPatchEntity(void) // // Notifications // - attributesMerge(finalApiEntityP, incomingP); - alteration(entityId, entityType, finalApiEntityP, incomingP, dbEntityP); + attributesMerge(finalApiEntityWithSysAttrs, incomingP); + + finalApiEntityP = kjClone(orionldState.kjsonP, finalApiEntityWithSysAttrs); + sysAttrsStrip(finalApiEntityP); + + alterationP = alteration(entityId, entityType, finalApiEntityP, incomingP, dbEntityP); + alterationP->finalApiEntityWithSysAttrsP = finalApiEntityWithSysAttrs; // // For TRoE we need: diff --git a/src/lib/orionld/serviceRoutines/orionldPatchEntity2.cpp b/src/lib/orionld/serviceRoutines/orionldPatchEntity2.cpp index 4c0e8a50a8..d60865e8fe 100644 --- a/src/lib/orionld/serviceRoutines/orionldPatchEntity2.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPatchEntity2.cpp @@ -614,6 +614,10 @@ bool orionldPatchEntity2(void) bool dbUpdateResult = false; KjNode* requestForLocal = (orionldState.requestTree != NULL)? kjClone(orionldState.kjsonP, orionldState.requestTree) : NULL; + // + // Anything left for local database? + // Remember: distOpRequests() removes all attributes that match exclusive/redirect regiostrations + // if (requestForLocal != NULL) { orionldState.alterations = orionldAlterations(entityId, entityType, orionldState.requestTree, dbAttrsP, false); diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test index ba56c0b190..cd827c20d2 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test @@ -42,11 +42,13 @@ accumulatorStart --pretty-print # 09. PATCH /entities/urn:E1/attrs/P1 # 10. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) # 11. PATCH /entities/urn:E1 -# 12. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +# 12. See the notification in the accumulator, BUT NOTE that sysAttrs haven't been implemented for PATCH Entity # 13. PATCH /entities/urn:E1/attrs # 14. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) # 15. POST /entities/urn:E1/attrs # 16. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +# +# Later: # 17. PUT /ngsi-ld/v1/entities/urn:E1/attrs/P1 # 18. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) # 19. PUT /ngsi-ld/v1/entities/urn:E1 @@ -180,6 +182,42 @@ echo echo +echo "11. PATCH /entities/urn:E1" +echo "==========================" +payload='{ + "P1": 11 +}' +orionCurl --url /ngsi-ld/v1/entities/urn:E1 --payload "$payload" -X PATCH +echo +echo + + +echo "12. See the notification in the accumulator, BUT NOTE that sysAttrs haven't been implemented for PATCH Entity" +echo "=============================================================================================================" +accumulatorDump +accumulatorReset +echo +echo + + +echo "13. PATCH /entities/urn:E1/attrs" +echo "================================" +payload='{ + "P1": 13 +}' +orionCurl --url /ngsi-ld/v1/entities/urn:E1/attrs --payload "$payload" -X PATCH +echo +echo + + +echo "14. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt)" +echo "===========================================================================================" +accumulatorDump +accumulatorReset +echo +echo + + --REGEXPECT-- 01. Attempt to create a subscription with sysAttrs AND format==simplified - see error ===================================================================================== @@ -405,8 +443,85 @@ Ngsild-Attribute-Format: Normalized ======================================= +11. PATCH /entities/urn:E1 +========================== +HTTP/1.1 204 No Content +Date: REGEX(.*) + + + +12. See the notification in the accumulator, BUT NOTE that sysAttrs haven't been implemented for PATCH Entity +============================================================================================================= +POST http://REGEX(.*)/notify?subscriptionId=urn:S1 +Content-Length: 230 +User-Agent: orionld/REGEX(.*) +Host: tux +Accept: application/json +Content-Type: application/json +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" +Ngsild-Attribute-Format: Normalized + +{ + "data": [ + { + "P1": { + "type": "Property", + "value": 11 + }, + "id": "urn:E1", + "type": "T" + } + ], + "id": "urn:ngsi-ld:Notification:REGEX(.*)", + "notifiedAt": "202REGEX(.*)Z", + "subscriptionId": "urn:S1", + "type": "Notification" +} +======================================= + + +13. PATCH /entities/urn:E1/attrs +================================ +HTTP/1.1 204 No Content +Date: REGEX(.*) + + + +14. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +=========================================================================================== +POST http://REGEX(.*)/notify?subscriptionId=urn:S1 +Content-Length: 388 +User-Agent: orionld/REGEX(.*) +Host: tux +Accept: application/json +Content-Type: application/json +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" +Ngsild-Attribute-Format: Normalized + +{ + "data": [ + { + "P1": { + "createdAt": "202REGEX(.*)Z", + "modifiedAt": "202REGEX(.*)Z", + "type": "Property", + "value": 13 + }, + "createdAt": "202REGEX(.*)Z", + "id": "urn:E1", + "modifiedAt": "202REGEX(.*)Z", + "type": "T" + } + ], + "id": "urn:ngsi-ld:Notification:REGEX(.*)", + "notifiedAt": "202REGEX(.*)Z", + "subscriptionId": "urn:S1", + "type": "Notification" +} +======================================= + + --TEARDOWN-- brokerStop CB accumulatorStop dbDrop CB -dbDrop CB openiot diff --git a/test/functionalTest/testHarness.sh b/test/functionalTest/testHarness.sh index 898339320c..463194d72a 100755 --- a/test/functionalTest/testHarness.sh +++ b/test/functionalTest/testHarness.sh @@ -195,6 +195,7 @@ function usage() echo "$empty [--cache (force broker to be started without the option --noCache)]" echo "$empty [--noThreadpool (do not use a threadpool, unless specified by a test case. If not set, a thread pool of 200:20 is used by default in test cases which do not set notificationMode options)]" echo "$empty [ ]*" + echo "$empty [--errors (show errors from last run)]" echo echo "* Please note that if a directory is passed as parameter, its entire path must be given, not only the directory-name" echo "* If a file is passed as parameter, its entire file-name must be given, including '.test'" @@ -403,6 +404,18 @@ logMsg "$ME, in directory $SCRIPT_HOME" +# ------------------------------------------------------------------------------ +# +# showErrorsdAndExit - +# +function showErrorsdAndExit() +{ + grep ERROR /tmp/functest.old + exit 0 +} + + + # ------------------------------------https://github.com/telefonicaid/fiware-orion/pull/394#discussion_r13321709------------------------------------------ # # Argument parsing @@ -465,6 +478,7 @@ do elif [ "$1" == "--noCache" ]; then noCache=ON; elif [ "$1" == "--cache" ]; then noCache=OFF; elif [ "$1" == "--noThreadpool" ]; then threadpool=OFF; + elif [ "$1" == "--errors" ]; then showErrorsdAndExit; else if [ "$dirOrFile" == "" ] then From 6e1b1d722e41c8739dd584253c94952694f93134 Mon Sep 17 00:00:00 2001 From: Ken Zangelin Date: Wed, 28 Jun 2023 22:31:54 +0200 Subject: [PATCH 4/7] sysAttrs in notifications triggered by POST /entities/{EID}/attrs --- .../serviceRoutines/orionldPostEntity.cpp | 9 ++- .../ngsild_subscription_with_sysAttrs.test | 59 +++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/lib/orionld/serviceRoutines/orionldPostEntity.cpp b/src/lib/orionld/serviceRoutines/orionldPostEntity.cpp index fc5841f8b8..d4f12d1c7a 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostEntity.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostEntity.cpp @@ -49,6 +49,7 @@ extern "C" #include "orionld/mongoc/mongocAttributesAdd.h" // mongocAttributesAdd #include "orionld/notifications/alteration.h" // alteration #include "orionld/notifications/previousValues.h" // previousValues +#include "orionld/notifications/sysAttrsStrip.h" // sysAttrsStrip #include "orionld/forwarding/distOpRequests.h" // distOpRequests #include "orionld/forwarding/distOpResponses.h" // distOpResponses #include "orionld/forwarding/distOpListRelease.h" // distOpListRelease @@ -297,9 +298,11 @@ bool orionldPostEntity(void) dbAttrsMerge(dbAttrsP, dbAttrsUpdate, orionldState.uriParamOptions.noOverwrite == false); OrionldProblemDetails pd; - KjNode* finalApiEntityP = dbModelToApiEntity2(dbEntityP, false, RF_NORMALIZED, orionldState.uriParams.lang, false, &pd); - - alteration(entityId, entityType, finalApiEntityP, orionldState.requestTree, initialDbEntityP); + KjNode* finalApiEntityWithSysAttrs = dbModelToApiEntity2(dbEntityP, true, RF_NORMALIZED, orionldState.uriParams.lang, false, &pd); + KjNode* finalApiEntity = kjClone(orionldState.kjsonP, finalApiEntityWithSysAttrs); + sysAttrsStrip(finalApiEntity); + OrionldAlteration* alterationP = alteration(entityId, entityType, finalApiEntity, orionldState.requestTree, initialDbEntityP); + alterationP->finalApiEntityWithSysAttrsP = finalApiEntityWithSysAttrs; } } diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test index cd827c20d2..4a978f6310 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test @@ -218,6 +218,24 @@ echo echo +echo "15. POST /entities/urn:E1/attrs" +echo "===============================" +payload='{ + "P1": 15 +}' +orionCurl --url /ngsi-ld/v1/entities/urn:E1/attrs --payload "$payload" +echo +echo + + +echo "16. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt)" +echo "===========================================================================================" +accumulatorDump +accumulatorReset +echo +echo + + --REGEXPECT-- 01. Attempt to create a subscription with sysAttrs AND format==simplified - see error ===================================================================================== @@ -521,6 +539,47 @@ Ngsild-Attribute-Format: Normalized ======================================= +15. POST /entities/urn:E1/attrs +=============================== +HTTP/1.1 204 No Content +Date: REGEX(.*) + + + +16. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +=========================================================================================== +POST http://REGEX(.*)/notify?subscriptionId=urn:S1 +Content-Length: 388 +User-Agent: orionld/REGEX(.*) +Host: tux +Accept: application/json +Content-Type: application/json +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" +Ngsild-Attribute-Format: Normalized + +{ + "data": [ + { + "P1": { + "createdAt": "202REGEX(.*)Z", + "modifiedAt": "202REGEX(.*)Z", + "type": "Property", + "value": 15 + }, + "createdAt": "202REGEX(.*)Z", + "id": "urn:E1", + "modifiedAt": "202REGEX(.*)Z", + "type": "T" + } + ], + "id": "urn:ngsi-ld:Notification:REGEX(.*)", + "notifiedAt": "202REGEX(.*)Z", + "subscriptionId": "urn:S1", + "type": "Notification" +} +======================================= + + --TEARDOWN-- brokerStop CB accumulatorStop From 1c68a8597dc4d95bb63e456cf8909c9761c68bac Mon Sep 17 00:00:00 2001 From: Ken Zangelin Date: Wed, 28 Jun 2023 22:55:11 +0200 Subject: [PATCH 5/7] sysAttrs in notifications triggered by PUT /entities/{EID} --- .../serviceRoutines/orionldPutEntity.cpp | 20 +++--- .../ngsild_subscription_with_sysAttrs.test | 61 ++++++++++++++++++- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/lib/orionld/serviceRoutines/orionldPutEntity.cpp b/src/lib/orionld/serviceRoutines/orionldPutEntity.cpp index 6d93ef6ef8..0661790eb4 100644 --- a/src/lib/orionld/serviceRoutines/orionldPutEntity.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPutEntity.cpp @@ -38,6 +38,7 @@ extern "C" #include "orionld/mongoc/mongocEntityLookup.h" // mongocEntityLookup #include "orionld/mongoc/mongocEntityReplace.h" // mongocEntityReplace #include "orionld/dbModel/dbModelFromApiEntity.h" // dbModelFromApiEntity +#include "orionld/dbModel/dbModelToApiEntity.h" // dbModelToApiEntity2 #include "orionld/context/orionldAttributeExpand.h" // orionldAttributeExpand #include "orionld/payloadCheck/pCheckUri.h" // pCheckUri #include "orionld/payloadCheck/pCheckEntityType.h" // pCheckEntityType @@ -311,15 +312,18 @@ bool orionldPutEntity(void) return false; } + KjNode* finalApiEntityWithSysAttrs = dbModelToApiEntity2(dbEntityP, true, RF_NORMALIZED, NULL, false, &orionldState.pd); + orionldState.alterations = (OrionldAlteration*) kaAlloc(&orionldState.kalloc, sizeof(OrionldAlteration)); - orionldState.alterations->entityId = entityId; - orionldState.alterations->entityType = typeNodeP->value.s; - orionldState.alterations->inEntityP = orionldState.requestTree; - orionldState.alterations->dbEntityP = NULL; - orionldState.alterations->finalApiEntityP = orionldState.requestTree; // entity id, createdAt, modifiedAt ... - orionldState.alterations->alteredAttributes = 0; - orionldState.alterations->alteredAttributeV = NULL; - orionldState.alterations->next = NULL; + orionldState.alterations->entityId = entityId; + orionldState.alterations->entityType = typeNodeP->value.s; + orionldState.alterations->inEntityP = orionldState.requestTree; + orionldState.alterations->dbEntityP = NULL; + orionldState.alterations->finalApiEntityWithSysAttrsP = finalApiEntityWithSysAttrs; + orionldState.alterations->finalApiEntityP = orionldState.requestTree; + orionldState.alterations->alteredAttributes = 0; + orionldState.alterations->alteredAttributeV = NULL; + orionldState.alterations->next = NULL; // // Is the entity id inside orionldState.requestTree? diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test index 4a978f6310..a6c0e5f5ce 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test @@ -48,7 +48,6 @@ accumulatorStart --pretty-print # 15. POST /entities/urn:E1/attrs # 16. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) # -# Later: # 17. PUT /ngsi-ld/v1/entities/urn:E1/attrs/P1 # 18. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) # 19. PUT /ngsi-ld/v1/entities/urn:E1 @@ -236,6 +235,25 @@ echo echo +echo "19. PUT /ngsi-ld/v1/entities/urn:E1" +echo "===================================" +payload='{ + "P1": 19, + "type": "T" +}' +orionCurl --url /ngsi-ld/v1/entities/urn:E1 --payload "$payload" -X PUT +echo +echo + + +echo "20. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt)" +echo "===========================================================================================" +accumulatorDump +accumulatorReset +echo +echo + + --REGEXPECT-- 01. Attempt to create a subscription with sysAttrs AND format==simplified - see error ===================================================================================== @@ -580,6 +598,47 @@ Ngsild-Attribute-Format: Normalized ======================================= +19. PUT /ngsi-ld/v1/entities/urn:E1 +=================================== +HTTP/1.1 204 No Content +Date: REGEX(.*) + + + +20. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +=========================================================================================== +POST http://REGEX(.*)/notify?subscriptionId=urn:S1 +Content-Length: 388 +User-Agent: orionld/REGEX(.*) +Host: tux +Accept: application/json +Content-Type: application/json +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" +Ngsild-Attribute-Format: Normalized + +{ + "data": [ + { + "P1": { + "createdAt": "202REGEX(.*)Z", + "modifiedAt": "202REGEX(.*)Z", + "type": "Property", + "value": 19 + }, + "createdAt": "202REGEX(.*)Z", + "id": "urn:E1", + "modifiedAt": "202REGEX(.*)Z", + "type": "T" + } + ], + "id": "urn:ngsi-ld:Notification:REGEX(.*)", + "notifiedAt": "202REGEX(.*)Z", + "subscriptionId": "urn:S1", + "type": "Notification" +} +======================================= + + --TEARDOWN-- brokerStop CB accumulatorStop From c4fb3d605e86af8e563d60bb693e96bc2d6512b3 Mon Sep 17 00:00:00 2001 From: Ken Zangelin Date: Wed, 28 Jun 2023 23:20:45 +0200 Subject: [PATCH 6/7] sysAttrs in notifications triggered by PUT /entities/{EID}/attrs/{attrName} --- .../serviceRoutines/orionldPutAttribute.cpp | 44 +++++++++++--- .../ngsild_subscription_with_sysAttrs.test | 59 +++++++++++++++++++ 2 files changed, 96 insertions(+), 7 deletions(-) diff --git a/src/lib/orionld/serviceRoutines/orionldPutAttribute.cpp b/src/lib/orionld/serviceRoutines/orionldPutAttribute.cpp index 0487ad1974..46c1197ada 100644 --- a/src/lib/orionld/serviceRoutines/orionldPutAttribute.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPutAttribute.cpp @@ -56,6 +56,7 @@ extern "C" #include "orionld/forwarding/distOpSuccess.h" // distOpSuccess #include "orionld/notifications/alteration.h" // alteration #include "orionld/notifications/previousValuePopulate.h" // previousValuePopulate +#include "orionld/notifications/sysAttrsStrip.h" // sysAttrsStrip #include "orionld/serviceRoutines/orionldPutAttribute.h" // Own Interface @@ -96,12 +97,17 @@ bool orionldPutAttribute(void) char* attrLongNameEq = kaStrdup(&orionldState.kalloc, attrLongName); KjNode* apiAttributeAsEntityP = NULL; KjNode* dbEntityCopy = NULL; - KjNode* apiEntityP = NULL; KjNode* oldAttrP = NULL; KjNode* apiAttributeClone = NULL; bool entityNotFoundLocally = false; bool attrNotFoundLocally = false; + OrionldAlteration* alterationP = NULL; + KjNode* finalApiEntityWithSysAttrs = NULL; + KjNode* finalApiEntity = NULL; + KjNode* createdAtP = NULL; + KjNode* modifiedAtP = NULL; + dotForEq(attrLongNameEq); if (dbEntityP == NULL) @@ -198,6 +204,7 @@ bool orionldPutAttribute(void) // Set creDate (mongocAttributeReplace sets modDate) dbModelAttributeCreatedAtSet(orionldState.requestTree, createdAt); + kjTreeLog(orionldState.requestTree, "orionldState.requestTree", LmtSR); // Write to mongo if (mongocAttributeReplace(entityId, orionldState.requestTree, &detail) == false) @@ -233,15 +240,16 @@ bool orionldPutAttribute(void) // o Insert a copy of the attribute that was replaced (new copy) // dbEntityCopy = kjClone(orionldState.kjsonP, dbEntityP); - apiEntityP = dbModelToApiEntity2(dbEntityCopy, false, RF_NORMALIZED, NULL, false, &orionldState.pd); - if (apiEntityP == NULL) + finalApiEntityWithSysAttrs = dbModelToApiEntity2(dbEntityCopy, true, RF_NORMALIZED, NULL, false, &orionldState.pd); + + if (finalApiEntityWithSysAttrs == NULL) { LM_E(("dbModelToApiEntity unable to convert DB Entity '%s' to API Entity (%s: %s)", entityId, orionldState.pd.title, orionldState.pd.detail)); goto response; } - oldAttrP = kjLookup(apiEntityP, attrLongName); + oldAttrP = kjLookup(finalApiEntityWithSysAttrs, attrLongName); if (oldAttrP == NULL) { LM_E(("Unable to find the attribute '%s' in the entity '%s'", attrLongName, entityId)); @@ -254,10 +262,32 @@ bool orionldPutAttribute(void) LM_E(("Unable to clone the attribute '%s' in the entity '%s'", attrLongName, entityId)); goto response; } - kjChildRemove(apiEntityP, oldAttrP); - kjChildAdd(apiEntityP, apiAttributeClone); + kjChildRemove(finalApiEntityWithSysAttrs, oldAttrP); + kjChildAdd(finalApiEntityWithSysAttrs, apiAttributeClone); + + // Now get createdAt/modifiedAt from oldAttrP + createdAtP = kjLookup(oldAttrP, "createdAt"); + modifiedAtP = kjLookup(oldAttrP, "modifiedAt"); + + if (createdAtP != NULL) + { + kjChildRemove(oldAttrP, createdAtP); + kjChildAdd(apiAttributeClone, createdAtP); + } + + if (modifiedAtP != NULL) + { + kjChildRemove(oldAttrP, modifiedAtP); + kjChildAdd(apiAttributeClone, modifiedAtP); + } + + + finalApiEntity = kjClone(orionldState.kjsonP, finalApiEntityWithSysAttrs); // Check for NULL ! + sysAttrsStrip(finalApiEntity); + - alteration(entityId, entityType, apiEntityP, apiAttributeAsEntityP, dbEntityP); + alterationP = alteration(entityId, entityType, finalApiEntity, apiAttributeAsEntityP, dbEntityP); + alterationP->finalApiEntityWithSysAttrsP = finalApiEntityWithSysAttrs; response: if (distOpList != NULL) diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test index a6c0e5f5ce..163c524ac1 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test @@ -235,6 +235,24 @@ echo echo +echo "17. PUT /ngsi-ld/v1/entities/urn:E1/attrs/P1" +echo "============================================" +payload='{ + "value": 17 +}' +orionCurl --url /ngsi-ld/v1/entities/urn:E1/attrs/P1 --payload "$payload" -X PUT +echo +echo + + +echo "18. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt)" +echo "===========================================================================================" +accumulatorDump +accumulatorReset +echo +echo + + echo "19. PUT /ngsi-ld/v1/entities/urn:E1" echo "===================================" payload='{ @@ -598,6 +616,47 @@ Ngsild-Attribute-Format: Normalized ======================================= +17. PUT /ngsi-ld/v1/entities/urn:E1/attrs/P1 +============================================ +HTTP/1.1 204 No Content +Date: REGEX(.*) + + + +18. See the notification in the accumulator, especially the sysAttrs (createdAt+modifiedAt) +=========================================================================================== +POST http://REGEX(.*)/notify?subscriptionId=urn:S1 +Content-Length: 388 +User-Agent: orionld/REGEX(.*) +Host: tux +Accept: application/json +Content-Type: application/json +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" +Ngsild-Attribute-Format: Normalized + +{ + "data": [ + { + "P1": { + "createdAt": "202REGEX(.*)Z", + "modifiedAt": "202REGEX(.*)Z", + "type": "Property", + "value": 17 + }, + "createdAt": "202REGEX(.*)Z", + "id": "urn:E1", + "modifiedAt": "202REGEX(.*)Z", + "type": "T" + } + ], + "id": "urn:ngsi-ld:Notification:REGEX(.*)", + "notifiedAt": "202REGEX(.*)Z", + "subscriptionId": "urn:S1", + "type": "Notification" +} +======================================= + + 19. PUT /ngsi-ld/v1/entities/urn:E1 =================================== HTTP/1.1 204 No Content From 2bf77bbf91aee500d9ab0bb3fe36882f7cafc4ed Mon Sep 17 00:00:00 2001 From: Ken Zangelin Date: Thu, 29 Jun 2023 10:25:16 +0200 Subject: [PATCH 7/7] Added a few missing REGEX in a functest --- .../0000_ngsild/ngsild_subscription_with_sysAttrs.test | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test index 163c524ac1..48aae74e57 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_with_sysAttrs.test @@ -509,7 +509,7 @@ Date: REGEX(.*) POST http://REGEX(.*)/notify?subscriptionId=urn:S1 Content-Length: 230 User-Agent: orionld/REGEX(.*) -Host: tux +Host: REGEX(.*) Accept: application/json Content-Type: application/json Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -546,7 +546,7 @@ Date: REGEX(.*) POST http://REGEX(.*)/notify?subscriptionId=urn:S1 Content-Length: 388 User-Agent: orionld/REGEX(.*) -Host: tux +Host: REGEX(.*) Accept: application/json Content-Type: application/json Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -587,7 +587,7 @@ Date: REGEX(.*) POST http://REGEX(.*)/notify?subscriptionId=urn:S1 Content-Length: 388 User-Agent: orionld/REGEX(.*) -Host: tux +Host: REGEX(.*) Accept: application/json Content-Type: application/json Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -628,7 +628,7 @@ Date: REGEX(.*) POST http://REGEX(.*)/notify?subscriptionId=urn:S1 Content-Length: 388 User-Agent: orionld/REGEX(.*) -Host: tux +Host: REGEX(.*) Accept: application/json Content-Type: application/json Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -669,7 +669,7 @@ Date: REGEX(.*) POST http://REGEX(.*)/notify?subscriptionId=urn:S1 Content-Length: 388 User-Agent: orionld/REGEX(.*) -Host: tux +Host: REGEX(.*) Accept: application/json Content-Type: application/json Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"