Skip to content

Commit f7badab

Browse files
authored
Merge pull request #1283 from jason-fox/feature/merge-patch
Add Merge-Patch Support Handling
2 parents 3ab51eb + bcc8ba5 commit f7badab

File tree

19 files changed

+839
-56
lines changed

19 files changed

+839
-56
lines changed

CHANGES_NEXT_RELEASE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
- Add NGSI-LD Merge-Patch Support Handling #1283
12
- Update to offer NGSI-LD 1.6.1. Registrations #1302
23
- Document removal of support for NGSI-LD 1.3.1 Interface

config.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ var config = {
2929
},
3030
server: {
3131
port: 4041,
32-
host: '0.0.0.0'
32+
host: '0.0.0.0',
33+
ldSupport : {
34+
null: true,
35+
datasetId: true,
36+
merge: false
37+
}
3338
},
3439
authentication: {
3540
enabled: true,

doc/howto.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,14 @@ iotAgentLib.setDataUpdateHandler(updateContextHandler);
430430
iotAgentLib.setDataQueryHandler(queryContextHandler);
431431
```
432432

433+
Where necessary, additional handlers to deal with command actuations and merge-patch operations may also be added when
434+
necessary.
435+
436+
```javascript
437+
iotAgentLib.setCommandHandler(commandHandler);
438+
iotAgentLib.setMergePatchHandler(mergePatchHandler);
439+
```
440+
433441
#### IOTA Testing
434442

435443
In order to test it, we need to create an HTTP server simulating the device. The quickest way to do that may be using

doc/installationguide.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,22 @@ overriding the group setting.
7272
}
7373
```
7474

75+
When connected to an **NGSI-LD** context broker, an IoT Agent is able to indicate whether it is willing to accept `null`
76+
values and also whether it is able to process the **NGSI-LD** `datasetId` metadata element. Setting these
77+
values to `false` will cause the IoT Agent to return a 400 **Bad Request** HTTP status code explaining that the IoT
78+
Agent does not support nulls or multi-attribute requests if they are encountered.
79+
80+
```javascript
81+
{
82+
baseRoot: '/',
83+
port: 4041,
84+
ldSupport : {
85+
null: true,
86+
datasetId: true
87+
}
88+
}
89+
```
90+
7591
- **stats**: configure the periodic collection of statistics. Use `interval` in milliseconds to set the time between
7692
stats writings.
7793

@@ -301,6 +317,8 @@ overrides.
301317
| IOTA_CB_NGSI_VERSION | `contextBroker.ngsiVersion` |
302318
| IOTA_NORTH_HOST | `server.host` |
303319
| IOTA_NORTH_PORT | `server.port` |
320+
| IOTA_LD_SUPPORT_NULL | `server.ldSupport.null` |
321+
| IOTA_LD_SUPPORT_DATASET_ID | `server.ldSupport.datasetId` |
304322
| IOTA_PROVIDER_URL | `providerUrl` |
305323
| IOTA_AUTH_ENABLED | `authentication.enabled` |
306324
| IOTA_AUTH_TYPE | `authentication.type` |

doc/usermanual.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,83 @@ values.
354354
The handler is expected to call its callback once with no parameters (failing to do so may cause unexpected behaviors in
355355
the IoT Agent).
356356

357+
##### iotagentLib.setCommandHandler()
358+
359+
###### Signature
360+
361+
```javascript
362+
function setCommandHandler(newHandler)
363+
```
364+
365+
###### Description
366+
367+
Sets the new user handler for registered entity commands. This handler will be called whenever a command request arrives, with
368+
the following parameters: (`id`, `type`, `service`, `subservice`, `attributes`, `callback`). The handler must retrieve
369+
all the corresponding information from the devices and return a NGSI entity with the requested values.
370+
371+
The callback must be invoked with the updated Context Element, using the information retrieved from the devices. E.g.:
372+
373+
```javascript
374+
callback(null, {
375+
type: "TheType",
376+
isPattern: false,
377+
id: "EntityID",
378+
attributes: [
379+
{
380+
name: "lumniscence",
381+
type: "Lumens",
382+
value: "432"
383+
}
384+
]
385+
});
386+
```
387+
388+
In the case of NGSI requests affecting multiple entities, this handler will be called multiple times, one for each
389+
entity, and all the results will be combined into a single response. Only IoT Agents which deal with actuator devices will include a handler for commands.
390+
391+
###### Params
392+
393+
- newHandler: User handler for command requests.
394+
395+
##### iotagentLib.setMergePatchHandler()
396+
397+
###### Signature
398+
399+
```javascript
400+
function setMergePatchHandler(newHandler)
401+
```
402+
403+
###### Description
404+
405+
Sets the new user handler for NGSI-LD Entity [merge-patch](https://datatracker.ietf.org/doc/html/rfc7386) requests. This handler will be called whenever a merge-patch request arrives, with
406+
the following parameters: (`id`, `type`, `service`, `subservice`, `attributes`, `callback`). The handler must retrieve
407+
all the corresponding information from the devices and return a NGSI entity with the requested values.
408+
409+
The callback must be invoked with the updated Context Element, using the information retrieved from the devices. E.g.:
410+
411+
```javascript
412+
callback(null, {
413+
type: "TheType",
414+
isPattern: false,
415+
id: "EntityID",
416+
attributes: [
417+
{
418+
name: "lumniscence",
419+
type: "Lumens",
420+
value: "432"
421+
}
422+
]
423+
});
424+
```
425+
426+
In the case of NGSI-LD requests affecting multiple entities, this handler will be
427+
called multiple times. Since merge-patch is an advanced function, not all IoT Agents
428+
will include a handler for merge-patch.
429+
430+
###### Params
431+
432+
- newHandler: User handler for merge-patch requests.
433+
357434
##### iotagentLib.setProvisioningHandler()
358435

359436
###### Signature

lib/commonConfig.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,9 @@ function processEnvironmentVariables() {
158158
'IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION',
159159
'IOTA_JSON_LD_CONTEXT',
160160
'IOTA_FALLBACK_TENANT',
161-
'IOTA_FALLBACK_PATH'
161+
'IOTA_FALLBACK_PATH',
162+
'IOTA_LD_SUPPORT_NULL',
163+
'IOTA_LD_SUPPORT_DATASET_ID'
162164
];
163165
const iotamVariables = [
164166
'IOTA_IOTAM_URL',
@@ -263,6 +265,18 @@ function processEnvironmentVariables() {
263265
config.server.port = process.env.IOTA_NORTH_PORT;
264266
}
265267

268+
config.server.ldSupport = config.server.ldSupport || {null: true, datasetId: true, merge: false};
269+
270+
if (process.env.IOTA_LD_SUPPORT_NULL) {
271+
config.server.ldSupport.null = process.env.IOTA_LD_SUPPORT_NULL === 'true';
272+
}
273+
if (process.env.IOTA_LD_SUPPORT_DATASET_ID) {
274+
config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_DATASET_ID === 'true';
275+
}
276+
if (process.env.IOTA_LD_SUPPORT_MERGE) {
277+
config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_MERGE === 'true';
278+
}
279+
266280
if (process.env.IOTA_PROVIDER_URL) {
267281
config.providerUrl = process.env.IOTA_PROVIDER_URL;
268282
}

lib/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ module.exports = {
6060
SUBSERVICE_HEADER: 'fiware-servicepath',
6161
NGSI_LD_TENANT_HEADER: 'NGSILD-Tenant',
6262
NGSI_LD_PATH_HEADER: 'NGSILD-Path',
63+
NGSI_LD_NULL: 'urn:ngsi-ld:null',
6364
//FIXME: check Keystone support this in lowercase, then change
6465
AUTH_HEADER: 'X-Auth-Token',
6566
X_FORWARDED_FOR_HEADER: 'x-forwarded-for',

lib/fiware-iotagent-lib.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ exports.getConfigurationSilently = groupConfig.getSilently;
323323
exports.findConfiguration = groupConfig.find;
324324
exports.setDataUpdateHandler = contextServer.setUpdateHandler;
325325
exports.setCommandHandler = contextServer.setCommandHandler;
326+
exports.setMergePatchHandler = contextServer.setMergePatchHandler;
326327
exports.setDataQueryHandler = contextServer.setQueryHandler;
327328
exports.setConfigurationHandler = contextServer.setConfigurationHandler;
328329
exports.setRemoveConfigurationHandler = contextServer.setRemoveConfigurationHandler;

lib/services/devices/deviceService.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ function registerDevice(deviceObj, callback) {
363363
deviceObj.type = deviceData.type;
364364
deviceObj.staticAttributes = deviceData.staticAttributes;
365365
deviceObj.commands = deviceData.commands;
366+
deviceObj.lazy = deviceData.lazy;
366367
if ('timestamp' in deviceData && deviceData.timestamp !== undefined) {
367368
deviceObj.timestamp = deviceData.timestamp;
368369
}

lib/services/devices/registrationUtils.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
314314
const properties = [];
315315
const lazy = deviceData.lazy || [];
316316
const commands = deviceData.commands || [];
317+
const supportMerge = config.getConfig().server.ldSupport.merge;
317318

318319
lazy.forEach((element) => {
319320
properties.push(element.name);
@@ -328,6 +329,9 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
328329
if (commands.length > 0){
329330
operations.push('updateOps');
330331
}
332+
if (supportMerge){
333+
operations.push('mergeEntity');
334+
}
331335

332336
if (properties.length === 0) {
333337
logger.debug(context, 'Registration with Context Provider is not needed. Device without lazy atts or commands');

0 commit comments

Comments
 (0)