Skip to content

Commit 24e08f5

Browse files
authored
Merge pull request #848 from jason-fox/feature/ngsi-ld-command
Add NGSI-LD basic Command support
2 parents 8399d52 + e099b84 commit 24e08f5

File tree

76 files changed

+2478
-2331
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+2478
-2331
lines changed

CHANGES_NEXT_RELEASE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ Refresh Documentation
22
Add NGSIv2 metadata support to device provisioned attributes
33
Fix: Error message when sending measures with unknown/undefined attribute
44
Add Null check within executeWithSecurity() to avoid crash (#829)
5+
NGSI-LD Command support (#848)
56
Basic NGSI-LD active measures support (#841)
67
Add GeoJSON and DateTime, unitCode and observedAt NGSI-LD support (#843)
78
- The NGSI v2 `TimeInstant` element has been mapped onto the NGSI-LD `observedAt` property

doc/installationguide.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ used for the same purpose. For instance:
226226
the IoTAgent runs in a single thread. For more details about multi-core functionality, please refer to the
227227
[Cluster](https://nodejs.org/api/cluster.html) module in Node.js and
228228
[this section](howto.md#iot-agent-in-multi-thread-mode) of the library documentation.
229+
- **fallbackTenant** - For Linked Data Context Brokers which do not support multi-tenancy, this provides an alternative mechanism for suppling the `NGSILD-Tenant` header.
230+
Note that for backwards compatibility with NGSI v2, the `fiware-service` header is already used as alternative if the `NGSILD-Tenant` header is not supplied.
231+
- **fallbackPath** - For Linked Data Context Brokers which do not support a service path, this provides an alternative mechanism for suppling the `NGSILD-Path` header.
232+
Note that for backwards compatibility with NGSI v2, the `fiware-service-path` header is already used as alternative if the `NGSILD-Path` header is not supplied.
229233

230234
### Configuration using environment variables
231235

@@ -282,3 +286,5 @@ overrides.
282286
| IOTA_AUTOCAST | `autocast` |
283287
| IOTA_MULTI_CORE | `multiCore` |
284288
| IOTA_JSON_LD_CONTEXT | `jsonLdContext` |
289+
| IOTA_FALLBACK_TENANT | `fallbackTenant` |
290+
| IOTA_FALLBACK_PATH | `fallbackPath` |

lib/commonConfig.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ function processEnvironmentVariables() {
9595
'IOTA_POLLING_EXPIRATION',
9696
'IOTA_POLLING_DAEMON_FREQ',
9797
'IOTA_MULTI_CORE',
98-
'IOTA_JSON_LD_CONTEXT'
98+
'IOTA_JSON_LD_CONTEXT',
99+
'IOTA_FALLBACK_TENANT',
100+
'IOTA_FALLBACK_PATH'
99101
],
100102
iotamVariables = [
101103
'IOTA_IOTAM_URL',
@@ -157,6 +159,11 @@ function processEnvironmentVariables() {
157159
config.contextBroker.jsonLdContext = process.env.IOTA_JSON_LD_CONTEXT;
158160
}
159161

162+
config.contextBroker.fallbackTenant = process.env.IOTA_FALLBACK_TENANT ||
163+
config.contextBroker.service || 'iotagent';
164+
config.contextBroker.fallbackPath = process.env.IOTA_FALLBACK_PATH ||
165+
config.contextBroker.subservice || '/';
166+
160167
// North Port Configuration (ensuring the configuration sub-object exists before start using it)
161168
if (config.server === undefined) {
162169
config.server = {};

lib/constants.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ module.exports = {
5454
TIMESTAMP_TYPE_NGSI2: 'DateTime',
5555
SERVICE_HEADER: 'fiware-service',
5656
SUBSERVICE_HEADER: 'fiware-servicepath',
57+
NGSI_LD_TENANT_HEADER: 'NGSILD-Tenant',
58+
NGSI_LD_PATH_HEADER: 'NGSILD-Path',
5759
//FIXME: check Keystone support this in lowercase, then change
5860
AUTH_HEADER: 'X-Auth-Token',
5961

lib/fiware-iotagent-lib.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ var async = require('async'),
2929
intoTrans = require('./services/common/domain').intoTrans,
3030
middlewares = require('./services/common/genericMiddleware'),
3131
db = require('./model/dbConn'),
32+
ngsiService = require('./services/ngsi/ngsiService'),
3233
subscriptions = require('./services/ngsi/subscriptionService'),
3334
statsRegistry = require('./services/stats/statsRegistry'),
3435
domainUtils = require('./services/common/domain'),
@@ -164,6 +165,10 @@ function doActivate(newConfig, callback) {
164165
config.setRegistry(registry);
165166
config.setGroupRegistry(groupRegistry);
166167
config.setCommandRegistry(commandRegistry);
168+
deviceService.init();
169+
subscriptions.init();
170+
contextServer.init();
171+
ngsiService.init();
167172

168173
commands.start();
169174

lib/services/devices/deviceService.js

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,22 @@ var async = require('async'),
3838
_ = require('underscore'),
3939
context = {
4040
op: 'IoTAgentNGSI.DeviceService'
41-
},
42-
NGSIv1 = require('./devices-NGSI-v1'),
43-
NGSIv2 = require('./devices-NGSI-v2'),
44-
NGSILD = require('./devices-NGSI-LD');
41+
};
42+
43+
let deviceHandler;
44+
45+
/**
46+
* Loads the correct device handler based on the current config.
47+
*/
48+
function init(){
49+
if (config.checkNgsiLD()) {
50+
deviceHandler = require('./devices-NGSI-LD');
51+
} else if (config.checkNgsi2()) {
52+
deviceHandler = require('./devices-NGSI-v2');
53+
} else {
54+
deviceHandler = require('./devices-NGSI-v1');
55+
}
56+
}
4557

4658
/**
4759
* Creates the initial entity representing the device in the Context Broker. This is important mainly to allow the
@@ -51,13 +63,7 @@ var async = require('async'),
5163
* @param {Object} newDevice Device object that will be stored in the database.
5264
*/
5365
function createInitialEntity(deviceData, newDevice, callback) {
54-
if (config.checkNgsiLD()) {
55-
NGSILD.createInitialEntity(deviceData, newDevice, callback);
56-
} else if (config.checkNgsi2()) {
57-
NGSIv2.createInitialEntity(deviceData, newDevice, callback);
58-
} else {
59-
NGSIv1.createInitialEntity(deviceData, newDevice, callback);
60-
}
66+
deviceHandler.createInitialEntity(deviceData, newDevice, callback);
6167
}
6268

6369
/**
@@ -348,13 +354,7 @@ function unregisterDevice(id, service, subservice, callback) {
348354
}
349355

350356
function updateRegisterDevice(deviceObj, callback) {
351-
if (config.checkNgsiLD()) {
352-
NGSILD.updateRegisterDevice(deviceObj, callback);
353-
} else if (config.checkNgsi2()) {
354-
NGSIv2.updateRegisterDevice(deviceObj, callback);
355-
} else {
356-
NGSIv1.updateRegisterDevice(deviceObj, callback);
357-
}
357+
deviceHandler.updateRegisterDevice(deviceObj, callback);
358358
}
359359

360360
/**
@@ -553,3 +553,4 @@ exports.clearRegistry = intoTrans(context, checkRegistry)(clearRegistry);
553553
exports.retrieveDevice = intoTrans(context, checkRegistry)(retrieveDevice);
554554
exports.mergeDeviceWithConfiguration = mergeDeviceWithConfiguration;
555555
exports.findConfigurationGroup = findConfigurationGroup;
556+
exports.init = init;

lib/services/devices/devices-NGSI-LD.js

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ function createInitialEntityHandlerNgsiLD(deviceData, newDevice, callback) {
7878
alarms.raise(constants.ORION_ALARM, error);
7979

8080
callback(error);
81-
} else if (response && response.statusCode === 200) {
81+
} else if (response && response.statusCode === 204 ) {
8282
alarms.release(constants.ORION_ALARM);
8383
logger.debug(context, 'Initial entity created successfully.');
8484
callback(null, newDevice);
@@ -120,7 +120,7 @@ function updateEntityHandlerNgsiLD(deviceData, updatedDevice, callback) {
120120
alarms.raise(constants.ORION_ALARM, error);
121121

122122
callback(error);
123-
} else if (response && response.statusCode === 200) {
123+
} else if (response && response.statusCode === 204) {
124124
alarms.release(constants.ORION_ALARM);
125125
logger.debug(context, 'Entity updated successfully.');
126126
callback(null, updatedDevice);
@@ -170,19 +170,17 @@ function createInitialEntityNgsiLD(deviceData, newDevice, callback) {
170170
}
171171

172172
json = ngsiLD.formatAsNGSILD(json);
173-
delete json['@context'];
174173

175174
var options = {
176175
url: config.getConfig().contextBroker.url + '/ngsi-ld/v1/entityOperations/upsert/',
177176
method: 'POST',
178177
json: [json],
179178
headers: {
180179
'fiware-service': deviceData.service,
181-
'Content-Type': 'application/ld+json',
182-
Link:
183-
'<' +
184-
config.getConfig().contextBroker.jsonLdContext +
185-
'>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"'
180+
'fiware-servicepath': deviceData.subservice,
181+
'NGSILD-Tenant': deviceData.service,
182+
'NGSILD-Path': deviceData.subservice,
183+
'Content-Type': 'application/ld+json'
186184
}
187185
};
188186

@@ -212,11 +210,10 @@ function updateEntityNgsiLD(deviceData, updatedDevice, callback) {
212210
json: {},
213211
headers: {
214212
'fiware-service': deviceData.service,
215-
'Content-Type': 'application/ld+json',
216-
Link:
217-
'<' +
218-
config.getConfig().contextBroker.jsonLdContext +
219-
'>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"'
213+
'fiware-servicepath': deviceData.subservice,
214+
'NGSILD-Tenant': deviceData.service,
215+
'NGSILD-Path': deviceData.subservice,
216+
'Content-Type': 'application/ld+json'
220217
}
221218
};
222219

lib/services/devices/registrationUtils.js

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,9 @@ function sendUnregistrationsNgsiLD(deviceData, callback) {
305305
json: true,
306306
headers: {
307307
'fiware-service': deviceData.service,
308-
'fiware-servicepath': deviceData.subservice
308+
'fiware-servicepath': deviceData.subservice,
309+
'NGSILD-Tenant': deviceData.service,
310+
'NGSILD-Path': deviceData.subservice,
309311
}
310312
};
311313
if (deviceData.cbHost && deviceData.cbHost.indexOf('://') !== -1) {
@@ -441,21 +443,17 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
441443
}
442444

443445
var properties = [];
444-
var additionalContext = {
445-
'ngsi-ld': 'https://uri.etsi.org/ngsi-ld/default-context/'
446-
};
447446
var lazy = deviceData.lazy || [];
448447
var commands = deviceData.commands || [];
449448

450449
lazy.forEach((element) => {
451450
properties.push(element.name);
452-
additionalContext[element.name] = 'ngsi-ld:' + element.name;
453451
});
454452
commands.forEach((element) => {
455453
properties.push(element.name);
456-
additionalContext[element.name] = 'ngsi-ld:' + element.name;
457454
});
458455

456+
459457
if (properties.length === 0) {
460458
logger.debug(
461459
context,
@@ -472,10 +470,6 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
472470
cbHost = 'http://' + deviceData.cbHost;
473471
}
474472

475-
var contexts = [additionalContext];
476-
if (config.getConfig().contextBroker.jsonLdContext) {
477-
contexts.push(config.getConfig().contextBroker.jsonLdContext);
478-
}
479473

480474
var id = String(deviceData.name);
481475
id = id.startsWith(NGSI_LD_URN) ? id : NGSI_LD_URN + deviceData.type + ':' + id;
@@ -492,11 +486,13 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
492486
}
493487
],
494488
endpoint: config.getConfig().providerUrl,
495-
'@context': contexts
489+
'@context': config.getConfig().contextBroker.jsonLdContext
496490
},
497491
headers: {
498492
'fiware-service': deviceData.service,
499493
'fiware-servicepath': deviceData.subservice,
494+
'NGSILD-Tenant': deviceData.service,
495+
'NGSILD-Path': deviceData.subservice,
500496
'Content-Type': 'application/ld+json'
501497
}
502498
};

lib/services/ngsi/entities-NGSI-LD.js

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ function formatAsNGSILD(json) {
265265
*/
266266
function generateNGSILDOperationHandler(operationName, entityName, typeInformation, token, options, callback) {
267267
return function(error, response, body) {
268+
var bodyAsString = body ? JSON.stringify(body, null, 4) : '';
269+
268270
if (error) {
269271
logger.error(context, 'Error found executing ' + operationName + ' action in Context Broker: %s', error);
270272

@@ -278,15 +280,16 @@ function generateNGSILDOperationHandler(operationName, entityName, typeInformati
278280
);
279281

280282
callback(new errors.BadRequest(body.orionError.details));
281-
} else if (response && operationName === 'update' && response.statusCode === 200) {
283+
} else if (response && operationName === 'update' &&
284+
(response.statusCode === 200 ||response.statusCode === 204)) {
282285
logger.debug(context, 'Received the following response from the CB: Value updated successfully\n');
283286
alarms.release(constants.ORION_ALARM);
284287
callback(null, body);
285288
} else if (response && operationName === 'query' && body !== undefined && response.statusCode === 200) {
286289
logger.debug(
287290
context,
288291
'Received the following response from the CB:\n\n%s\n\n',
289-
JSON.stringify(body, null, 4)
292+
bodyAsString
290293
);
291294
logger.debug(context, 'Value queried successfully');
292295
alarms.release(constants.ORION_ALARM);
@@ -295,7 +298,7 @@ function generateNGSILDOperationHandler(operationName, entityName, typeInformati
295298
logger.debug(
296299
context,
297300
'Received the following response from the CB:\n\n%s\n\n',
298-
JSON.stringify(body, null, 4)
301+
bodyAsString
299302
);
300303

301304
logger.error(
@@ -319,7 +322,7 @@ function generateNGSILDOperationHandler(operationName, entityName, typeInformati
319322
logger.debug(
320323
context,
321324
'Received the following response from the CB:\n\n%s\n\n',
322-
JSON.stringify(body, null, 4)
325+
bodyAsString
323326
);
324327

325328
logger.error(context, 'Operation ' + operationName + ' error connecting to the Context Broker: %j', body);
@@ -344,6 +347,46 @@ function generateNGSILDOperationHandler(operationName, entityName, typeInformati
344347
};
345348
}
346349

350+
/**
351+
* Makes a query to the Device's entity in the context broker using NGSI-LD, with the list
352+
* of attributes given by the 'attributes' array.
353+
*
354+
* @param {String} entityName Name of the entity to query.
355+
* @param {Array} attributes Attribute array containing the names of the attributes to query.
356+
* @param {Object} typeInformation Configuration information for the device.
357+
* @param {String} token User token to identify against the PEP Proxies (optional).
358+
*/
359+
function sendQueryValueNgsiLD(entityName, attributes, typeInformation, token, callback) {
360+
var url = '/ngsi-ld/v1/entities/urn:ngsi-ld:' + typeInformation.type + ':' + entityName;
361+
362+
if (attributes && attributes.length > 0) {
363+
url = url + '?attrs=' + attributes.join(',');
364+
}
365+
366+
var options = NGSIUtils.createRequestObject(url, typeInformation, token);
367+
options.method = 'GET';
368+
options.json = true;
369+
370+
if (!typeInformation || !typeInformation.type) {
371+
callback(new errors.TypeNotFound(null, entityName));
372+
return;
373+
}
374+
375+
logger.debug(context, 'Querying values of the device in the Context Broker at [%s]', options.url);
376+
logger.debug(context, 'Using the following request:\n\n%s\n\n', JSON.stringify(options, null, 4));
377+
378+
request(
379+
options,
380+
generateNGSILDOperationHandler('query', entityName, typeInformation, token, options, function(error, result) {
381+
if (error) {
382+
callback(error);
383+
} else {
384+
NGSIUtils.applyMiddlewares(NGSIUtils.queryMiddleware, result, typeInformation, callback);
385+
}
386+
})
387+
);
388+
}
389+
347390
/**
348391
* Makes an update in the Device's entity in the context broker, with the values given in the 'attributes' array.
349392
* This array should comply to the NGSI-LD's attribute format.
@@ -505,3 +548,4 @@ function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, c
505548

506549
exports.formatAsNGSILD = formatAsNGSILD;
507550
exports.sendUpdateValue = sendUpdateValueNgsiLD;
551+
exports.sendQueryValue = sendQueryValueNgsiLD;

0 commit comments

Comments
 (0)