Skip to content

Commit

Permalink
Merge pull request #4166 from telefonicaid/feature/4159_extra_custom_…
Browse files Browse the repository at this point in the history
…notif_macros

ADD service, servicePath and authToken macros to custom notifications
  • Loading branch information
mapedraza authored Jul 12, 2022
2 parents 60e5f5e + 0574f07 commit c6fef64
Show file tree
Hide file tree
Showing 14 changed files with 1,110 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- Add: json field in httpCustom and mqttCustom subscriptions (#2560)
- Add: ${service}, ${servicePath} and ${authToken} macros in custom notifications (#4159)
- Upgrade Debian version from 11.2 to 11.3 in Dockerfile
- Remove: RPM package stuff
20 changes: 20 additions & 0 deletions doc/manuals/user/ngsiv2_implementation_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* [Ordering between different attribute value types](#ordering-between-different-attribute-value-types)
* [Oneshot subscriptions](#oneshot-subscriptions)
* [Subscriptions based in alteration type](#subscriptions-based-in-alteration-type)
* [Custom notification extra macros](#custom-notification-extra-macros)
* [Custom notification with JSON payload](#custom-notification-with-json-payload)
* [Custom notifications without payload](#custom-notifications-without-payload)
* [MQTT notifications](#mqtt-notifications)
Expand Down Expand Up @@ -495,6 +496,25 @@ modification, etc.) the subscription is triggered.

Please find details in [this specific documentation](subscriptions_alttype.md)

[Top](#top)

## Custom notification extra macros

Appart from the `${...}` macros described in "Custom Notifications" section in the NGSIv2
specification, the following ones can be used:

* `${service}` is replaced by the service (i.e. `fiware-service` header value) in the
update request triggering the subscription.
* `${servicePath}` is replaced by the service path (i.e. `fiware-servicepath` header value) in the
update request triggering the subscription.
* `${authToken}` is replaced by the authorization token (i.e. `x-auth-token` header value) in the
update request triggering the subscription.

In the rare case an attribute was named in the same way of the above (e.g. an attribute which
name is `service`) then the attribute value takes precedence.

[Top](#top)

## Custom notification with JSON payload

As alternative to `payload` field in `httpCustom` or `mqttCustom`, the `json` field can be
Expand Down
47 changes: 46 additions & 1 deletion src/lib/common/macroSubstitute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ static void attributeValue(std::string* valueP, const std::vector<ContextAttribu
* Date: Mon Jun 19 16:33:29 2017 +0200
*
*/
bool macroSubstitute(std::string* to, const std::string& from, const Entity& en)
bool macroSubstitute(std::string* to, const std::string& from, const Entity& en, const std::string& service, const std::string& authToken)
{
// Initial size check: is the string to convert too big?
//
Expand Down Expand Up @@ -198,6 +198,18 @@ bool macroSubstitute(std::string* to, const std::string& from, const Entity& en)
{
toAdd += en.type.length() * times;
}
else if (macroName == "service")
{
toAdd += service.length() * times;
}
else if (macroName == "servicePath")
{
toAdd += en.servicePath.length() * times;
}
else if (macroName == "authToken")
{
toAdd += authToken.length() * times;
}
else
{
std::string value;
Expand Down Expand Up @@ -232,6 +244,39 @@ bool macroSubstitute(std::string* to, const std::string& from, const Entity& en)
{
value = en.type;
}
else if (macroName == "service")
{
if (en.attributeVector.get("service") >= 0)
{
attributeValue(&value, en.attributeVector.vec, macroName.c_str());
}
else
{
value = service;
}
}
else if (macroName == "servicePath")
{
if (en.attributeVector.get("servicePath") >= 0)
{
attributeValue(&value, en.attributeVector.vec, macroName.c_str());
}
else
{
value = en.servicePath;
}
}
else if (macroName == "authToken")
{
if (en.attributeVector.get("authToken") >= 0)
{
attributeValue(&value, en.attributeVector.vec, macroName.c_str());
}
else
{
value = authToken;
}
}
else
{
attributeValue(&value, en.attributeVector.vec, macroName.c_str());
Expand Down
2 changes: 1 addition & 1 deletion src/lib/common/macroSubstitute.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@
*
* macroSubstitute -
*/
extern bool macroSubstitute(std::string* sP, const std::string& in, const Entity& en);
extern bool macroSubstitute(std::string* sP, const std::string& in, const Entity& en, const std::string& service, const std::string& authToken);

#endif // SRC_LIB_COMMON_MACROSUBSTITUTE_H_
27 changes: 18 additions & 9 deletions src/lib/ngsiNotify/Notifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ static bool setPayload
const std::string& notifPayload,
const SubscriptionId& subscriptionId,
Entity& en,
const std::string& service,
const std::string& token,
const std::vector<std::string>& attrsFilter,
bool blacklist,
const std::vector<std::string>& metadataFilter,
Expand Down Expand Up @@ -165,7 +167,7 @@ static bool setPayload
}
else
{
if (!macroSubstitute(payloadP, notifPayload, en))
if (!macroSubstitute(payloadP, notifPayload, en, service, token))
{
return false;
}
Expand All @@ -190,6 +192,8 @@ static bool setJsonPayload
(
orion::CompoundValueNode* json,
const Entity& en,
const std::string& service,
const std::string& token,
std::string* payloadP,
std::string* mimeTypeP
)
Expand All @@ -198,10 +202,15 @@ static bool setJsonPayload
// orion::CompoundValueNode()::toJson(), but the include Entity.h in CompoundValueNode.h
// makes compiler to cry (maybe some kind of circular dependency problem?)
std::map<std::string, std::string> replacements;
replacements.insert(std::pair<std::string, std::string>("id", en.id));
replacements.insert(std::pair<std::string, std::string>("type", en.type));
replacements.insert(std::pair<std::string, std::string>("id", "\"" + en.id + "\""));
replacements.insert(std::pair<std::string, std::string>("type", "\"" + en.type + "\""));
replacements.insert(std::pair<std::string, std::string>("service", "\"" + service + "\""));
replacements.insert(std::pair<std::string, std::string>("servicePath", "\"" + en.servicePath + "\""));
replacements.insert(std::pair<std::string, std::string>("authToken", "\"" + token + "\""));
for (unsigned int ix = 0; ix < en.attributeVector.size(); ix++)
{
// Note that if some attribute is named service, servicePath or authToken (although it would be
// an anti-pattern), the attribute takes precedence
replacements[en.attributeVector[ix]->name] = en.attributeVector[ix]->toJsonValue();
}

Expand Down Expand Up @@ -272,7 +281,7 @@ static std::vector<SenderThreadParams*>* buildSenderParamsCustom
// 2. URL
//
std::string notifUrl = (notification.type == ngsiv2::HttpNotification ? notification.httpInfo.url : notification.mqttInfo.url);
if (macroSubstitute(&url, notifUrl, en) == false)
if (macroSubstitute(&url, notifUrl, en, tenant, xauthToken) == false)
{
// Warning already logged in macroSubstitute()
return paramsV; // empty vector
Expand All @@ -288,15 +297,15 @@ static std::vector<SenderThreadParams*>* buildSenderParamsCustom
{
bool includePayload = (notification.type == ngsiv2::HttpNotification ? notification.httpInfo.includePayload : notification.mqttInfo.includePayload);
std::string notifPayload = (notification.type == ngsiv2::HttpNotification ? notification.httpInfo.payload : notification.mqttInfo.payload);
if (!setPayload(includePayload, notifPayload, subscriptionId, en, attrsFilter, blacklist, metadataFilter, &payload, &mimeType, &renderFormat))
if (!setPayload(includePayload, notifPayload, subscriptionId, en, tenant, xauthToken, attrsFilter, blacklist, metadataFilter, &payload, &mimeType, &renderFormat))
{
// Warning already logged in macroSubstitute()
return paramsV; // empty vector
}
}
else
{
setJsonPayload(json, en, &payload, &mimeType);
setJsonPayload(json, en, tenant, xauthToken, &payload, &mimeType);
renderFormat = NGSI_V2_CUSTOM;
}

Expand All @@ -312,7 +321,7 @@ static std::vector<SenderThreadParams*>* buildSenderParamsCustom
std::string key = it->first;
std::string value = it->second;

if ((macroSubstitute(&key, it->first, en) == false) || (macroSubstitute(&value, it->second, en) == false))
if ((macroSubstitute(&key, it->first, en, tenant, xauthToken) == false) || (macroSubstitute(&value, it->second, en, tenant, xauthToken) == false))
{
// Warning already logged in macroSubstitute()
return paramsV; // empty vector
Expand All @@ -338,7 +347,7 @@ static std::vector<SenderThreadParams*>* buildSenderParamsCustom
std::string key = it->first;
std::string value = it->second;

if ((macroSubstitute(&key, it->first, en) == false) || (macroSubstitute(&value, it->second, en) == false))
if ((macroSubstitute(&key, it->first, en, tenant, xauthToken) == false) || (macroSubstitute(&value, it->second, en, tenant, xauthToken) == false))
{
// Warning already logged in macroSubstitute()
return paramsV; // empty vector
Expand Down Expand Up @@ -403,7 +412,7 @@ static std::vector<SenderThreadParams*>* buildSenderParamsCustom
// 8. Topic (only in the case of MQTT notifications)
if (notification.type == ngsiv2::MqttNotification)
{
if (macroSubstitute(&topic, notification.mqttInfo.topic, en) == false)
if (macroSubstitute(&topic, notification.mqttInfo.topic, en, tenant, xauthToken) == false)
{
// Warning already logged in macroSubstitute()
return paramsV; // empty vector
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Copyright 2022 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 HTTP with json field using service, servicePath and authToken macros

--SHELL-INIT--
dbInit CB s1
brokerStart CB 0 IPV4 -multiservice
accumulatorStart --pretty-print

--SHELL--

#
# 01. Create custom subscription with json field using service, servicePath and authToken macros
# 02. Create E1 entity with specific service, servicePath and authToken to trigger sub
# 03. Dump accumulator, see 1 notifications
#

echo "01. Create custom subscription with json field using service, servicePath and authToken macros"
echo "=============================================================================================="
payload='{
"subject": {
"entities": [
{
"id" : "E1",
"type": "T"
}
]
},
"notification": {
"httpCustom": {
"url": "http://127.0.0.1:'${LISTENER_PORT}'/notify",
"qs": {
"S": "my-${service}",
"t": "my-${authToken}"
},
"json": {
"id": "${id}",
"S": "${service}",
"SS": "${servicePath}",
"token": "${authToken}"
}
}
}
}'
orionCurl --url /v2/subscriptions --payload "$payload" --tenant s1
echo
echo


echo "02. Create E1 entity with specific service, servicePath and authToken to trigger sub"
echo "===================================================================================="
payload='{
"id": "E1",
"type": "T",
"A": {
"value": "trigger!",
"type": "Text"
}
}'
orionCurl --url /v2/entities --payload "$payload" --tenant s1 --servicePath "/subservA" --xauthToken fgm
echo
echo


echo "03. Dump accumulator, see 1 notification"
echo "========================================"
accumulatorDump
echo
echo


--REGEXPECT--
01. Create custom subscription with json field using service, servicePath and authToken macros
==============================================================================================
HTTP/1.1 201 Created
Content-Length: 0
Location: /v2/subscriptions/REGEX([0-9a-f]{24})
Fiware-Correlator: REGEX([0-9a-f\-]{36})
Date: REGEX(.*)



02. Create E1 entity with specific service, servicePath and authToken to trigger sub
====================================================================================
HTTP/1.1 201 Created
Content-Length: 0
Location: /v2/entities/E1?type=T
Fiware-Correlator: REGEX([0-9a-f\-]{36})
Date: REGEX(.*)



03. Dump accumulator, see 1 notification
========================================
POST http://127.0.0.1:REGEX(\d+)/notify?S=my-s1&t=my-fgm
Fiware-Servicepath: /subservA
Content-Length: 51
X-Auth-Token: fgm
User-Agent: orion/REGEX(\d+\.\d+\.\d+.*)
Ngsiv2-Attrsformat: custom
Host: 127.0.0.1:REGEX(\d+)
Accept: application/json
Fiware-Service: s1
Content-Type: application/json; charset=utf-8
Fiware-Correlator: REGEX([0-9a-f\-]{36}); cbnotif=1

{
"S": "s1",
"SS": "/subservA",
"id": "E1",
"token": "fgm"
}
=======================================


--TEARDOWN--
brokerStop CB
dbDrop CB s1
accumulatorStop
Loading

0 comments on commit c6fef64

Please sign in to comment.