Skip to content

Commit 2ac3b07

Browse files
committed
Merge branch 'master' into feature/842_ngsi_ld
2 parents 674830e + 8b3f232 commit 2ac3b07

14 files changed

+443
-41
lines changed

CHANGES_NEXT_RELEASE

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ Add basic NGSI-LD support as experimental feature (#842)
66
- Multi-measures
77
- Lazy Attributes
88
- Commands
9+
Add: enable use group commands in device and register it in iotagent-manager
10+
Add: extends commands definition to add mqtt options (qos, retain)
911
Add: include findTypeSilently for groups to log some false errors as debug instead of alarm
1012
Add: include `description` field in group schema
1113
Add: include from in log context (#918)
1214
Fix: Update internal attributes in Group update (#917)
1315
Fix: Static attributes from service not applied if device has static attributes (#757)
1416
Move Docker secret support inside the Node Application - remove Entrypoint (#885)
15-
Fix: IOTA_EXPLICIT_ATTRS env var was not working
17+
Fix: IOTA_EXPLICIT_ATTRS env var was not working
18+
Add lax validation mode using IOTA_RELAX_TEMPLATE_VALIDATION (#920)

doc/installationguide.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ used for the same purpose. For instance:
227227
- **singleConfigurationMode**: enables the Single Configuration mode for backwards compatibility (see description in
228228
the Overview). Default to false.
229229
- **timestamp**: if this flag is activated:
230-
- For NGSIv1/NGSIv2, the IoT Agent will add a `TimeInstant` metadata attribute to all the attributes updated from device information.
230+
- For NGSIv1/NGSIv2, the IoT Agent will add a `TimeInstant` metadata attribute to all the attributes updated from device information.
231231
This flag is overwritten by `timestamp` flag in group or device
232232
- With NGSI-LD, the standard `observedAt` property-of-a-property is created instead.
233233
- **defaultResource**: default string to use as resource for the registration of new Configurations (if no resource is
@@ -255,6 +255,11 @@ used for the same purpose. For instance:
255255
Note that for backwards compatibility with NGSI v2, the `fiware-servicepath` header is already used as alternative if the `NGSILD-Path` header is not supplied. Note that NGSILD-Path has not yet been included in the NGSI-LD standard (it has been proposed for the next update of the standard, but the final decision has yet been confirmed), take into account it could change
256256
- **explicitAttrs**: if this flag is activated, only provisioned attributes will be processed to Context Broker.
257257
This flag is overwritten by `explicitAttrs` flag in group or device provision.
258+
- **relaxTemplateValidation**: if this flag is activated, `objectId` attributes for incoming devices are not validated,
259+
and may exceptionally include characters (such as semi-colons) which are
260+
[forbidden](https://fiware-orion.readthedocs.io/en/master/user/forbidden_characters/index.html) according to the NGSI
261+
specification. When provisioning devices, it is necessary that the developer provides valid `objectId`-`name` mappings
262+
whenever relaxed mode is used, to prevent the consumption of forbidden characters.
258263

259264
### Configuration using environment variables
260265

@@ -264,7 +269,7 @@ with container-based technologies, like Docker, Heroku, etc...
264269
The following table shows the accepted environment variables, as well as the configuration parameter the variable
265270
overrides.
266271

267-
| Environment variable | Configuration attribute |
272+
| Environment variable | Configuration attribute |
268273
| :------------------------------- | :------------------------------ |
269274
| IOTA_CB_URL | `contextBroker.url` |
270275
| IOTA_CB_HOST | `contextBroker.host` |
@@ -317,6 +322,8 @@ overrides.
317322
| IOTA_FALLBACK_PATH | `fallbackPath` |
318323
| IOTA_DEFAULT_EXPRESSION_LANGUAGE | `defaultExpressionLanguage` |
319324
| IOTA_EXPLICIT_ATTRS | `explicitAttrs` |
325+
| IOTA_RELAX_TEMPLATE_VALIDATION | `relaxTemplateValidation` |
320326

321327
Note:
322328
- If you need to pass more than one JSON-LD context, you can define the IOTA_JSON_LD_CONTEXT environment variable as a comma separated list of contexts (e.g. `'http://context1.json-ld,http://context2.json-ld'`)
329+

lib/commonConfig.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ function processEnvironmentVariables() {
164164
'IOTA_POLLING_DAEMON_FREQ',
165165
'IOTA_MULTI_CORE',
166166
'IOTA_DEFAULT_EXPRESSION_LANGUAGE',
167+
'IOTA_RELAX_TEMPLATE_VALIDATION',
167168
'IOTA_JSON_LD_CONTEXT',
168169
'IOTA_FALLBACK_TENANT',
169170
'IOTA_FALLBACK_PATH'
@@ -474,6 +475,11 @@ function processEnvironmentVariables() {
474475
config.defaultExpressionLanguage =
475476
process.env.IOTA_DEFAULT_EXPRESSION_LANGUAGE;
476477
}
478+
if (process.env.IOTA_RELAX_TEMPLATE_VALIDATION) {
479+
config.relaxTemplateValidation = process.env.IOTA_RELAX_TEMPLATE_VALIDATION === 'true';
480+
} else {
481+
config.relaxTemplateValidation = config.relaxTemplateValidation === true;
482+
}
477483
}
478484

479485
function setConfig(newConfig) {

lib/services/common/iotManagerService.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ function register(callback) {
5353
service_path: service.subservice,
5454
attributes: service.attributes,
5555
static_attributes: service.staticAttributes,
56+
commands: service.commands,
5657
description: service.description,
5758
timezone: service.timezone,
5859
timestamp: service.timestamp,

lib/services/devices/deviceService.js

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,9 @@ function mergeArrays(original, newArray) {
122122
* @param {Object} configuration Configuration data.
123123
*/
124124
function mergeDeviceWithConfiguration(fields, defaults, deviceData, configuration, callback) {
125-
logger.debug(context, 'deviceData after merge with conf: %j', deviceData);
125+
logger.debug(context, 'deviceData before merge with conf: %j', deviceData);
126126
for (let i = 0; i < fields.length; i++) {
127127
const confField = fields[i] === 'active' ? 'attributes' : fields[i];
128-
129128
if (deviceData && deviceData[fields[i]] && ['active', 'lazy', 'commands'].indexOf(fields[i]) >= 0) {
130129
deviceData[fields[i]] = deviceData[fields[i]].map(setDefaultAttributeIds);
131130
} else if (deviceData && deviceData[fields[i]] && ['internalAttributes'].indexOf(fields[i]) >= 0) {
@@ -159,7 +158,7 @@ function mergeDeviceWithConfiguration(fields, defaults, deviceData, configuratio
159158
if (configuration && configuration.cbHost) {
160159
deviceData.cbHost = configuration.cbHost;
161160
}
162-
logger.debug(context, 'deviceData before merge with conf: %j', deviceData);
161+
logger.debug(context, 'deviceData after merge with conf: %j', deviceData);
163162
callback(null, deviceData);
164163
}
165164

@@ -187,7 +186,13 @@ function findConfigurationGroup(deviceObj, callback) {
187186
} else {
188187
config
189188
.getGroupRegistry()
190-
.findTypeSilently(deviceObj.service, deviceObj.subservice, deviceObj.type, deviceObj.apikey, handlerGroupFind);
189+
.findTypeSilently(
190+
deviceObj.service,
191+
deviceObj.subservice,
192+
deviceObj.type,
193+
deviceObj.apikey,
194+
handlerGroupFind
195+
);
191196
}
192197
}
193198

@@ -258,35 +263,33 @@ function registerDevice(deviceObj, callback) {
258263

259264
logger.debug(context, 'Registering device into NGSI Service:\n%s', JSON.stringify(deviceData, null, 4));
260265

261-
async.waterfall(
262-
[
263-
apply(registrationUtils.sendRegistrations, false, deviceData),
264-
apply(registrationUtils.processContextRegistration, deviceData),
265-
apply(createInitialEntity, deviceData)
266-
],
267-
function(error, results) {
268-
if (error) {
269-
callback(error);
270-
} else {
271-
deviceObj.registrationId = results.registrationId;
272-
deviceObj.name = deviceData.name;
273-
deviceObj.service = deviceData.service;
274-
deviceObj.subservice = deviceData.subservice;
275-
deviceObj.type = deviceData.type;
276-
deviceObj.staticAttributes = deviceData.staticAttributes;
277-
if ('timestamp' in deviceData && deviceData.timestamp !== undefined) {
278-
deviceObj.timestamp = deviceData.timestamp;
279-
}
280-
if ('autoprovision' in deviceData && deviceData.autoprovision !== undefined) {
281-
deviceObj.autoprovision = deviceData.autoprovision;
282-
}
283-
if ('explicitAttrs' in deviceData && deviceData.explicitAttrs !== undefined) {
284-
deviceObj.explicitAttrs = deviceData.explicitAttrs;
285-
}
286-
config.getRegistry().store(deviceObj, callback);
266+
async.waterfall([
267+
apply(registrationUtils.sendRegistrations, false, deviceData),
268+
apply(registrationUtils.processContextRegistration, deviceData),
269+
apply(createInitialEntity, deviceData)
270+
], function(error, results) {
271+
if (error) {
272+
callback(error);
273+
} else {
274+
deviceObj.registrationId = results.registrationId;
275+
deviceObj.name = deviceData.name;
276+
deviceObj.service = deviceData.service;
277+
deviceObj.subservice = deviceData.subservice;
278+
deviceObj.type = deviceData.type;
279+
deviceObj.staticAttributes = deviceData.staticAttributes;
280+
deviceObj.commands = deviceData.commands;
281+
if ('timestamp' in deviceData && deviceData.timestamp !== undefined) {
282+
deviceObj.timestamp = deviceData.timestamp;
287283
}
284+
if ('autoprovision' in deviceData && deviceData.autoprovision !== undefined) {
285+
deviceObj.autoprovision = deviceData.autoprovision;
286+
}
287+
if ('explicitAttrs' in deviceData && deviceData.explicitAttrs !== undefined) {
288+
deviceObj.explicitAttrs = deviceData.explicitAttrs;
289+
}
290+
config.getRegistry().store(deviceObj, callback);
288291
}
289-
);
292+
});
290293
}
291294

292295
async.waterfall(

lib/services/northBound/deviceProvisioningServer.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ var async = require('async'),
3636
apply = async.apply,
3737
provisioningHandler,
3838
removeDeviceHandler,
39-
updateDeviceTemplate = require('../../templates/updateDevice.json'),
40-
createDeviceTemplate = require('../../templates/createDevice.json'),
39+
updateDeviceTemplate,
40+
createDeviceTemplate,
4141
mandatoryHeaders = [
4242
'fiware-service',
4343
'fiware-servicepath'
@@ -65,6 +65,20 @@ var async = require('async'),
6565
explicitAttrs: 'explicitAttrs'
6666
};
6767

68+
/**
69+
* Load the templates for validating provisioning requests. The introduction of "Lax Mode" enables the addition of
70+
* characters which valid in the OPU-UA IoT Agent (e.g. semi-colons) but are usually forbidden by other IoT Agents
71+
*/
72+
function setConfiguration(config){
73+
if (config.relaxTemplateValidation) {
74+
updateDeviceTemplate = require('../../templates/updateDeviceLax.json');
75+
createDeviceTemplate = require('../../templates/createDeviceLax.json');
76+
} else {
77+
updateDeviceTemplate = require('../../templates/updateDevice.json');
78+
createDeviceTemplate = require('../../templates/createDevice.json');
79+
}
80+
}
81+
6882
/**
6983
* Express middleware to handle incoming device provisioning requests. Every request is validated and handled to the
7084
* NGSI Service for the registration.
@@ -163,7 +177,8 @@ function attributeToProvisioningAPIFormat(attribute) {
163177
expression: attribute.expression,
164178
reverse: attribute.reverse,
165179
entity_name: attribute.entity_name,
166-
entity_type: attribute.entity_type
180+
entity_type: attribute.entity_type,
181+
mqtt: attribute.mqtt
167182
};
168183
}
169184

@@ -249,7 +264,7 @@ function handleRemoveDevice(req, res, next) {
249264
} else {
250265
callback(new errors.DeviceNotFound(deviceId));
251266
}
252-
});
267+
});
253268
}
254269

255270
function applyRemoveDeviceHandler(device, callback) {
@@ -365,6 +380,7 @@ function clear(callback) {
365380
callback();
366381
}
367382

383+
exports.setConfiguration = setConfiguration;
368384
exports.loadContextRoutes = intoTrans(context, loadContextRoutes);
369385
exports.setProvisioningHandler = intoTrans(context, setProvisioningHandler);
370386
exports.setRemoveDeviceHandler = intoTrans(context, setRemoveDeviceHandler);

lib/services/northBound/northboundServer.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ function start(config, callback) {
4242
let baseRoot = '/';
4343
let iotaInformation;
4444

45+
deviceProvisioning.setConfiguration(config);
46+
4547
northboundServer = {
4648
server: null,
4749
app: express(),

lib/templates/createDevice.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,22 @@
168168
"description": "Value of the command (currently ignored)",
169169
"type": "string",
170170
"pattern": "^([^<>();'=\"]+)*$"
171+
},
172+
"mqtt": {
173+
"description": "Mqtt properties",
174+
"type": "object",
175+
"additionalProperties": false,
176+
"properties": {
177+
"retain": {
178+
"description": "MQTT Retain message",
179+
"type": "boolean"
180+
},
181+
"qos": {
182+
"description": "MQTT QoS level",
183+
"type": "string",
184+
"pattern": "^([0-2])$"
185+
}
186+
}
171187
}
172188
}
173189
}

0 commit comments

Comments
 (0)