From 834379ecfb53ea912ec118847fc9811e462c7e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferm=C3=ADn=20Gal=C3=A1n=20M=C3=A1rquez?= Date: Thu, 11 Jan 2024 14:22:31 +0100 Subject: [PATCH 1/3] FIX NGSI patching macro subscription covering completely the string always cast to string for id/type --- CHANGES_NEXT_RELEASE | 1 + doc/manuals/orion-api.md | 4 +- src/lib/ngsiNotify/Notifier.cpp | 30 ++- ...tching_id_type_macro_always_stringfly.test | 206 ++++++++++++++++++ 4 files changed, 232 insertions(+), 9 deletions(-) create mode 100644 test/functionalTest/cases/4462_ngsi_patching_id_type_macro_always_stringfly/ngsi_patching_id_type_macro_always_stringfly.test diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 51c5369b83..b2cca13521 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -6,6 +6,7 @@ - Fix: update forwarding was not working when entity type is not included in the request (#4460) - Fix: DateTime and geo:json types were not supported in custom notifications using ngsi patching (#4435) - Fix: logDeprecate not working correctly (`geo:json` wrongly considered as deprecated) +- Fix: in custom notification NGSI payload patching macro subscription covering completely the string where is used, always cast to string for id/type (#4462) - Fix: improve error traces (#4387) - Fix: on delete attributes operation, the lack of attribute in entity doesn't preclude the deletion of attributes which do exist (this way behaving same way as update attributes operation) - Fix: POST /v2/entities/E/attrs?options=append was wrongly creating entities diff --git a/doc/manuals/orion-api.md b/doc/manuals/orion-api.md index c23cebe0dc..11b86a3859 100644 --- a/doc/manuals/orion-api.md +++ b/doc/manuals/orion-api.md @@ -2253,8 +2253,10 @@ Some notes to take into account when using `ngsi`: * It cannot be used in the key part of JSON objects, i.e. `"${key}": 10` will not work * It cannot be used in the attribute `type`. Only in the `value` macro replacements can be done. * If the macro *covers completely the string where is used*, then the JSON nature of the attribute value - is taken into account. For instance, `"value": "${temperature}"` resolves to `"value": 10` + is taken into account. For instance, `"value": "${temperature}"` resolves to `"value": 10`. if temperature attribute is a number or to `"value": "10"` if `temperature` attribute is a string. + * Exception to this is `id` and `type`. Given that entity id and type must be a string (as decribed + in [this section](#identifiers-syntax-restrictions)) the attribute value is always casted to string in this case. * If the macro *is only part of string where is used*, then the attribute value is always casted to string. For instance, `"value": "Temperature is: ${temperature}"` resolves to `"value": "Temperature is 10"` even if temperature attribute is a number. Note that if the diff --git a/src/lib/ngsiNotify/Notifier.cpp b/src/lib/ngsiNotify/Notifier.cpp index 75865a4f4f..0eb136c8fa 100644 --- a/src/lib/ngsiNotify/Notifier.cpp +++ b/src/lib/ngsiNotify/Notifier.cpp @@ -209,6 +209,26 @@ static bool setJsonPayload } +/* **************************************************************************** +* +* removeQuotes - +* +* Entity id and type are special. Different from a attribute, they are always +* strings and cannot take a number, boolean, etc. as value. +*/ +inline std::string removeQuotes(std::string s) +{ + if (s[0] == '"') + { + return s.substr(1, s.size()-2); + } + else + { + return s; + } +} + + /* **************************************************************************** * @@ -247,10 +267,7 @@ static bool setNgsiPayload else { // If id is not found in the replacements macro, we use en.id. - // In addition, note we have to remove double quotes here given the - // values stored in replacements map are "raw strings" - std::string s = smartStringValue(ngsi.id, &replacements, '"' + en.id + '"'); - effectiveId = s.substr(1, s.size()-2); + effectiveId = removeQuotes(smartStringValue(ngsi.id, &replacements, '"' + en.id + '"')); } std::string effectiveType; @@ -261,10 +278,7 @@ static bool setNgsiPayload else { // If type is not found in the replacements macro, we use en.type. - // In addition, note we have to remove double quotes here given the - // values stored in replacements map are "raw strings" - std::string s = smartStringValue(ngsi.type, &replacements, '"' + en.type + '"'); - effectiveType = s.substr(1, s.size()-2); + effectiveType = removeQuotes(smartStringValue(ngsi.type, &replacements, '"' + en.type + '"')); } cer.entity.fill(effectiveId, effectiveType, en.isPattern, en.servicePath); diff --git a/test/functionalTest/cases/4462_ngsi_patching_id_type_macro_always_stringfly/ngsi_patching_id_type_macro_always_stringfly.test b/test/functionalTest/cases/4462_ngsi_patching_id_type_macro_always_stringfly/ngsi_patching_id_type_macro_always_stringfly.test new file mode 100644 index 0000000000..9795cc1ab7 --- /dev/null +++ b/test/functionalTest/cases/4462_ngsi_patching_id_type_macro_always_stringfly/ngsi_patching_id_type_macro_always_stringfly.test @@ -0,0 +1,206 @@ +# Copyright 2024 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 implie d 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-- +NGSI patching macro subscription covering completely the string always cast to string for id/type + +--SHELL-INIT-- +dbInit CB +brokerStart CB +accumulatorStart --pretty-print + +--SHELL-- + +# +# 01. Create custom sub1 with ngsi id and type with the value of idAtt and typeAtt attributes +# 02. Create entity E with idAtt=1 and typeAtt=2 as numbers +# 03. Update entity E with idAtt=110 and typeAtt=220 as numbers +# 04. Dump accumulator and see notifications with id "1" and type "2" and id "110" and type "220" +# + + +echo "01. Create custom sub1 with ngsi id and type with the value of idAtt and typeAtt attributes" +echo "===========================================================================================" +payload='{ + "subject": { + "entities": [ + { + "id" : "E", + "type": "T" + } + ] + }, + "notification": { + "httpCustom": { + "url": "http://127.0.0.1:'${LISTENER_PORT}'/notify", + "ngsi": { + "id": "${idAtt}", + "type": "${typeAtt}" + } + } + } +}' +orionCurl --url /v2/subscriptions --payload "$payload" +echo +echo + + +echo "02. Create entity E with idAtt=1 and typeAtt=2 as numbers" +echo "=========================================================" +payload='{ + "id": "E", + "type": "T", + "idAtt": { + "value": 1, + "type": "Number" + }, + "typeAtt": { + "value": 2, + "type": "Number" + } +}' +orionCurl --url /v2/entities --payload "$payload" +echo +echo + + +echo "03. Update entity E with idAtt=110 and typeAtt=220 as numbers" +echo "=============================================================" +payload='{ + "idAtt": { + "value": 110, + "type": "Number" + }, + "typeAtt": { + "value": 220, + "type": "Number" + } +}' +orionCurl --url /v2/entities/E/attrs -X PATCH --payload "$payload" +echo +echo + + +echo '04. Dump accumulator and see notifications with id "1" and type "2" and id "110" and type "220"' +echo "===============================================================================================" +accumulatorDump +echo +echo + + +--REGEXPECT-- +01. Create custom sub1 with ngsi id and type with the value of idAtt and typeAtt attributes +=========================================================================================== +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 entity E with idAtt=1 and typeAtt=2 as numbers +========================================================= +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 entity E with idAtt=110 and typeAtt=220 as numbers +============================================================= +HTTP/1.1 204 No Content +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) + + + +04. Dump accumulator and see notifications with id "1" and type "2" and id "110" and type "220" +=============================================================================================== +POST http://127.0.0.1:REGEX(\d+)/notify +Fiware-Servicepath: / +Content-Length: 178 +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": [ + { + "id": "1", + "idAtt": { + "metadata": {}, + "type": "Number", + "value": 1 + }, + "type": "2", + "typeAtt": { + "metadata": {}, + "type": "Number", + "value": 2 + } + } + ], + "subscriptionId": "REGEX([0-9a-f]{24})" +} +======================================= +POST http://127.0.0.1:REGEX(\d+)/notify +Fiware-Servicepath: / +Content-Length: 186 +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": [ + { + "id": "110", + "idAtt": { + "metadata": {}, + "type": "Number", + "value": 110 + }, + "type": "220", + "typeAtt": { + "metadata": {}, + "type": "Number", + "value": 220 + } + } + ], + "subscriptionId": "REGEX([0-9a-f]{24})" +} +======================================= + + +--TEARDOWN-- +brokerStop CB +dbDrop CB +accumulatorStop From 06d84b9c1e668dd3306520f2dddd73bf909973ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferm=C3=ADn=20Gal=C3=A1n=20M=C3=A1rquez?= Date: Thu, 11 Jan 2024 14:23:42 +0100 Subject: [PATCH 2/3] Update doc/manuals/orion-api.md --- doc/manuals/orion-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manuals/orion-api.md b/doc/manuals/orion-api.md index 11b86a3859..72f457e42d 100644 --- a/doc/manuals/orion-api.md +++ b/doc/manuals/orion-api.md @@ -2253,7 +2253,7 @@ Some notes to take into account when using `ngsi`: * It cannot be used in the key part of JSON objects, i.e. `"${key}": 10` will not work * It cannot be used in the attribute `type`. Only in the `value` macro replacements can be done. * If the macro *covers completely the string where is used*, then the JSON nature of the attribute value - is taken into account. For instance, `"value": "${temperature}"` resolves to `"value": 10`. + is taken into account. For instance, `"value": "${temperature}"` resolves to `"value": 10` if temperature attribute is a number or to `"value": "10"` if `temperature` attribute is a string. * Exception to this is `id` and `type`. Given that entity id and type must be a string (as decribed in [this section](#identifiers-syntax-restrictions)) the attribute value is always casted to string in this case. From bb72014138c11f5221130342a6b1ac84558ad398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferm=C3=ADn=20Gal=C3=A1n=20M=C3=A1rquez?= Date: Thu, 11 Jan 2024 17:42:05 +0100 Subject: [PATCH 3/3] Update test/functionalTest/cases/4462_ngsi_patching_id_type_macro_always_stringfly/ngsi_patching_id_type_macro_always_stringfly.test --- .../ngsi_patching_id_type_macro_always_stringfly.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functionalTest/cases/4462_ngsi_patching_id_type_macro_always_stringfly/ngsi_patching_id_type_macro_always_stringfly.test b/test/functionalTest/cases/4462_ngsi_patching_id_type_macro_always_stringfly/ngsi_patching_id_type_macro_always_stringfly.test index 9795cc1ab7..87b08ba0d1 100644 --- a/test/functionalTest/cases/4462_ngsi_patching_id_type_macro_always_stringfly/ngsi_patching_id_type_macro_always_stringfly.test +++ b/test/functionalTest/cases/4462_ngsi_patching_id_type_macro_always_stringfly/ngsi_patching_id_type_macro_always_stringfly.test @@ -8,7 +8,7 @@ # 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 implie d warranty of +# 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. #