From 484babfac3e8da8afddea813e8948ed7e9aeb544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferm=C3=ADn=20Gal=C3=A1n=20M=C3=A1rquez?= Date: Wed, 8 Nov 2023 12:29:36 +0100 Subject: [PATCH] FIX correctly identify changes in JSON metadata values in subscription triggering logic --- src/lib/mongoBackend/MongoCommonUpdate.cpp | 69 +- src/lib/mongoDriver/BSONArray.cpp | 11 + src/lib/mongoDriver/BSONArray.h | 1 + src/lib/mongoDriver/BSONObj.cpp | 11 + src/lib/mongoDriver/BSONObj.h | 1 + .../change_evaluation_geojson.test} | 2 +- ..._evaluation_metadata_object_and_array.test | 600 ++++++++++++++++++ ...ge_evaluation_value_object_and_array.test} | 2 +- 8 files changed, 641 insertions(+), 56 deletions(-) rename test/functionalTest/cases/{4211_notify_upon_actual_change_in_json_value/notify_upon_actual_change_in_geo_json.test => 4211_right_json_change_evaluation_in_subs/change_evaluation_geojson.test} (99%) create mode 100644 test/functionalTest/cases/4211_right_json_change_evaluation_in_subs/change_evaluation_metadata_object_and_array.test rename test/functionalTest/cases/{4211_notify_upon_actual_change_in_json_value/notify_upon_actual_change_in_json_value.test => 4211_right_json_change_evaluation_in_subs/change_evaluation_value_object_and_array.test} (99%) diff --git a/src/lib/mongoBackend/MongoCommonUpdate.cpp b/src/lib/mongoBackend/MongoCommonUpdate.cpp index 544c94d3c1..bc5e0d036e 100644 --- a/src/lib/mongoBackend/MongoCommonUpdate.cpp +++ b/src/lib/mongoBackend/MongoCommonUpdate.cpp @@ -136,7 +136,7 @@ static bool hasMetadata(std::string name, std::string type, ContextAttribute* ca * * equalMetadataValues - */ -static bool equalMetadataValues(const orion::BSONObj& md1, const orion::BSONObj& md2) +static bool equalMetadataItems(const orion::BSONObj& md1, const orion::BSONObj& md2) { bool md1TypeExist = md1.hasField(ENT_ATTRS_MD_TYPE); bool md2TypeExist = md2.hasField(ENT_ATTRS_MD_TYPE); @@ -147,58 +147,23 @@ static bool equalMetadataValues(const orion::BSONObj& md1, const orion::BSONObj& return false; } - // If type exists in both metadata elments, check if they are the same + // If type exists in both metadata elements, check if they are the same if (md1TypeExist && md2TypeExist) { - if (getFieldF(md1, ENT_ATTRS_MD_TYPE).type() != getFieldF(md2, ENT_ATTRS_MD_TYPE).type()) + if ((getFieldF(md1, ENT_ATTRS_MD_TYPE).type() != orion::String)) { + LM_E(("Runtime Error (unallowed JSON type for metadata NGSI type: %d)", getFieldF(md1, ENT_ATTRS_MD_TYPE).type())); return false; } - switch (getFieldF(md1, ENT_ATTRS_MD_TYPE).type()) + if ((getFieldF(md2, ENT_ATTRS_MD_TYPE).type() != orion::String)) { - /* FIXME #643 P6: metadata array/object are now supported, but we haven't - implemented yet the logic to compare compounds between them - case Object: - ... - break; - - case Array: - ... - break; - */ - - case orion::NumberDouble: - if (getNumberFieldF(md1, ENT_ATTRS_MD_TYPE) != getNumberFieldF(md2, ENT_ATTRS_MD_TYPE)) - { - return false; - } - break; - - case orion::Bool: - if (getBoolFieldF(md1, ENT_ATTRS_MD_TYPE) != getBoolFieldF(md2, ENT_ATTRS_MD_TYPE)) - { - return false; - } - break; - - case orion::String: - if (getStringFieldF(md1, ENT_ATTRS_MD_TYPE) != getStringFieldF(md2, ENT_ATTRS_MD_TYPE)) - { - return false; - } - break; - - case orion::jstNULL: - if (!getFieldF(md2, ENT_ATTRS_MD_TYPE).isNull()) - { - return false; - } - break; + LM_E(("Runtime Error (unallowed JSON type for metadata NGSI type: %d)", getFieldF(md2, ENT_ATTRS_MD_TYPE).type())); + return false; + } - default: - LM_E(("Runtime Error (unknown JSON type for metadata NGSI type: %d)", getFieldF(md1, ENT_ATTRS_MD_TYPE).type())); + if (getStringFieldF(md1, ENT_ATTRS_MD_TYPE) != getStringFieldF(md2, ENT_ATTRS_MD_TYPE)) + { return false; - break; } } @@ -210,15 +175,11 @@ static bool equalMetadataValues(const orion::BSONObj& md1, const orion::BSONObj& switch (getFieldF(md1, ENT_ATTRS_MD_VALUE).type()) { - /* FIXME not yet case orion::Object: - ... - break; + return getObjectFieldF(md1, ENT_ATTRS_MD_VALUE).equal(getObjectFieldF(md2, ENT_ATTRS_MD_VALUE)); case orion::Array: - ... - break; - */ + return getArrayFieldF(md1, ENT_ATTRS_MD_VALUE).equal(getArrayFieldF(md2, ENT_ATTRS_MD_VALUE)); case orion::NumberDouble: return getNumberFieldF(md1, ENT_ATTRS_MD_VALUE) == getNumberFieldF(md2, ENT_ATTRS_MD_VALUE); @@ -266,7 +227,7 @@ static bool equalMetadata(const orion::BSONObj& md1, const orion::BSONObj& md2) orion::BSONObj md1Item = getObjectFieldF(md1, currentMd); orion::BSONObj md2Item = getObjectFieldF(md2, currentMd); - if (!equalMetadataValues(md1Item, md2Item)) + if (!equalMetadataItems(md1Item, md2Item)) { return false; } @@ -281,7 +242,7 @@ static bool equalMetadata(const orion::BSONObj& md1, const orion::BSONObj& md2) * * changedAttr - */ -static bool attrValueChanges(const orion::BSONObj& attr, ContextAttribute* caP, const bool& forcedUpdate, ApiVersion apiVersion) +static bool attrValueChanges(const orion::BSONObj& attr, ContextAttribute* caP, const bool& forcedUpdate) { /* Not finding the attribute field at MongoDB is considered as an implicit "" */ if (!attr.hasField(ENT_ATTRS_VALUE)) @@ -545,7 +506,7 @@ static ChangeType mergeAttrInfo bool mdChanged; if (caP->compoundValueP == NULL) { - valueChanged = attrValueChanges(attr, caP, forcedUpdate, apiVersion); + valueChanged = attrValueChanges(attr, caP, forcedUpdate); } else { diff --git a/src/lib/mongoDriver/BSONArray.cpp b/src/lib/mongoDriver/BSONArray.cpp index 7e69099455..afef83670e 100644 --- a/src/lib/mongoDriver/BSONArray.cpp +++ b/src/lib/mongoDriver/BSONArray.cpp @@ -87,6 +87,17 @@ std::string BSONArray::toString(void) const +/* **************************************************************************** +* +* BSONArray::equal - +*/ +bool BSONArray::equal(const BSONArray& ba) +{ + return (bson_compare(this->b, ba.b) == 0); +} + + + /* **************************************************************************** * * BSONArray::operator= - diff --git a/src/lib/mongoDriver/BSONArray.h b/src/lib/mongoDriver/BSONArray.h index d0e8b52c3f..098ac3520c 100644 --- a/src/lib/mongoDriver/BSONArray.h +++ b/src/lib/mongoDriver/BSONArray.h @@ -46,6 +46,7 @@ class BSONArray BSONArray(const BSONArray& _ba); int nFields(void) const; std::string toString(void) const; + bool equal(const BSONArray& ba); BSONArray& operator= (const BSONArray& rhs); // methods to be used only by mongoDriver/ code (with references to low-level driver code) diff --git a/src/lib/mongoDriver/BSONObj.cpp b/src/lib/mongoDriver/BSONObj.cpp index ad1ad1efb7..d7fcad2a49 100644 --- a/src/lib/mongoDriver/BSONObj.cpp +++ b/src/lib/mongoDriver/BSONObj.cpp @@ -192,6 +192,17 @@ void BSONObj::toElementsVector(std::vector* v) +/* **************************************************************************** +* +* BSONObj::equal - +*/ +bool BSONObj::equal(const BSONObj& bo) +{ + return (bson_compare(this->b, bo.b) == 0); +} + + + /* **************************************************************************** * * BSONObj::operator= - diff --git a/src/lib/mongoDriver/BSONObj.h b/src/lib/mongoDriver/BSONObj.h index d8b35f2c0b..473e974c01 100644 --- a/src/lib/mongoDriver/BSONObj.h +++ b/src/lib/mongoDriver/BSONObj.h @@ -59,6 +59,7 @@ class BSONObj bool isEmpty(void) const; void toStringMap(std::map* m); void toElementsVector(std::vector* v); + bool equal(const BSONObj& bo); BSONObj& operator= (const BSONObj& rhs); // methods to be used only by mongoDriver/ code (with references to low-level driver code) diff --git a/test/functionalTest/cases/4211_notify_upon_actual_change_in_json_value/notify_upon_actual_change_in_geo_json.test b/test/functionalTest/cases/4211_right_json_change_evaluation_in_subs/change_evaluation_geojson.test similarity index 99% rename from test/functionalTest/cases/4211_notify_upon_actual_change_in_json_value/notify_upon_actual_change_in_geo_json.test rename to test/functionalTest/cases/4211_right_json_change_evaluation_in_subs/change_evaluation_geojson.test index cd9a0c0506..a540d7c043 100644 --- a/test/functionalTest/cases/4211_notify_upon_actual_change_in_json_value/notify_upon_actual_change_in_geo_json.test +++ b/test/functionalTest/cases/4211_right_json_change_evaluation_in_subs/change_evaluation_geojson.test @@ -21,7 +21,7 @@ # VALGRIND_READY - to mark the test ready for valgrindTestSuite.sh --NAME-- -Notify upon actual change in geo:json value +Right JSON change evaluation in subs (GeoJson case) --SHELL-INIT-- dbInit CB diff --git a/test/functionalTest/cases/4211_right_json_change_evaluation_in_subs/change_evaluation_metadata_object_and_array.test b/test/functionalTest/cases/4211_right_json_change_evaluation_in_subs/change_evaluation_metadata_object_and_array.test new file mode 100644 index 0000000000..6fd56fd903 --- /dev/null +++ b/test/functionalTest/cases/4211_right_json_change_evaluation_in_subs/change_evaluation_metadata_object_and_array.test @@ -0,0 +1,600 @@ +# Copyright 2023 Telefonica Investigacion y Desarrollo, S.A.U +# +# This file is part of Orion Context Broker. +# +# Orion Context Broker is free software: you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Orion Context Broker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +# General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Orion Context Broker. If not, see http://www.gnu.org/licenses/. +# +# For those usages not covered by this license please contact with +# iot_support at tid dot es + +# VALGRIND_READY - to mark the test ready for valgrindTestSuite.sh + +--NAME-- +Right JSON change evaluation in subs (objects and arrays in metadata case) + +--SHELL-INIT-- +dbInit CB +brokerStart CB +accumulatorStart --pretty-print + +--SHELL-- + +# +# 01. Create subscription covering E-A +# 02. Create E-A entity with JSON object in metadata (notification) +# 03. Update E-A entity with same JSON object in metadata (no notification) +# 04. Update E-A entity with different JSON object in metadata (notification) +# 05. Update E-A entity with JSON array in metadata (notification) +# 06. Update E-A entity with same JSON array in metadata (no notification) +# 07. Update E-A entity with different JSON array in metadata (notification) +# 08. Update E-A entity with same string in metadata (notification) +# 09. Dump accumulator, see 5 notifications +# + +echo "01. Create subscription covering E-A" +echo "====================================" +payload='{ + "subject": { + "entities": [ + { + "id" : "E", + "type": "T" + } + ] + }, + "notification": { + "http": { + "url": "http://127.0.0.1:'${LISTENER_PORT}'/notify" + } + } +}' +orionCurl --url /v2/subscriptions --payload "$payload" +echo +echo + + +echo "02. Create E-A entity with JSON object in metadata (notification)" +echo "=================================================================" +payload='{ + "id": "E", + "type": "T", + "A": { + "value": 1, + "type": "Number", + "metadata": { + "M": { + "value": { + "text": "foo", + "number": 10, + "bool": true, + "null": null, + "array": [ + "22", + { + "x" : [ "x1", "x2" ], + "y" : 3 + }, + [ "z1", "z2" ] + ], + "object": { + "x": { + "x1": "a", + "x2": "b" + }, + "y": [ "y1", "y2" ] + } + }, + "type": "Json" + } + } + } +}' +orionCurl --url /v2/entities --payload "$payload" +echo +echo + + +echo "03. Update E-A entity with same JSON object in metadata (no notification)" +echo "=========================================================================" +payload='{ + "A": { + "value": 1, + "type": "Number", + "metadata": { + "M": { + "value": { + "text": "foo", + "number": 10, + "bool": true, + "null": null, + "array": [ + "22", + { + "x" : [ "x1", "x2" ], + "y" : 3 + }, + [ "z1", "z2" ] + ], + "object": { + "x": { + "x1": "a", + "x2": "b" + }, + "y": [ "y1", "y2" ] + } + }, + "type": "Json" + } + } + } +}' +orionCurl --url /v2/entities/E/attrs --payload "$payload" +echo +echo + + +echo "04. Update E-A entity with different JSON object in metadata (notification)" +echo "===========================================================================" +payload='{ + "A": { + "value": 1, + "type": "Number", + "metadata": { + "M": { + "value": { + "text": "foo", + "number": 10, + "bool": true, + "null": null, + "array": [ + "22", + { + "x" : [ "x1", "x2" ], + "y" : 3 + }, + [ "z1", "z2" ] + ], + "object": { + "x": { + "x1": "a", + "x2": "b" + }, + "y": [ "y1", "y_new" ] + } + }, + "type": "Json" + } + } + } +}' +orionCurl --url /v2/entities/E/attrs --payload "$payload" +echo +echo + + +echo "05. Update E-A entity with JSON array in metadata (notification)" +echo "================================================================" +payload='{ + "A": { + "value": 1, + "type": "Number", + "metadata": { + "M": { + "value": [ + "", + { + "x": [ "x1", "x2" ], + "y": "3" + }, + [ "z1", "z2" ] + ], + "type": "Array" + } + } + } +}' +orionCurl --url /v2/entities/E/attrs --payload "$payload" +echo +echo + + +echo "06. Update E-A entity with same JSON array in metadata (no notification)" +echo "========================================================================" +payload='{ + "A": { + "value": 1, + "type": "Number", + "metadata": { + "M": { + "value": [ + "", + { + "x": [ "x1", "x2" ], + "y": "3" + }, + [ "z1", "z2" ] + ], + "type": "Array" + } + } + } +}' +orionCurl --url /v2/entities/E/attrs --payload "$payload" +echo +echo + + +echo "07. Update E-A entity with different JSON array in metadata (notification)" +echo "==========================================================================" +payload='{ + "A": { + "value": 1, + "type": "Number", + "metadata": { + "M": { + "value": [ + "", + { + "x": [ "x1", "x_new" ], + "y": "3" + }, + [ "z1", "z2" ] + ], + "type": "Array" + } + } + } +}' +orionCurl --url /v2/entities/E/attrs --payload "$payload" +echo +echo + + +echo "08. Update E-A entity with same string in metadata (notification)" +echo "=================================================================" +payload='{ + "A": { + "value": 1, + "type": "Number", + "metadata": { + "M": { + "value": "foo", + "type": "Text" + } + } + } +}' +orionCurl --url /v2/entities/E/attrs --payload "$payload" +echo +echo + + +echo "09. Dump accumulator, see 5 notifications" +echo "=========================================" +accumulatorDump +echo +echo + + +--REGEXPECT-- +01. Create subscription covering E-A +==================================== +HTTP/1.1 201 Created +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) +Location: /v2/subscriptions/REGEX([0-9a-f]{24}) +Content-Length: 0 + + + +02. Create E-A entity with JSON object in metadata (notification) +================================================================= +HTTP/1.1 201 Created +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) +Location: /v2/entities/E?type=T +Content-Length: 0 + + + +03. Update E-A entity with same JSON object in metadata (no notification) +========================================================================= +HTTP/1.1 204 No Content +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) + + + +04. Update E-A entity with different JSON object in metadata (notification) +=========================================================================== +HTTP/1.1 204 No Content +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) + + + +05. Update E-A entity with JSON array in metadata (notification) +================================================================ +HTTP/1.1 204 No Content +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) + + + +06. Update E-A entity with same JSON array in metadata (no notification) +======================================================================== +HTTP/1.1 204 No Content +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) + + + +07. Update E-A entity with different JSON array in metadata (notification) +========================================================================== +HTTP/1.1 204 No Content +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) + + + +08. Update E-A entity with same string in metadata (notification) +================================================================= +HTTP/1.1 204 No Content +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) + + + +09. Dump accumulator, see 5 notifications +========================================= +POST http://127.0.0.1:REGEX(\d+)/notify +Fiware-Servicepath: / +Content-Length: 302 +User-Agent: orion/REGEX(\d+\.\d+\.\d+.*) +Ngsiv2-Attrsformat: normalized +Host: 127.0.0.1:REGEX(\d+) +Accept: application/json +Content-Type: application/json; charset=utf-8 +Fiware-Correlator: REGEX([0-9a-f\-]{36}); cbnotif=1 + +{ + "data": [ + { + "A": { + "metadata": { + "M": { + "type": "Json", + "value": { + "array": [ + "22", + { + "x": [ + "x1", + "x2" + ], + "y": 3 + }, + [ + "z1", + "z2" + ] + ], + "bool": true, + "null": null, + "number": 10, + "object": { + "x": { + "x1": "a", + "x2": "b" + }, + "y": [ + "y1", + "y2" + ] + }, + "text": "foo" + } + } + }, + "type": "Number", + "value": 1 + }, + "id": "E", + "type": "T" + } + ], + "subscriptionId": "REGEX([0-9a-f]{24})" +} +======================================= +POST http://127.0.0.1:REGEX(\d+)/notify +Fiware-Servicepath: / +Content-Length: 305 +User-Agent: orion/REGEX(\d+\.\d+\.\d+.*) +Ngsiv2-Attrsformat: normalized +Host: 127.0.0.1:REGEX(\d+) +Accept: application/json +Content-Type: application/json; charset=utf-8 +Fiware-Correlator: REGEX([0-9a-f\-]{36}); cbnotif=1 + +{ + "data": [ + { + "A": { + "metadata": { + "M": { + "type": "Json", + "value": { + "array": [ + "22", + { + "x": [ + "x1", + "x2" + ], + "y": 3 + }, + [ + "z1", + "z2" + ] + ], + "bool": true, + "null": null, + "number": 10, + "object": { + "x": { + "x1": "a", + "x2": "b" + }, + "y": [ + "y1", + "y_new" + ] + }, + "text": "foo" + } + } + }, + "type": "Number", + "value": 1 + }, + "id": "E", + "type": "T" + } + ], + "subscriptionId": "REGEX([0-9a-f]{24})" +} +======================================= +POST http://127.0.0.1:REGEX(\d+)/notify +Fiware-Servicepath: / +Content-Length: 193 +User-Agent: orion/REGEX(\d+\.\d+\.\d+.*) +Ngsiv2-Attrsformat: normalized +Host: 127.0.0.1:REGEX(\d+) +Accept: application/json +Content-Type: application/json; charset=utf-8 +Fiware-Correlator: REGEX([0-9a-f\-]{36}); cbnotif=1 + +{ + "data": [ + { + "A": { + "metadata": { + "M": { + "type": "Array", + "value": [ + "", + { + "x": [ + "x1", + "x2" + ], + "y": "3" + }, + [ + "z1", + "z2" + ] + ] + } + }, + "type": "Number", + "value": 1 + }, + "id": "E", + "type": "T" + } + ], + "subscriptionId": "REGEX([0-9a-f]{24})" +} +======================================= +POST http://127.0.0.1:REGEX(\d+)/notify +Fiware-Servicepath: / +Content-Length: 196 +User-Agent: orion/REGEX(\d+\.\d+\.\d+.*) +Ngsiv2-Attrsformat: normalized +Host: 127.0.0.1:REGEX(\d+) +Accept: application/json +Content-Type: application/json; charset=utf-8 +Fiware-Correlator: REGEX([0-9a-f\-]{36}); cbnotif=1 + +{ + "data": [ + { + "A": { + "metadata": { + "M": { + "type": "Array", + "value": [ + "", + { + "x": [ + "x1", + "x_new" + ], + "y": "3" + }, + [ + "z1", + "z2" + ] + ] + } + }, + "type": "Number", + "value": 1 + }, + "id": "E", + "type": "T" + } + ], + "subscriptionId": "REGEX([0-9a-f]{24})" +} +======================================= +POST http://127.0.0.1:REGEX(\d+)/notify +Fiware-Servicepath: / +Content-Length: 155 +User-Agent: orion/REGEX(\d+\.\d+\.\d+.*) +Ngsiv2-Attrsformat: normalized +Host: 127.0.0.1:REGEX(\d+) +Accept: application/json +Content-Type: application/json; charset=utf-8 +Fiware-Correlator: REGEX([0-9a-f\-]{36}); cbnotif=1 + +{ + "data": [ + { + "A": { + "metadata": { + "M": { + "type": "Text", + "value": "foo" + } + }, + "type": "Number", + "value": 1 + }, + "id": "E", + "type": "T" + } + ], + "subscriptionId": "REGEX([0-9a-f]{24})" +} +======================================= + + +--TEARDOWN-- +brokerStop CB +dbDrop CB +accumulatorStop diff --git a/test/functionalTest/cases/4211_notify_upon_actual_change_in_json_value/notify_upon_actual_change_in_json_value.test b/test/functionalTest/cases/4211_right_json_change_evaluation_in_subs/change_evaluation_value_object_and_array.test similarity index 99% rename from test/functionalTest/cases/4211_notify_upon_actual_change_in_json_value/notify_upon_actual_change_in_json_value.test rename to test/functionalTest/cases/4211_right_json_change_evaluation_in_subs/change_evaluation_value_object_and_array.test index b50fa070b2..cc664dd6cd 100644 --- a/test/functionalTest/cases/4211_notify_upon_actual_change_in_json_value/notify_upon_actual_change_in_json_value.test +++ b/test/functionalTest/cases/4211_right_json_change_evaluation_in_subs/change_evaluation_value_object_and_array.test @@ -21,7 +21,7 @@ # VALGRIND_READY - to mark the test ready for valgrindTestSuite.sh --NAME-- -Notify upon actual change in JSON value +Right JSON change evaluation in subs (objects and arrays in attribute value case) --SHELL-INIT-- dbInit CB