Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Add basic NGSI-LD support as experimental feature (#842)
- Multi-measures
- Lazy Attributes
- Commands
- Mixed mode (based in ngsiVersion field in the provisioning API)
Update codebase to use ES6
- Remove JSHint and jshint overrides
- Add esLint using standard tamia presets
Expand Down
8 changes: 5 additions & 3 deletions doc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ correspondence between the API resource fields and the same fields in the databa
| `attributes` | `attributes` | list of common active attributes of the device. For each attribute, its `name` and `type` must be provided, additional `metadata` is optional. |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest to add the mixed mode to the NGSI-LD feature sublist in CHANGES_NEXT_RELEASE. I mean:

Add basic NGSI-LD support as experimental feature (#842)
...
- Commands
- Mixed mode (based in ngsiVersion field in the provisioning API)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed 3dee480

| `static_attributes` | `staticAttributes` | this attributes will be added to all the entities of this group 'as is', additional `metadata` is optional. |
| `internal_attributes` | `internalAttributes` | optional section with free format, to allow specific IoT Agents to store information along with the devices in the Device Registry. |
| `expressionLanguage` | `expresionLanguage` | optional boolean value, to set expression language used to compute expressions, possible values are: legacy or jexl. When not set or wrongly set, legacy is used as default value. |
| `explicitAttrs` | `explicitAttrs` | optional boolean value, to support selective ignore of measures so that IOTA doesn’t progress. If not specified default is false. |
| `expressionLanguage` | `expresionLanguage` | optional boolean value, to set expression language used to compute expressions, possible values are: legacy or jexl. When not set or wrongly set, `legacy` is used as default value. |
| `explicitAttrs` | `explicitAttrs` | optional boolean value, to support selective ignore of measures so that IOTA doesn’t progress. If not specified default is `false`. |
| `ngsiVersion` | `ngsiVersion` | optional string value used in mixed mode to switch between **NGSI-v2** and **NGSI-LD** payloads. Possible values are: `v2` or `ld`. The default is `v2`. When not running in mixed mode, this field is ignored.

### Service Group Endpoint

Expand Down Expand Up @@ -224,7 +225,8 @@ the API resource fields and the same fields in the database model.
| `internal_attributes` | `internalAttributes` | List of internal attributes with free format for specific IoT Agent configuration | LWM2M mappings from object URIs to attributes |
| `static_attributes` | `staticAttributes` | List of static attributes to append to the entity. All the updateContext requests to the CB will have this set of attributes appended. | `[ { "name": "attr_name", "type": "Text" } ]` |
| `expressionLanguage` | `expresionLanguage` | optional boolean value, to set expression language used to compute expressions, possible values are: legacy or jexl. When not set or wrongly set, legacy is used as default value. |
| `explicitAttrs` | `explicitAttrs` | Boolean value to support selective ignore of measures for device so that IOTA doesn’t progress. If not specified default is false. | `true/false` |
| `explicitAttrs` | `explicitAttrs` | Boolean value to support selective ignore of measures for device so that IOTA doesn’t progress. If not specified default is `false`. | `true/false` |
| `ngsiVersion` | `ngsiVersion` | optional string value used in mixed mode to switch between **NGSI-v2** and **NGSI-LD** payloads. The default is `v2`. When not running in mixed mode, this field is ignored. | `v2/ld` |

#### Attribute lists

Expand Down
21 changes: 18 additions & 3 deletions doc/installationguide.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ These are the parameters that can be configured in the global section:
}
```

- If you want to use NGSI v2:
- If you want to use **NGSI v2**:

```javascript
{
Expand All @@ -30,7 +30,7 @@ These are the parameters that can be configured in the global section:
}
```

- If you want to use NGSI-LD (experimental):
- If you want to use **NGSI-LD** (experimental):

```javascript
{
Expand All @@ -43,7 +43,22 @@ These are the parameters that can be configured in the global section:

Where `http://context.json-ld` is the location of the NGSI-LD `@context` element which provides additional information
allowing the computer to interpret the rest of the data with more clarity and depth. Read the
[JSON-LD specification](https://w3c.github.io/json-ld-syntax/#the-context) for more informtaion.
[JSON-LD specification](https://w3c.github.io/json-ld-syntax/#the-context) for more information.

- If you want to support a "mixed" mode with both **NGSI-v2** and **NGSI-LD** (experimental):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest to add an explanation of what mixed mode means. For instance:

In mixed mode the NGSI version to use is chosen at group or device provisioning time, instead of using an static setting for that. The ngsiVersion field in the provisioning API is used to specify this, at both group at device level (the device level overriding the group setting).

It is just an example, it can be improved, of course.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed 52cb089


```javascript
{
host: '192.168.56.101',
port: '1026',
ngsiVersion: 'mixed',
jsonLdContext: 'http://context.json-ld' // or ['http://context1.json-ld','http://context2.json-ld'] if you need more than one
}
```

Under mixed mode, **NGSI v2** payloads are used for context broker communications by default, but this payload may also be switched
to **NGSI LD** at service group or device provisioning time using the `ngsiVersion` field in the provisioning API.
The `ngsiVersion` field switch may be added at either group or device level, with the device level overriding the group setting.

- **server**: configuration used to create the Context Server (port where the IoT Agent will be listening as a Context
Provider and base root to prefix all the paths). The `port` attribute is required. If no `baseRoot` attribute is
Expand Down
37 changes: 24 additions & 13 deletions lib/commonConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -504,30 +504,40 @@ function getCommandRegistry() {
}

/**
* It checks if the configuration file states the use of NGSIv2
* Returns the supported NGSI format
*
* @return {boolean} Result of the checking
* @return {string} the supported NGSI format
*/
function checkNgsi2() {
if (config.contextBroker && config.contextBroker.ngsiVersion && config.contextBroker.ngsiVersion === 'v2') {
return true;
function ngsiVersion() {
if (config && config.contextBroker && config.contextBroker.ngsiVersion) {
return config.contextBroker.ngsiVersion.toLowerCase();
}

return false;
return 'unknown';
}

/**
* It checks if the configuration file states the use of NGSI-LD
* It checks if the configuration file states a non-legacy format,
* either v2, LD or mixed.
*
* @return {boolean} Result of the checking
*/
function checkNgsiLD() {
if (config.contextBroker && config.contextBroker.ngsiVersion && config.contextBroker.ngsiVersion === 'ld') {
return true;
function isCurrentNgsi() {
if (config.contextBroker && config.contextBroker.ngsiVersion) {
const version = config.contextBroker.ngsiVersion.toLowerCase();
return version === 'v2' || version === 'ld' || version === 'mixed';
}

return false;
}
/**
* It checks if a combination of typeInformation or common Config is LD
*
* @return {boolean} Result of the checking
*/
function checkNgsiLD(typeInformation) {
const format = typeInformation.ngsiVersion || ngsiVersion();
return format.toLowerCase() === 'ld';
}

function setSecurityService(newSecurityService) {
securityService = newSecurityService;
}
Expand All @@ -544,8 +554,9 @@ exports.setGroupRegistry = setGroupRegistry;
exports.getGroupRegistry = getGroupRegistry;
exports.setCommandRegistry = setCommandRegistry;
exports.getCommandRegistry = getCommandRegistry;
exports.checkNgsi2 = checkNgsi2;
exports.ngsiVersion = ngsiVersion;
exports.checkNgsiLD = checkNgsiLD;
exports.isCurrentNgsi = isCurrentNgsi;
exports.setSecurityService = setSecurityService;
exports.getSecurityService = getSecurityService;
exports.getSecretData = getSecretData;
3 changes: 1 addition & 2 deletions lib/fiware-iotagent-lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const ngsi = require('./services/ngsi/ngsiService');
const intoTrans = require('./services/common/domain').intoTrans;
const middlewares = require('./services/common/genericMiddleware');
const db = require('./model/dbConn');
const ngsiService = require('./services/ngsi/ngsiService');
const subscriptions = require('./services/ngsi/subscriptionService');
const statsRegistry = require('./services/stats/statsRegistry');
const domainUtils = require('./services/common/domain');
Expand Down Expand Up @@ -179,7 +178,7 @@ function doActivate(newConfig, callback) {
deviceService.init();
subscriptions.init();
contextServer.init();
ngsiService.init();
ngsi.init();

commands.start();

Expand Down
3 changes: 2 additions & 1 deletion lib/model/Device.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ const Device = new Schema({
internalAttributes: Object,
autoprovision: Boolean,
expressionLanguage: String,
explicitAttrs: Boolean
explicitAttrs: Boolean,
ngsiVersion: String
});

function load(db) {
Expand Down
3 changes: 2 additions & 1 deletion lib/model/Group.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ const Group = new Schema({
internalAttributes: Array,
autoprovision: Boolean,
expressionLanguage: String,
explicitAttrs: Boolean
explicitAttrs: Boolean,
ngsiVersion: String
});

function load(db) {
Expand Down
4 changes: 2 additions & 2 deletions lib/plugins/attributeAlias.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function extractAllMappings(typeInformation) {
function applyAlias(mappings) {
return function aliasApplier(attribute) {
if (mappings.direct[attribute.name]) {
if (config.checkNgsi2() || config.checkNgsiLD()) {
if (config.isCurrentNgsi()) {
/*jshint camelcase: false */
attribute.object_id = attribute.name; // inverse not usefull due to collision
}
Expand All @@ -93,7 +93,7 @@ function applyAlias(mappings) {
*/
function updateAttribute(entity, typeInformation, callback) {
const mappings = extractAllMappings(typeInformation);
if (config.checkNgsi2() || config.checkNgsiLD()) {
if (config.isCurrentNgsi()) {
let attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
attsArray = attsArray.map(applyAlias(mappings));
entity = utils.createNgsi2Entity(entity.id, entity.type, attsArray, true);
Expand Down
2 changes: 1 addition & 1 deletion lib/plugins/bidirectionalData.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function sendSubscriptions(device, attributeList, callback) {

logger.debug(context, 'Sending bidirectionality subscriptions for device [%s]', device.id);

if (config.checkNgsi2() || config.checkNgsiLD()) {
if (config.isCurrentNgsi()) {
async.map(attributeList, sendSingleSubscriptionNgsi2, callback);
} else {
async.map(attributeList, sendSingleSubscriptionNgsi1, callback);
Expand Down
2 changes: 1 addition & 1 deletion lib/plugins/expressionParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ function expressionApplier(context, typeInformation) {
};

/*jshint camelcase: false */
if ((config.checkNgsi2() || config.checkNgsiLD()) && attribute.object_id) {
if (config.isCurrentNgsi() && attribute.object_id) {
newAttribute.object_id = attribute.object_id;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/plugins/expressionPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function update(entity, typeInformation, callback) {
}

try {
if (config.checkNgsi2() || config.checkNgsiLD()) {
if (config.isCurrentNgsi()) {
let attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
attsArray = processEntityUpdateNgsi2(attsArray);
entity = utils.createNgsi2Entity(entity.id, entity.type, attsArray, true);
Expand Down
2 changes: 1 addition & 1 deletion lib/plugins/jexlParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ function expressionApplier(context, typeInformation) {
};

/*jshint camelcase: false */
if (config.checkNgsi2() && attribute.object_id) {
if (config.isCurrentNgsi() && attribute.object_id) {
newAttribute.object_id = attribute.object_id;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/plugins/multiEntity.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ function updateAttributeNgsi2(entity, typeInformation, callback) {
}

function updateAttribute(entity, typeInformation, callback) {
if (config.checkNgsi2() || config.checkNgsiLD()) {
if (config.isCurrentNgsi()) {
updateAttributeNgsi2(entity, typeInformation, callback);
} else {
updateAttributeNgsi1(entity, typeInformation, callback);
Expand Down
7 changes: 3 additions & 4 deletions lib/plugins/pluginUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@ function createProcessAttribute(fn, attributeType) {
if (attribute.type && attribute.type === attributeType) {
attribute.value = fn(attribute.value);
}

if (config.checkNgsi2() || config.checkNgsiLD()) {
if (config.isCurrentNgsi()) {
// This code is backwards compatible to process metadata in the older NGSIv1-style (array)
// as well as supporting the newer NGSIv2-style (object). The redundant Array Check can be
// therefore be removed if/when NGSIv1 support is removed from the library.
Expand Down Expand Up @@ -149,7 +148,7 @@ function createUpdateFilter(fn, attributeType) {
return entity;
}

if (config.checkNgsi2() || config.checkNgsiLD()) {
if (config.isCurrentNgsi()) {
entity = processEntityUpdateNgsi2(entity);
} else {
entity.contextElements = entity.contextElements.map(processEntityUpdateNgsi1);
Expand Down Expand Up @@ -184,7 +183,7 @@ function createQueryFilter(fn, attributeType) {
return entity;
}

if (config.checkNgsi2() || config.checkNgsiLD()) {
if (config.isCurrentNgsi()) {
entity = processEntityQueryNgsi2(entity);
} else {
entity.contextResponses = entity.contextResponses.map(processEntityQueryNgsi1);
Expand Down
2 changes: 1 addition & 1 deletion lib/plugins/timestampProcessPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function updatePluginNgsi1(entity, entityType, callback) {
* @param {Object} entity NGSI Entity as it would have been sent before the plugin.
*/
function updatePlugin(entity, entityType, callback) {
if (config.checkNgsi2() || config.checkNgsiLD()) {
if (config.isCurrentNgsi()) {
updatePluginNgsi2(entity, entityType, callback);
} else {
updatePluginNgsi1(entity, entityType, callback);
Expand Down
3 changes: 2 additions & 1 deletion lib/services/common/iotManagerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ function register(callback) {
timestamp: service.timestamp,
autoprovision: service.autoprovision,
explicitAttrs: service.explicitAttrs,
expressionLanguage: service.expressionLanguage
expressionLanguage: service.expressionLanguage,
ngsiVersion: service.ngsiVersion
};
}

Expand Down
61 changes: 32 additions & 29 deletions lib/services/devices/deviceRegistryMongoDB.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,32 @@ let context = {
op: 'IoTAgentNGSI.MongoDBDeviceRegister'
};

const attributeList = [
'id',
'type',
'name',
'service',
'subservice',
'lazy',
'commands',
'staticAttributes',
'active',
'registrationId',
'internalId',
'internalAttributes',
'resource',
'apikey',
'protocol',
'endpoint',
'transport',
'polling',
'timestamp',
'autoprovision',
'explicitAttrs',
'expressionLanguage',
'ngsiVersion'
];

/**
* Generates a handler for the save device operations. The handler will take the customary error and the saved device
* as the parameters (and pass the serialized DAO as the callback value).
Expand All @@ -58,35 +84,11 @@ function saveDeviceHandler(callback) {
* @param {Object} newDevice Device object to be stored
*/
function storeDevice(newDevice, callback) {
const deviceObj = new Device.model(); // eslint-disable-line new-cap
const attributeList = [
'id',
'type',
'name',
'service',
'subservice',
'lazy',
'commands',
'staticAttributes',
'active',
'registrationId',
'internalId',
'internalAttributes',
'resource',
'apikey',
'protocol',
'endpoint',
'transport',
'polling',
'timestamp',
'autoprovision',
'explicitAttrs',
'expressionLanguage'
];

for (let i = 0; i < attributeList.length; i++) {
deviceObj[attributeList[i]] = newDevice[attributeList[i]];
}
/* eslint-disable-next-line new-cap */
const deviceObj = new Device.model();
attributeList.forEach((key) => {
deviceObj[key] = newDevice[key];
});

// Ensure protocol is in newDevice
if (!newDevice.protocol && config.getConfig().iotManager && config.getConfig().iotManager.protocol) {
Expand Down Expand Up @@ -286,6 +288,7 @@ function update(device, callback) {
data.name = device.name;
data.type = device.type;
data.explicitAttrs = device.explicitAttrs;
data.ngsiVersion = device.ngsiVersion;

data.save(saveDeviceHandler(callback));
}
Expand Down
Loading