Skip to content

Commit

Permalink
Merge pull request #1283 from jason-fox/feature/merge-patch
Browse files Browse the repository at this point in the history
Add Merge-Patch Support Handling
  • Loading branch information
fgalan authored Mar 15, 2023
2 parents 3ab51eb + bcc8ba5 commit f7badab
Show file tree
Hide file tree
Showing 19 changed files with 839 additions and 56 deletions.
1 change: 1 addition & 0 deletions CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
- Add NGSI-LD Merge-Patch Support Handling #1283
- Update to offer NGSI-LD 1.6.1. Registrations #1302
- Document removal of support for NGSI-LD 1.3.1 Interface
7 changes: 6 additions & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ var config = {
},
server: {
port: 4041,
host: '0.0.0.0'
host: '0.0.0.0',
ldSupport : {
null: true,
datasetId: true,
merge: false
}
},
authentication: {
enabled: true,
Expand Down
8 changes: 8 additions & 0 deletions doc/howto.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,14 @@ iotAgentLib.setDataUpdateHandler(updateContextHandler);
iotAgentLib.setDataQueryHandler(queryContextHandler);
```

Where necessary, additional handlers to deal with command actuations and merge-patch operations may also be added when
necessary.

```javascript
iotAgentLib.setCommandHandler(commandHandler);
iotAgentLib.setMergePatchHandler(mergePatchHandler);
```

#### IOTA Testing

In order to test it, we need to create an HTTP server simulating the device. The quickest way to do that may be using
Expand Down
18 changes: 18 additions & 0 deletions doc/installationguide.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,22 @@ overriding the group setting.
}
```

When connected to an **NGSI-LD** context broker, an IoT Agent is able to indicate whether it is willing to accept `null`
values and also whether it is able to process the **NGSI-LD** `datasetId` metadata element. Setting these
values to `false` will cause the IoT Agent to return a 400 **Bad Request** HTTP status code explaining that the IoT
Agent does not support nulls or multi-attribute requests if they are encountered.

```javascript
{
baseRoot: '/',
port: 4041,
ldSupport : {
null: true,
datasetId: true
}
}
```

- **stats**: configure the periodic collection of statistics. Use `interval` in milliseconds to set the time between
stats writings.

Expand Down Expand Up @@ -301,6 +317,8 @@ overrides.
| IOTA_CB_NGSI_VERSION | `contextBroker.ngsiVersion` |
| IOTA_NORTH_HOST | `server.host` |
| IOTA_NORTH_PORT | `server.port` |
| IOTA_LD_SUPPORT_NULL | `server.ldSupport.null` |
| IOTA_LD_SUPPORT_DATASET_ID | `server.ldSupport.datasetId` |
| IOTA_PROVIDER_URL | `providerUrl` |
| IOTA_AUTH_ENABLED | `authentication.enabled` |
| IOTA_AUTH_TYPE | `authentication.type` |
Expand Down
77 changes: 77 additions & 0 deletions doc/usermanual.md
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,83 @@ values.
The handler is expected to call its callback once with no parameters (failing to do so may cause unexpected behaviors in
the IoT Agent).

##### iotagentLib.setCommandHandler()

###### Signature

```javascript
function setCommandHandler(newHandler)
```

###### Description

Sets the new user handler for registered entity commands. This handler will be called whenever a command request arrives, with
the following parameters: (`id`, `type`, `service`, `subservice`, `attributes`, `callback`). The handler must retrieve
all the corresponding information from the devices and return a NGSI entity with the requested values.

The callback must be invoked with the updated Context Element, using the information retrieved from the devices. E.g.:

```javascript
callback(null, {
type: "TheType",
isPattern: false,
id: "EntityID",
attributes: [
{
name: "lumniscence",
type: "Lumens",
value: "432"
}
]
});
```

In the case of NGSI requests affecting multiple entities, this handler will be called multiple times, one for each
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.

###### Params

- newHandler: User handler for command requests.

##### iotagentLib.setMergePatchHandler()

###### Signature

```javascript
function setMergePatchHandler(newHandler)
```

###### Description

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
the following parameters: (`id`, `type`, `service`, `subservice`, `attributes`, `callback`). The handler must retrieve
all the corresponding information from the devices and return a NGSI entity with the requested values.

The callback must be invoked with the updated Context Element, using the information retrieved from the devices. E.g.:

```javascript
callback(null, {
type: "TheType",
isPattern: false,
id: "EntityID",
attributes: [
{
name: "lumniscence",
type: "Lumens",
value: "432"
}
]
});
```

In the case of NGSI-LD requests affecting multiple entities, this handler will be
called multiple times. Since merge-patch is an advanced function, not all IoT Agents
will include a handler for merge-patch.

###### Params

- newHandler: User handler for merge-patch requests.

##### iotagentLib.setProvisioningHandler()

###### Signature
Expand Down
16 changes: 15 additions & 1 deletion lib/commonConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ function processEnvironmentVariables() {
'IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION',
'IOTA_JSON_LD_CONTEXT',
'IOTA_FALLBACK_TENANT',
'IOTA_FALLBACK_PATH'
'IOTA_FALLBACK_PATH',
'IOTA_LD_SUPPORT_NULL',
'IOTA_LD_SUPPORT_DATASET_ID'
];
const iotamVariables = [
'IOTA_IOTAM_URL',
Expand Down Expand Up @@ -263,6 +265,18 @@ function processEnvironmentVariables() {
config.server.port = process.env.IOTA_NORTH_PORT;
}

config.server.ldSupport = config.server.ldSupport || {null: true, datasetId: true, merge: false};

if (process.env.IOTA_LD_SUPPORT_NULL) {
config.server.ldSupport.null = process.env.IOTA_LD_SUPPORT_NULL === 'true';
}
if (process.env.IOTA_LD_SUPPORT_DATASET_ID) {
config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_DATASET_ID === 'true';
}
if (process.env.IOTA_LD_SUPPORT_MERGE) {
config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_MERGE === 'true';
}

if (process.env.IOTA_PROVIDER_URL) {
config.providerUrl = process.env.IOTA_PROVIDER_URL;
}
Expand Down
1 change: 1 addition & 0 deletions lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ module.exports = {
SUBSERVICE_HEADER: 'fiware-servicepath',
NGSI_LD_TENANT_HEADER: 'NGSILD-Tenant',
NGSI_LD_PATH_HEADER: 'NGSILD-Path',
NGSI_LD_NULL: 'urn:ngsi-ld:null',
//FIXME: check Keystone support this in lowercase, then change
AUTH_HEADER: 'X-Auth-Token',
X_FORWARDED_FOR_HEADER: 'x-forwarded-for',
Expand Down
1 change: 1 addition & 0 deletions lib/fiware-iotagent-lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ exports.getConfigurationSilently = groupConfig.getSilently;
exports.findConfiguration = groupConfig.find;
exports.setDataUpdateHandler = contextServer.setUpdateHandler;
exports.setCommandHandler = contextServer.setCommandHandler;
exports.setMergePatchHandler = contextServer.setMergePatchHandler;
exports.setDataQueryHandler = contextServer.setQueryHandler;
exports.setConfigurationHandler = contextServer.setConfigurationHandler;
exports.setRemoveConfigurationHandler = contextServer.setRemoveConfigurationHandler;
Expand Down
1 change: 1 addition & 0 deletions lib/services/devices/deviceService.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ function registerDevice(deviceObj, callback) {
deviceObj.type = deviceData.type;
deviceObj.staticAttributes = deviceData.staticAttributes;
deviceObj.commands = deviceData.commands;
deviceObj.lazy = deviceData.lazy;
if ('timestamp' in deviceData && deviceData.timestamp !== undefined) {
deviceObj.timestamp = deviceData.timestamp;
}
Expand Down
4 changes: 4 additions & 0 deletions lib/services/devices/registrationUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
const properties = [];
const lazy = deviceData.lazy || [];
const commands = deviceData.commands || [];
const supportMerge = config.getConfig().server.ldSupport.merge;

lazy.forEach((element) => {
properties.push(element.name);
Expand All @@ -328,6 +329,9 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
if (commands.length > 0){
operations.push('updateOps');
}
if (supportMerge){
operations.push('mergeEntity');
}

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

0 comments on commit f7badab

Please sign in to comment.