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