diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index d3f5a12fa..89a933723 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1 +1 @@ - +- Add: X-Processing-Time response header with processing time (in milliseconds) expended by current HTTP measure (iotagent-node-lib#1650) diff --git a/docs/usermanual.md b/docs/usermanual.md index 36b90aa6e..083c4d2a9 100644 --- a/docs/usermanual.md +++ b/docs/usermanual.md @@ -118,19 +118,22 @@ It is possible to report as a measure a NGSI-v2 or NGSI-LD payload when related `payloadType` `ngsiv2` or `ngsild`. In these cases payload is ingested as measure where entity attributes are measure attributes. -Note that the entity ID and type in the measure are also include as attributes `measure_id` and `measure_type` as described -[here](https://github.dev/telefonicaid/iotagent-node-lib/doc/api.md#special-measures-and-attributes-names) (both using -attribute type `Text`). The ID and type of the entity updated at Context Broker is taken from device/group configuration or provision, +Note that the entity ID and type in the measure are also include as attributes `measure_id` and `measure_type` as +described [here](https://github.dev/telefonicaid/iotagent-node-lib/doc/api.md#special-measures-and-attributes-names) +(both using attribute type `Text`). The ID and type of the entity updated at Context Broker is taken from device/group +configuration or provision, -However, it is possible to use the same entity ID that the original one by using `entityNameExp` -at [device group provision](https://github.com/telefonicaid/iotagent-node-lib/blob/master/doc/api.md#config-group-datamodel), this way: +However, it is possible to use the same entity ID that the original one by using `entityNameExp` at +[device group provision](https://github.com/telefonicaid/iotagent-node-lib/blob/master/doc/api.md#config-group-datamodel), +this way: ``` "entityNameExp": "measure_id" ``` -The `actionType` used in the update sent to Context Broker is taken from the measure in the case that measure corresponds to -a NGSI-v2 batch update. In other cases (i.e. NGSI-LD or NGSI-v2 non-batch update), the `actionType` is the default one (`append`). +The `actionType` used in the update sent to Context Broker is taken from the measure in the case that measure +corresponds to a NGSI-v2 batch update. In other cases (i.e. NGSI-LD or NGSI-v2 non-batch update), the `actionType` is +the default one (`append`). For instance, given an incoming **measure** as the follwing one: @@ -138,7 +141,7 @@ For instance, given an incoming **measure** as the follwing one: { "id": "MyEntityId1", "type": "MyEntityType1", - "attr1": { "type": "Text", "value": "MyAttr1Value"} + "attr1": { "type": "Text", "value": "MyAttr1Value" } } ``` @@ -146,11 +149,11 @@ It would persist an entity into the Context Broker like the following one: ```json { - "id":"MyProvisionID", - "type":"MyProvisionType", - "attr1": { "type": "Text", "value": "MyAttr1Value"}, - "measure_id": {"type": "Text","value": "MyEntityId1"}, - "measure_type":{"type": "Text","value": "MyEntityType1"} + "id": "MyProvisionID", + "type": "MyProvisionType", + "attr1": { "type": "Text", "value": "MyAttr1Value" }, + "measure_id": { "type": "Text", "value": "MyEntityId1" }, + "measure_type": { "type": "Text", "value": "MyEntityType1" } } ``` @@ -521,6 +524,14 @@ Content-type: application/json } ``` +#### Time processing + +HTTP bindig is returning in a HTTP header named `X-Processing-Time` processing time (in milliseconds) expended by current HTTP measure +request. For example +``` +X-Processing-Time: 38 +``` + ### MQTT binding MQTT binding is based on the existence of a MQTT broker and the usage of different topics to separate the different diff --git a/lib/bindings/HTTPBinding.js b/lib/bindings/HTTPBinding.js index 770f07c9c..bfb64b4ed 100644 --- a/lib/bindings/HTTPBinding.js +++ b/lib/bindings/HTTPBinding.js @@ -454,6 +454,7 @@ function sendConfigurationToDevice(apiKey, group, deviceId, results, callback) { function handleConfigurationRequest(req, res, next) { function replyToDevice(error) { + res.set(constants.X_PROCESSING_TIME, Date.now() - req.startTime); if (error) { res.status(error.code).json(error); } else { @@ -476,6 +477,11 @@ function handleConfigurationRequest(req, res, next) { }); } +function reqTiming(req, res, next) { + req.startTime = Date.now(); + next(); +} + function handleError(error, req, res, next) { let code = 500; @@ -484,7 +490,7 @@ function handleError(error, req, res, next) { if (error.code && String(error.code).match(/^[2345]\d\d$/)) { code = error.code; } - + res.set(constants.X_PROCESSING_TIME, Date.now() - req.startTime); res.status(code).json({ name: error.name, message: error.message @@ -623,6 +629,7 @@ function returnCommands(req, res, next) { } if (req.query && req.query.getCmd === '1') { iotAgentLib.commandQueue(req.device.service, req.device.subservice, req.deviceId, function (error, list) { + res.set(constants.X_PROCESSING_TIME, Date.now() - req.startTime); if (error || !list || list.count === 0) { if (req.accepts('json')) { res.status(200).send({}); @@ -639,8 +646,10 @@ function returnCommands(req, res, next) { } }); } else if (req.accepts('json')) { + res.set(constants.X_PROCESSING_TIME, Date.now() - req.startTime); res.status(200).send({}); } else { + res.set(constants.X_PROCESSING_TIME, Date.now() - req.startTime); res.status(200).send(''); } } @@ -664,6 +673,7 @@ function start(callback) { httpBindingServer.app.set('port', config.getConfig().http.port); httpBindingServer.app.set('host', config.getConfig().http.host || '0.0.0.0'); + httpBindingServer.app.use(reqTiming); httpBindingServer.router.get( config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH, diff --git a/lib/constants.js b/lib/constants.js index f0abf0886..3698b139a 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -58,5 +58,7 @@ module.exports = { AMQP_DEFAULT_QUEUE: 'iotaqueue', AMQP_DEFAULT_DURABLE: true, AMQP_DEFAULT_RETRIES: 5, - AMQP_DEFAULT_RETRY_TIME: 5 + AMQP_DEFAULT_RETRY_TIME: 5, + + X_PROCESSING_TIME: 'X-Processing-Time' };