diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index c3b7ccea03..7c55d0ee44 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1,5 +1,6 @@ - Add: servicePath field to builtin attributes (#2877) - Add: notification.mqtt.retain and notification.mqttCustom.retain flag for MQTT retain in notifications (#4388) +- 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: improve error traces (#4387) - Add: CLI parameter -dbUri / env var ORION_MONGO_URI (#3794) diff --git a/src/lib/jsonParseV2/jsonRequestTreat.cpp b/src/lib/jsonParseV2/jsonRequestTreat.cpp index f0cb326947..def0d2b519 100644 --- a/src/lib/jsonParseV2/jsonRequestTreat.cpp +++ b/src/lib/jsonParseV2/jsonRequestTreat.cpp @@ -102,7 +102,7 @@ std::string jsonRequestTreat case EntityAttributeRequest: releaseP->attribute = &parseDataP->attr.attribute; releaseP->attribute->name = compV[4]; - answer = parseContextAttribute(ciP, &parseDataP->attr.attribute); + answer = parseContextAttribute(ciP, &parseDataP->attr.attribute, true); if (answer != "OK") { return answer; diff --git a/src/lib/jsonParseV2/parseContextAttribute.cpp b/src/lib/jsonParseV2/parseContextAttribute.cpp index 95bb1a26dd..751dfb66d3 100644 --- a/src/lib/jsonParseV2/parseContextAttribute.cpp +++ b/src/lib/jsonParseV2/parseContextAttribute.cpp @@ -105,7 +105,8 @@ static std::string parseContextAttributeObject ( const rapidjson::Value& start, ContextAttribute* caP, - bool* compoundVector + bool* compoundVector, + bool checkAttrSpecialTypes ) { // This is NGSIv2 parsing and in NGSIv2, no value means implicit null. Note that @@ -215,7 +216,7 @@ static std::string parseContextAttributeObject } // Is it a (not null) date? - if (((caP->type == DATE_TYPE) || (caP->type == DATE_TYPE_ALT)) && (caP->valueType != orion::ValueTypeNull)) + if (checkAttrSpecialTypes && ((caP->type == DATE_TYPE) || (caP->type == DATE_TYPE_ALT)) && (caP->valueType != orion::ValueTypeNull)) { caP->numberValue = parse8601Time(caP->stringValue); @@ -230,7 +231,7 @@ static std::string parseContextAttributeObject } // It is a safe GeoJSON? - if (caP->type == GEO_JSON) + if (checkAttrSpecialTypes && caP->type == GEO_JSON) { std::string r = checkGeoJson(caP); if (r != "OK") @@ -252,7 +253,8 @@ std::string parseContextAttribute ( ConnectionInfo* ciP, const rapidjson::Value::ConstMemberIterator& iter, - ContextAttribute* caP + ContextAttribute* caP, + bool checkAttrSpecialTypes ) { std::string name = iter->name.GetString(); @@ -354,7 +356,7 @@ std::string parseContextAttribute // Attribute has a regular structure, in which 'value' is mandatory (except in v2) if (iter->value.HasMember("value") || ciP->apiVersion == V2) { - std::string r = parseContextAttributeObject(iter->value, caP, &compoundVector); + std::string r = parseContextAttributeObject(iter->value, caP, &compoundVector, checkAttrSpecialTypes); if (r == "max deep reached") { alarmMgr.badInput(clientIp, "max deep reached", "found in ContextAttributeObject::Object"); @@ -398,7 +400,7 @@ std::string parseContextAttribute * * parseContextAttribute - */ -std::string parseContextAttribute(ConnectionInfo* ciP, ContextAttribute* caP) +std::string parseContextAttribute(ConnectionInfo* ciP, ContextAttribute* caP, bool checkAttrSpecialTypes) { rapidjson::Document document; @@ -426,7 +428,7 @@ std::string parseContextAttribute(ConnectionInfo* ciP, ContextAttribute* caP) } bool compoundVector = false; - std::string r = parseContextAttributeObject(document, caP, &compoundVector); + std::string r = parseContextAttributeObject(document, caP, &compoundVector, checkAttrSpecialTypes); if (r == "max deep reached") { diff --git a/src/lib/jsonParseV2/parseContextAttribute.h b/src/lib/jsonParseV2/parseContextAttribute.h index ac92ad5eaa..e3210e9403 100644 --- a/src/lib/jsonParseV2/parseContextAttribute.h +++ b/src/lib/jsonParseV2/parseContextAttribute.h @@ -41,7 +41,8 @@ extern std::string parseContextAttribute ( ConnectionInfo* ciP, const rapidjson::Value::ConstMemberIterator& iter, - ContextAttribute* caP + ContextAttribute* caP, + bool checkAttrSpecialTypes ); @@ -50,6 +51,6 @@ extern std::string parseContextAttribute * * parseContextAttribute - */ -extern std::string parseContextAttribute(ConnectionInfo* ciP, ContextAttribute* caP); +extern std::string parseContextAttribute(ConnectionInfo* ciP, ContextAttribute* caP, bool checkAttrSpecialTypes); #endif // SRC_LIB_JSONPARSEV2_PARSECONTEXTATTRIBUTE_H_ diff --git a/src/lib/jsonParseV2/parseEntity.cpp b/src/lib/jsonParseV2/parseEntity.cpp index 46b9909314..c47c3116a8 100644 --- a/src/lib/jsonParseV2/parseEntity.cpp +++ b/src/lib/jsonParseV2/parseEntity.cpp @@ -235,7 +235,7 @@ std::string parseEntity(ConnectionInfo* ciP, Entity* eP, bool eidInURL) eP->attributeVector.push_back(caP); - std::string r = parseContextAttribute(ciP, iter, caP); + std::string r = parseContextAttribute(ciP, iter, caP, true); if (r == "max deep reached") { OrionError oe(SccBadRequest, ERROR_DESC_PARSE_MAX_JSON_NESTING, ERROR_PARSE); diff --git a/src/lib/jsonParseV2/parseEntityObject.cpp b/src/lib/jsonParseV2/parseEntityObject.cpp index 9d54d5e7ca..aa6cc8b665 100644 --- a/src/lib/jsonParseV2/parseEntityObject.cpp +++ b/src/lib/jsonParseV2/parseEntityObject.cpp @@ -157,7 +157,7 @@ std::string parseEntityObject ContextAttribute* caP = new ContextAttribute(); - r = parseContextAttribute(ciP, iter, caP); + r = parseContextAttribute(ciP, iter, caP, true); if (r == "OK") { eP->attributeVector.push_back(caP); diff --git a/src/lib/jsonParseV2/parseSubscription.cpp b/src/lib/jsonParseV2/parseSubscription.cpp index f68e11c259..3724528ba1 100644 --- a/src/lib/jsonParseV2/parseSubscription.cpp +++ b/src/lib/jsonParseV2/parseSubscription.cpp @@ -546,7 +546,7 @@ static std::string parseCustomPayload ngsi->attributeVector.push_back(caP); - std::string r = parseContextAttribute(ciP, iter, caP); + std::string r = parseContextAttribute(ciP, iter, caP, false); if (r == "max deep reached") { diff --git a/test/functionalTest/cases/4435_ngsi_patching_special_attr_types/ngsi_patching_special_attr_types.test b/test/functionalTest/cases/4435_ngsi_patching_special_attr_types/ngsi_patching_special_attr_types.test new file mode 100644 index 0000000000..8352a77b7b --- /dev/null +++ b/test/functionalTest/cases/4435_ngsi_patching_special_attr_types/ngsi_patching_special_attr_types.test @@ -0,0 +1,255 @@ +# 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-- +Custom notification NGSI patching special attr types + +--SHELL-INIT-- +dbInit CB +brokerStart CB 0-255 + +--SHELL-- + +# +# 01. Create custom subscription with ngsi patching with DateTime and geo:json +# 02. Get subscription +# 03. Update custom subscription with ngsi patching with DateTime and geo:json +# 04. Get subscription +# + +echo "01. Create custom subscription with ngsi patching with DateTime and geo:json" +echo "============================================================================" +payload='{ + "description": "DateTime test", + "status": "active", + "subject": { + "entities": [ + { + "idPattern": ".*", + "type": "NoiseLevelObserved" + } + ], + "condition": { + "attrs": [ + "TimeInstant" + ], + "notifyOnMetadataChange": true + } + }, + "notification": { + "attrs": [ + "dateobservedto", + "mylocation" + ], + "onlyChangedAttrs": false, + "attrsFormat": "normalized", + "httpCustom": { + "url": "http://127.0.0.1:'${LISTENER_PORT}'/notify", + "ngsi": { + "dateobservedto": { + "type": "DateTime", + "value": "${TimeInstant}" + }, + "mylocation": { + "type": "geo:json", + "value": "${location}" + } + } + } + } +}' +orionCurl --url /v2/subscriptions --payload "$payload" +echo +echo + + +SUB_ID=$(echo "$_responseHeaders" | grep Location | awk -F/ '{ print $4 }' | tr -d "\r\n") + + +echo "02. Get subscriptions" +echo "=====================" +orionCurl --url /v2/subscriptions/$SUB_ID +echo +echo + + +echo "03. Update custom subscription with ngsi patching with DateTime and geo:json" +echo "============================================================================" +payload='{ + "notification": { + "attrs": [ + "dateobservedto2", + "mylocation2" + ], + "onlyChangedAttrs": false, + "attrsFormat": "normalized", + "httpCustom": { + "url": "http://127.0.0.1:'${LISTENER_PORT}'/notify", + "ngsi": { + "dateobservedto2": { + "type": "DateTime", + "value": "${TimeInstant}" + }, + "mylocation2": { + "type": "geo:json", + "value": "${location}" + } + } + } + } +}' +orionCurl --url /v2/subscriptions/$SUB_ID -X PATCH --payload "$payload" +echo +echo + + +echo "04. Get subscriptions" +echo "=====================" +orionCurl --url /v2/subscriptions/$SUB_ID +echo +echo + + +--REGEXPECT-- +01. Create custom subscription with ngsi patching with DateTime and geo:json +============================================================================ +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. Get subscriptions +===================== +HTTP/1.1 200 OK +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) +Content-Type: application/json +Content-Length: 524 + +{ + "description": "DateTime test", + "id": "REGEX([0-9a-f]{24})", + "notification": { + "attrs": [ + "dateobservedto", + "mylocation" + ], + "attrsFormat": "normalized", + "covered": false, + "httpCustom": { + "ngsi": { + "dateobservedto": { + "type": "DateTime", + "value": "${TimeInstant}" + }, + "mylocation": { + "type": "geo:json", + "value": "${location}" + } + }, + "url": "http://127.0.0.1:9997/notify" + }, + "onlyChangedAttrs": false + }, + "status": "active", + "subject": { + "condition": { + "attrs": [ + "TimeInstant" + ], + "notifyOnMetadataChange": true + }, + "entities": [ + { + "idPattern": ".*", + "type": "NoiseLevelObserved" + } + ] + } +} + + +03. Update custom subscription with ngsi patching with DateTime and geo:json +============================================================================ +HTTP/1.1 204 No Content +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) + + + +04. Get subscriptions +===================== +HTTP/1.1 200 OK +Date: REGEX(.*) +Fiware-Correlator: REGEX([0-9a-f\-]{36}) +Content-Type: application/json +Content-Length: 528 + +{ + "description": "DateTime test", + "id": "REGEX([0-9a-f]{24})", + "notification": { + "attrs": [ + "dateobservedto2", + "mylocation2" + ], + "attrsFormat": "normalized", + "covered": false, + "httpCustom": { + "ngsi": { + "dateobservedto2": { + "type": "DateTime", + "value": "${TimeInstant}" + }, + "mylocation2": { + "type": "geo:json", + "value": "${location}" + } + }, + "url": "http://127.0.0.1:9997/notify" + }, + "onlyChangedAttrs": false + }, + "status": "active", + "subject": { + "condition": { + "attrs": [ + "TimeInstant" + ], + "notifyOnMetadataChange": true + }, + "entities": [ + { + "idPattern": ".*", + "type": "NoiseLevelObserved" + } + ] + } +} + + +--TEARDOWN-- +brokerStop CB +dbDrop CB